/*! smart-connections-obsidian v4.5.0 | (c) 2026 🌴 Brian (Brian Petro) */
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJS = (cb, mod) => function __require() {
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);

// node_modules/obsidian-smart-env/node_modules/base64-js/index.js
var require_base64_js = __commonJS({
  "node_modules/obsidian-smart-env/node_modules/base64-js/index.js"(exports2) {
    "use strict";
    exports2.byteLength = byteLength;
    exports2.toByteArray = toByteArray;
    exports2.fromByteArray = fromByteArray;
    var lookup = [];
    var revLookup = [];
    var Arr = typeof Uint8Array !== "undefined" ? Uint8Array : Array;
    var code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    for (i = 0, len = code.length; i < len; ++i) {
      lookup[i] = code[i];
      revLookup[code.charCodeAt(i)] = i;
    }
    var i;
    var len;
    revLookup["-".charCodeAt(0)] = 62;
    revLookup["_".charCodeAt(0)] = 63;
    function getLens(b64) {
      var len2 = b64.length;
      if (len2 % 4 > 0) {
        throw new Error("Invalid string. Length must be a multiple of 4");
      }
      var validLen = b64.indexOf("=");
      if (validLen === -1) validLen = len2;
      var placeHoldersLen = validLen === len2 ? 0 : 4 - validLen % 4;
      return [validLen, placeHoldersLen];
    }
    function byteLength(b64) {
      var lens = getLens(b64);
      var validLen = lens[0];
      var placeHoldersLen = lens[1];
      return (validLen + placeHoldersLen) * 3 / 4 - placeHoldersLen;
    }
    function _byteLength(b64, validLen, placeHoldersLen) {
      return (validLen + placeHoldersLen) * 3 / 4 - placeHoldersLen;
    }
    function toByteArray(b64) {
      var tmp;
      var lens = getLens(b64);
      var validLen = lens[0];
      var placeHoldersLen = lens[1];
      var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen));
      var curByte = 0;
      var len2 = placeHoldersLen > 0 ? validLen - 4 : validLen;
      var i2;
      for (i2 = 0; i2 < len2; i2 += 4) {
        tmp = revLookup[b64.charCodeAt(i2)] << 18 | revLookup[b64.charCodeAt(i2 + 1)] << 12 | revLookup[b64.charCodeAt(i2 + 2)] << 6 | revLookup[b64.charCodeAt(i2 + 3)];
        arr[curByte++] = tmp >> 16 & 255;
        arr[curByte++] = tmp >> 8 & 255;
        arr[curByte++] = tmp & 255;
      }
      if (placeHoldersLen === 2) {
        tmp = revLookup[b64.charCodeAt(i2)] << 2 | revLookup[b64.charCodeAt(i2 + 1)] >> 4;
        arr[curByte++] = tmp & 255;
      }
      if (placeHoldersLen === 1) {
        tmp = revLookup[b64.charCodeAt(i2)] << 10 | revLookup[b64.charCodeAt(i2 + 1)] << 4 | revLookup[b64.charCodeAt(i2 + 2)] >> 2;
        arr[curByte++] = tmp >> 8 & 255;
        arr[curByte++] = tmp & 255;
      }
      return arr;
    }
    function tripletToBase64(num) {
      return lookup[num >> 18 & 63] + lookup[num >> 12 & 63] + lookup[num >> 6 & 63] + lookup[num & 63];
    }
    function encodeChunk(uint8, start, end) {
      var tmp;
      var output = [];
      for (var i2 = start; i2 < end; i2 += 3) {
        tmp = (uint8[i2] << 16 & 16711680) + (uint8[i2 + 1] << 8 & 65280) + (uint8[i2 + 2] & 255);
        output.push(tripletToBase64(tmp));
      }
      return output.join("");
    }
    function fromByteArray(uint8) {
      var tmp;
      var len2 = uint8.length;
      var extraBytes = len2 % 3;
      var parts = [];
      var maxChunkLength = 16383;
      for (var i2 = 0, len22 = len2 - extraBytes; i2 < len22; i2 += maxChunkLength) {
        parts.push(encodeChunk(uint8, i2, i2 + maxChunkLength > len22 ? len22 : i2 + maxChunkLength));
      }
      if (extraBytes === 1) {
        tmp = uint8[len2 - 1];
        parts.push(
          lookup[tmp >> 2] + lookup[tmp << 4 & 63] + "=="
        );
      } else if (extraBytes === 2) {
        tmp = (uint8[len2 - 2] << 8) + uint8[len2 - 1];
        parts.push(
          lookup[tmp >> 10] + lookup[tmp >> 4 & 63] + lookup[tmp << 2 & 63] + "="
        );
      }
      return parts.join("");
    }
  }
});

// src/main.js
var main_exports = {};
__export(main_exports, {
  default: () => SmartConnectionsPlugin
});
module.exports = __toCommonJS(main_exports);
var import_obsidian72 = __toESM(require("obsidian"), 1);

// node_modules/obsidian-smart-env/smart_env.js
var import_obsidian51 = require("obsidian");

// node_modules/obsidian-smart-env/node_modules/smart-events/adapters/_adapter.js
var WILDCARD_KEY = "*";
var SmartEventsAdapter = class {
  constructor(instance) {
    this.instance = instance;
    this.handlers = /* @__PURE__ */ Object.create(null);
  }
  /**
   * Register an event handler.
   * When event_key is '*', the handler subscribes to all events.
   * Handlers receive (event, event_key).
   * @param {string} event_key
   * @param {Function} event_callback
   */
  on(event_key, event_callback) {
  }
  /**
   * Register a one-time handler.
   * When event_key is '*', the handler fires once on the next emitted event of any key.
   * Handlers receive (event, event_key).
   * @param {string} event_key
   * @param {Function} event_callback
   */
  once(event_key, event_callback) {
  }
  /**
   * Remove an event handler.
   * When event_key is '*', removes from the wildcard list only.
   * @param {string} event_key
   * @param {Function} event_callback
   */
  off(event_key, event_callback) {
  }
  /**
   * Emit an event.
   * event_key must not be '*'.
   * @param {string} event_key
   * @param {Object} event
   */
  emit(event_key, event) {
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-events/adapters/default.js
var DefaultEventsAdapter = class extends SmartEventsAdapter {
  constructor(instance) {
    super(instance);
    this._next_id = 1;
  }
  on(event_key, event_callback = () => {
  }) {
    const key = event_key === WILDCARD_KEY ? WILDCARD_KEY : event_key;
    const list = this.handlers[key] || (this.handlers[key] = []);
    const entry = { id: this._next_id++, cb: event_callback };
    list.push(entry);
    return () => this.off_entry(key, entry.id);
  }
  once(event_key, event_callback = () => {
  }) {
    const key = event_key === WILDCARD_KEY ? WILDCARD_KEY : event_key;
    const list = this.handlers[key] || (this.handlers[key] = []);
    const entry = { id: this._next_id++, cb: null };
    const wrapper = (event, emitted_key) => {
      this.off_entry(key, entry.id);
      event_callback(event, emitted_key);
    };
    entry.cb = wrapper;
    list.push(entry);
    return () => this.off_entry(key, entry.id);
  }
  /**
   * Public removal by function reference.
   * Removes a single matching registration for the given function.
   * Preference is to remove the most-recent registration (LIFO) when duplicates exist.
   */
  off(event_key, event_callback) {
    const list = this.handlers[event_key];
    if (!list || !event_callback) return;
    for (let i = list.length - 1; i >= 0; i--) {
      if (list[i].cb === event_callback) {
        list.splice(i, 1);
        break;
      }
    }
  }
  /**
   * Internal precise removal by entry id.
   * Used by unsubscribe closures returned from on/once.
   */
  off_entry(event_key, entry_id) {
    const list = this.handlers[event_key];
    if (!list) return;
    const idx = list.findIndex((e) => e.id === entry_id);
    if (idx !== -1) list.splice(idx, 1);
  }
  emit(event_key, event = {}) {
    if (event_key === WILDCARD_KEY) {
      throw new Error('emit("*") is not allowed; "*" is reserved for wildcard listeners.');
    }
    const specific_list = this.handlers[event_key];
    const wildcard_list = this.handlers[WILDCARD_KEY];
    if (!specific_list && !wildcard_list) return;
    const call_specific = specific_list ? [...specific_list] : [];
    const call_wildcard = wildcard_list ? [...wildcard_list] : [];
    for (let i = 0; i < call_specific.length; i++) {
      call_specific[i].cb(event, event_key);
    }
    for (let i = 0; i < call_wildcard.length; i++) {
      call_wildcard[i].cb(event, event_key);
    }
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-events/smart_events.js
var SmartEvents = class _SmartEvents {
  constructor(env, opts = {}) {
    env.create_env_getter(this);
    this.opts = opts;
  }
  static create(env, opts = {}) {
    const smart_events = new _SmartEvents(env, opts);
    if (!Object.getOwnPropertyDescriptor(env, "events")) {
      Object.defineProperty(env, "events", { get: () => smart_events });
    }
    return smart_events;
  }
  get adapter() {
    if (!this._adapter) {
      this._adapter = this.opts.adapter_class ? new this.opts.adapter_class(this) : new DefaultEventsAdapter(this);
    }
    return this._adapter;
  }
  on(event_key, event_callback = (event) => {
  }) {
    return this.adapter.on(event_key, event_callback);
  }
  once(event_key, event_callback = (event) => {
  }) {
    return this.adapter.once(event_key, event_callback);
  }
  off(event_key, event_callback = (event) => {
  }) {
    return this.adapter.off(event_key, event_callback);
  }
  /**
   * Emit an event.
   * @param {string} event_key
   * @param {Record<string, unknown>} [event]
   * @returns {void}
   */
  emit(event_key, event = {}) {
    const payload = { ...event };
    if (payload.at === void 0) {
      payload.at = Date.now();
    }
    Object.freeze(payload);
    return this.adapter.emit(event_key, payload);
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-settings/smart_settings.js
var SmartSettings = class {
  /**
   * Creates an instance of SmartEnvSettings.
   * @param {Object} main - The main object to contain the instance (smart_settings) and getter (settings)
   * @param {Object} [opts={}] - Configuration options.
   */
  constructor(main, opts = {}) {
    this.main = main;
    this.opts = opts;
    this._fs = null;
    this._settings = {};
    this._saved = false;
    this.save_timeout = null;
    this.save_delay_ms = typeof opts.save_delay_ms === "number" ? opts.save_delay_ms : 1e3;
  }
  static async create(main, opts = {}) {
    const smart_settings = new this(main, opts);
    await smart_settings.load();
    main.smart_settings = smart_settings;
    Object.defineProperty(main, "settings", {
      get() {
        return smart_settings.settings;
      },
      set(settings) {
        smart_settings.settings = settings;
      }
    });
    return smart_settings;
  }
  static create_sync(main, opts = {}) {
    const smart_settings = new this(main, opts);
    smart_settings.load_sync();
    main.smart_settings = smart_settings;
    Object.defineProperty(main, "settings", {
      get() {
        return smart_settings.settings;
      },
      set(settings) {
        smart_settings.settings = settings;
      }
    });
    return smart_settings;
  }
  /**
   * Gets the current settings, wrapped with an observer to handle changes.
   * @returns {Proxy} A proxy object that observes changes to the settings.
   */
  get settings() {
    return observe_object(this._settings, (change) => {
      this.emit_settings_changed(change);
      this.schedule_save();
    });
  }
  /**
   * Sets the current settings.
   * @param {Object} settings - The new settings to apply.
   */
  set settings(settings) {
    this._settings = settings;
  }
  schedule_save() {
    if (this.save_timeout) clearTimeout(this.save_timeout);
    this.save_timeout = setTimeout(() => {
      this.save(this._settings);
      this.save_timeout = null;
    }, this.save_delay_ms);
  }
  emit_settings_changed(change) {
    const events_bus = this.resolve_events_bus();
    if (!events_bus?.emit) return;
    events_bus.emit("settings:changed", build_settings_changed_event(change));
  }
  resolve_events_bus() {
    if (this.opts.events) return this.opts.events;
    if (typeof this.opts.emit === "function") {
      return { emit: this.opts.emit };
    }
    if (this.main?.events) return this.main.events;
    if (this.main?.env?.events) return this.main.env.events;
    return null;
  }
  async save(settings = this._settings) {
    if (typeof this.opts.save === "function") await this.opts.save(settings);
    else await this.main.save_settings(settings);
  }
  async load() {
    if (typeof this.opts.load === "function") this._settings = await this.opts.load();
    else this._settings = await this.main.load_settings();
  }
  load_sync() {
    if (typeof this.opts.load === "function") this._settings = this.opts.load();
    else this._settings = this.main.load_settings();
  }
};
function observe_object(obj, on_change) {
  const proxy_cache = /* @__PURE__ */ new WeakMap();
  const proxy_targets = /* @__PURE__ */ new WeakMap();
  const wrap_value = (value, path) => {
    if (!is_observable(value)) return value;
    if (proxy_targets.has(value)) return value;
    if (proxy_cache.has(value)) return proxy_cache.get(value);
    const proxy = create_proxy(value, path);
    proxy_cache.set(value, proxy);
    proxy_targets.set(proxy, value);
    return proxy;
  };
  const create_proxy = (target, path) => new Proxy(target, {
    set(target2, property, value) {
      const property_path = [...path, property];
      const previous_snapshot = snapshot_value(target2[property]);
      const next_snapshot = snapshot_value(value);
      target2[property] = wrap_value(value, property_path);
      if (has_changed(previous_snapshot, next_snapshot)) {
        on_change({
          type: "set",
          path: property_path,
          value: next_snapshot,
          previous_value: previous_snapshot
        });
      }
      return true;
    },
    get(target2, property) {
      const result = target2[property];
      return wrap_value(result, [...path, property]);
    },
    deleteProperty(target2, property) {
      if (!Object.prototype.hasOwnProperty.call(target2, property)) {
        return true;
      }
      const property_path = [...path, property];
      const previous_snapshot = snapshot_value(target2[property]);
      delete target2[property];
      on_change({
        type: "delete",
        path: property_path,
        previous_value: previous_snapshot
      });
      return true;
    }
  });
  return wrap_value(obj, []);
}
function build_settings_changed_event(change) {
  const path = Array.isArray(change.path) ? change.path : [];
  return {
    type: change.type,
    path,
    path_string: path.join("."),
    value: change.value,
    previous_value: change.previous_value
  };
}
function snapshot_value(value) {
  if (!is_observable(value)) {
    return value;
  }
  if (typeof structuredClone === "function") {
    try {
      return structuredClone(value);
    } catch (error) {
    }
  }
  try {
    return JSON.parse(JSON.stringify(value));
  } catch (error) {
    return value;
  }
}
function has_changed(previous_snapshot, next_snapshot) {
  return serialize_value(previous_snapshot) !== serialize_value(next_snapshot);
}
function serialize_value(value) {
  if (value === void 0) return "undefined";
  if (Number.isNaN(value)) return "number:NaN";
  if (value === Infinity) return "number:Infinity";
  if (value === -Infinity) return "number:-Infinity";
  if (!is_observable(value)) {
    return `${typeof value}:${String(value)}`;
  }
  try {
    return `object:${JSON.stringify(value)}`;
  } catch (error) {
    return `object:${String(value)}`;
  }
}
function is_observable(value) {
  return typeof value === "object" && value !== null;
}

// node_modules/obsidian-smart-env/node_modules/smart-utils/deep_merge.js
function deep_merge(target = {}, source = {}) {
  for (const key in source) {
    if (!Object.prototype.hasOwnProperty.call(source, key)) continue;
    if (is_plain_object(source[key]) && is_plain_object(target[key])) {
      deep_merge(target[key], source[key]);
    } else {
      target[key] = source[key];
    }
  }
  return target;
}
function is_plain_object(o) {
  return o && typeof o === "object" && !Array.isArray(o);
}

// node_modules/obsidian-smart-env/node_modules/smart-utils/camel_case_to_snake_case.js
function camel_case_to_snake_case(str = "") {
  return str.replace(/([A-Z])/g, (m) => `_${m.toLowerCase()}`).replace(/^_/, "").replace(/2$/, "");
}

// node_modules/obsidian-smart-env/node_modules/smart-environment/utils/normalize_opts.js
function normalize_opts(opts) {
  if (!opts.collections) opts.collections = {};
  if (!opts.modules) opts.modules = {};
  if (!opts.items) opts.items = {};
  Object.entries(opts.collections).forEach(([key, val]) => {
    if (typeof val === "function") {
      opts.collections[key] = { class: val };
    }
    const new_key = camel_case_to_snake_case(key);
    if (new_key !== key) {
      opts.collections[new_key] = opts.collections[key];
      delete opts.collections[key];
    }
    if (!opts.collections[new_key].collection_key) opts.collections[new_key].collection_key = new_key;
    if (val.item_type) {
      const item_config_key = val.item_type.key || camel_case_to_snake_case(val.item_type.name);
      opts.items[item_config_key] = {
        class: val.item_type,
        ...val.item_type.version ? { version: val.item_type.version } : {},
        // include version if defined on the class itself
        // if already exists
        ...opts.items[item_config_key] || {}
        // preserve existing item config (e.g. actions) if already defined
      };
    }
  });
  Object.entries(opts.modules).forEach(([key, val]) => {
    if (typeof val === "function") {
      opts.modules[key] = { class: val };
    }
    const new_key = camel_case_to_snake_case(key);
    if (new_key !== key) {
      opts.modules[new_key] = opts.modules[key];
      delete opts.modules[key];
    }
  });
  if (!opts.items) opts.items = {};
  return opts;
}

// node_modules/obsidian-smart-env/node_modules/smart-environment/utils/deep_clone_config.js
function is_plain_object2(value) {
  if (!value || typeof value !== "object") return false;
  const proto = Object.getPrototypeOf(value);
  return proto === Object.prototype || proto === null;
}
function deep_clone_config(input) {
  if (Array.isArray(input)) {
    return input.map((item) => deep_clone_config(item));
  }
  if (is_plain_object2(input)) {
    const output = {};
    for (const [k, v] of Object.entries(input)) {
      output[k] = deep_clone_config(v);
    }
    return output;
  }
  return input;
}

// node_modules/obsidian-smart-env/node_modules/smart-environment/utils/compare_versions.js
function compare_versions(new_value, cur_value) {
  const a = normalize_version_value(new_value);
  const b = normalize_version_value(cur_value);
  const len = Math.max(a.parts.length, b.parts.length);
  for (let i = 0; i < len; i++) {
    const av = a.parts[i] !== void 0 ? a.parts[i] : 0;
    const bv = b.parts[i] !== void 0 ? b.parts[i] : 0;
    if (av > bv) return 1;
    if (av < bv) return -1;
  }
  if (a.type === b.type) return 0;
  if (a.type === "semver" && b.type !== "semver") return 1;
  if (b.type === "semver" && a.type !== "semver") return -1;
  if (a.type === "number" && b.type === "none") {
    return a.parts[0] === 0 ? 0 : 1;
  }
  if (b.type === "number" && a.type === "none") {
    return b.parts[0] === 0 ? 0 : -1;
  }
  return 0;
}
function normalize_version_value(value) {
  if (value === null || value === void 0) {
    return { type: "none", parts: [0, 0, 0] };
  }
  if (typeof value === "number") {
    if (!Number.isFinite(value)) {
      return { type: "none", parts: [0, 0, 0] };
    }
    const major = Math.floor(value);
    const minor = Math.floor((value - major) * 10);
    return { type: "number", parts: [major, minor, 0] };
  }
  if (typeof value === "string") {
    const trimmed = value.trim();
    if (!trimmed) {
      return { type: "none", parts: [0, 0, 0] };
    }
    const raw_parts = trimmed.split(".");
    const parts = raw_parts.map((part) => {
      const match = part.match(/^\d+/);
      if (!match) return 0;
      const num = Number.parseInt(match[0], 10);
      return Number.isNaN(num) ? 0 : num;
    });
    while (parts.length < 3) {
      parts.push(0);
    }
    return {
      type: "semver",
      parts
    };
  }
  return { type: "none", parts: [0, 0, 0] };
}

// node_modules/obsidian-smart-env/node_modules/smart-environment/utils/is_plain_object.js
function is_plain_object3(o) {
  if (o === null) return false;
  if (typeof o !== "object") return false;
  if (Array.isArray(o)) return false;
  if (o instanceof Function) return false;
  if (o instanceof Date) return false;
  return Object.getPrototypeOf(o) === Object.prototype;
}

// node_modules/obsidian-smart-env/node_modules/smart-environment/utils/deep_merge_no_overwrite.js
function deep_merge_no_overwrite(target, source, path = []) {
  if (!is_plain_object3(target) || !is_plain_object3(source)) {
    return target;
  }
  if (path.includes(source)) {
    return target;
  }
  path.push(source);
  for (const key of Object.keys(source)) {
    if (!Object.prototype.hasOwnProperty.call(source, key)) {
      continue;
    }
    const val = source[key];
    if (Array.isArray(target[key]) && Array.isArray(val)) {
      for (const item of val) {
        if (typeof item === "function") {
          const item_name = item.name;
          const has_same_fn = target[key].some(
            (el) => typeof el === "function" && el.name === item_name
          );
          if (!has_same_fn) {
            target[key].push(item);
          }
        } else if (item === null || ["string", "number", "boolean", "undefined"].includes(typeof item)) {
          if (!target[key].includes(item)) {
            target[key].push(item);
          }
        } else {
          target[key].push(item);
        }
      }
    } else if (is_plain_object3(val)) {
      if (!is_plain_object3(target[key])) {
        target[key] = {};
      }
      deep_merge_no_overwrite(target[key], val, [...path]);
    } else if (!Object.prototype.hasOwnProperty.call(target, key)) {
      target[key] = val;
    }
  }
  return target;
}

// node_modules/obsidian-smart-env/node_modules/smart-environment/utils/merge_env_config.js
var CONFIG_RECORD_ENV_VERSION_KEY = "__smart_env_version";
function merge_env_config(target, incoming) {
  const CUR_VER = target.version;
  const NEW_VER = incoming.version;
  for (const [key, value] of Object.entries(incoming)) {
    if (key === "version") {
      if (!Object.prototype.hasOwnProperty.call(target, "version") || compare_versions(value, target.version) > 0) {
        target.version = value;
      }
      continue;
    }
    if (key === "collections" && value && typeof value === "object") {
      if (!target.collections) target.collections = {};
      for (const [col_key, col_def] of Object.entries(value)) {
        const existing_def = target.collections[col_key];
        if (!existing_def) {
          target.collections[col_key] = { ...col_def };
          set_config_record_env_version(target.collections[col_key], NEW_VER);
          continue;
        }
        const new_version_raw = get_config_record_version(col_def);
        const cur_version_raw = get_config_record_version(existing_def);
        const cmp = compare_config_record_versions(
          new_version_raw,
          cur_version_raw,
          NEW_VER,
          get_config_record_env_version(existing_def, CUR_VER)
        );
        if (cmp > 0) {
          const replaced = { ...col_def };
          deep_merge_no_overwrite(replaced, existing_def);
          set_config_record_env_version(replaced, NEW_VER);
          target.collections[col_key] = replaced;
        } else {
          deep_merge_no_overwrite(existing_def, col_def);
        }
      }
      continue;
    }
    if (["actions", "collections", "components", "modules", "items"].includes(key) && value && typeof value === "object") {
      if (!target[key]) target[key] = {};
      for (const [comp_key, comp_def] of Object.entries(value)) {
        if (!target[key][comp_key]) {
          if (typeof comp_def === "function") {
            const comp_version = get_config_record_version(comp_def);
            target[key][comp_key] = {
              class: comp_def,
              ...comp_version !== void 0 ? { version: comp_version } : {}
            };
            set_config_record_env_version(target[key][comp_key], NEW_VER);
            continue;
          }
          target[key][comp_key] = { ...comp_def };
          set_config_record_env_version(target[key][comp_key], NEW_VER);
          continue;
        }
        const target_comp = target[key][comp_key];
        const incoming_ver = get_config_record_version(comp_def);
        const target_ver = get_config_record_version(target_comp);
        const cmp = compare_config_record_versions(
          incoming_ver,
          target_ver,
          NEW_VER,
          get_config_record_env_version(target_comp, CUR_VER)
        );
        if (cmp > 0) {
          target[key][comp_key] = comp_def;
          set_config_record_version(target[key][comp_key], incoming_ver);
          set_config_record_env_version(target[key][comp_key], NEW_VER);
        } else {
          deep_merge_no_overwrite(target_comp, comp_def);
        }
      }
      continue;
    }
    if (Array.isArray(value)) {
      if (Array.isArray(target[key])) {
        if (value.length > 0 && (typeof value[0] === "string" || typeof value[0] === "number" || typeof value[0] === "boolean")) {
          target[key] = Array.from(/* @__PURE__ */ new Set([...target[key], ...value]));
        } else {
          target[key] = [...target[key], ...value];
        }
      } else {
        if (value.length > 0 && (typeof value[0] === "string" || typeof value[0] === "number" || typeof value[0] === "boolean")) {
          target[key] = Array.from(new Set(value));
        } else {
          target[key] = [...value];
        }
      }
    } else if (value && typeof value === "object") {
      if (!target[key]) target[key] = {};
      deep_merge_no_overwrite(target[key], value);
    } else {
      target[key] = value;
    }
  }
  return target;
}
function get_config_record_version(record) {
  if (!record) return void 0;
  if (record.version !== void 0) return record.version;
  return record?.class?.version;
}
function get_config_record_env_version(record, fallback_version) {
  return record?.[CONFIG_RECORD_ENV_VERSION_KEY] ?? fallback_version;
}
function compare_config_record_versions(new_record_version, cur_record_version, new_env_version, cur_env_version) {
  const record_cmp = compare_versions(new_record_version, cur_record_version);
  if (record_cmp !== 0) return record_cmp;
  return compare_versions(new_env_version, cur_env_version);
}
function set_config_record_version(record, version4) {
  if (version4 === void 0) return;
  if (!record || typeof record !== "object" && typeof record !== "function") return;
  record.version = version4;
}
function set_config_record_env_version(record, version4) {
  if (version4 === void 0) return;
  if (!record || typeof record !== "object" && typeof record !== "function") return;
  Object.defineProperty(record, CONFIG_RECORD_ENV_VERSION_KEY, {
    value: version4,
    enumerable: false,
    configurable: true,
    writable: true
  });
}

// node_modules/obsidian-smart-env/node_modules/smart-environment/package.json
var package_default = {
  name: "smart-environment",
  author: "Brian Joseph Petro (\u{1F334} Brian)",
  license: "MIT",
  version: "2.4.2",
  type: "module",
  description: "Implements Smart Environment best practices.",
  main: "index.js",
  repository: {
    type: "git",
    url: "brianpetro/jsbrains"
  },
  bugs: {
    url: "https://github.com/brianpetro/jsbrains/issues"
  },
  scripts: {
    test: "npx ava --verbose"
  },
  homepage: "https://jsbrains.org",
  dependencies: {
    "smart-blocks": "*",
    "smart-collections": "*",
    "smart-embed-model": "*",
    "smart-entities": "*",
    "smart-events": "*",
    "smart-file-system": "*",
    "smart-http-request": "*",
    "smart-model": "*",
    "smart-notices": "*",
    "smart-settings": "*",
    "smart-sources": "*",
    "smart-utils": "*",
    "smart-view": "*"
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-environment/smart_env.js
var ROOT_SCOPE = typeof globalThis !== "undefined" ? globalThis : Function("return this")();
var SmartEnv = class {
  static version = package_default.version;
  scope_name = "smart_env";
  static global_ref = ROOT_SCOPE;
  global_ref = this.constructor.global_ref;
  constructor(opts = {}) {
    this.state = "init";
    this._components = {};
    this.collections = {};
    this.load_timeout = null;
    this._load_promise = null;
    this._collections_version_signature = null;
    this._events = SmartEvents.create(this, build_events_opts(this.config?.modules?.smart_events));
    if (opts.primary_main_key) this.primary_main_key = opts.primary_main_key;
  }
  /**
   * Builds or returns the cached configuration object.
   * The cache is invalidated automatically whenever the "version signature"
   * of any collection class changes (controlled by its static `version`).
   *
   * @returns {Object} the merged, up-to-date environment config
   */
  get config() {
    const signature = this.compute_collections_version_signature();
    if (this._config && signature === this._collections_version_signature) {
      return this._config;
    }
    this._collections_version_signature = signature;
    this._config = {};
    const sorted_configs = Object.entries(this.smart_env_configs).sort(([a_key], [b_key]) => {
      if (!this.primary_main_key) return 0;
      if (a_key === this.primary_main_key) return -1;
      if (b_key === this.primary_main_key) return 1;
      return 0;
    });
    for (const [key, rec] of sorted_configs) {
      if (!rec?.main) {
        console.warn(`SmartEnv: '${key}' unloaded, skipping`);
        delete this.smart_env_configs[key];
        continue;
      }
      if (!rec?.opts) {
        console.warn(`SmartEnv: '${key}' opts missing, skipping`);
        continue;
      }
      merge_env_config(
        this._config,
        deep_clone_config(normalize_opts(rec.opts))
      );
    }
    return this._config;
  }
  /**
   * Produces a deterministic string representing the current versions of every
   * collection class across all mains. When any collection ships a higher
   * `static version`, the signature changes - automatically invalidating the
   * cached `config`.
   *
   * @returns {string} pipe-delimited version signature
   */
  compute_collections_version_signature() {
    const list = [];
    for (const rec of Object.values(this.smart_env_configs)) {
      const { opts } = rec || {};
      if (!opts) continue;
      for (const [collection_key, def] of Object.entries(opts.collections || {})) {
        const cls = def?.class;
        const v = typeof cls?.version === "number" ? cls.version : 0;
        list.push(`${collection_key}:${v}`);
      }
    }
    return list.sort().join("|");
  }
  // ========================================================================
  // -- GLOBAL HELPERS / STATIC API                                         --
  // ========================================================================
  get env_start_wait_time() {
    if (typeof this.config.env_start_wait_time === "number") return this.config.env_start_wait_time;
    return 5e3;
  }
  static get global_env() {
    return this.global_ref.smart_env;
  }
  static set global_env(env) {
    this.global_ref.smart_env = env;
  }
  static get mains() {
    return Object.keys(this.global_ref.smart_env_configs || {});
  }
  get mains() {
    return Object.keys(this.global_ref.smart_env_configs || {});
  }
  static get should_reload() {
    if (!this.global_env) return true;
    if (this.global_env.state === "loaded" || is_global_env_locked(this.global_ref)) return false;
    if (typeof this.global_env?.constructor?.version === "undefined") return true;
    if (compare_versions(this.version, this.global_env.constructor?.version) > 0) {
      console.warn(
        "SmartEnv: Reloading environment because of version mismatch",
        `${this.version} > ${this.global_env.constructor.version}`
      );
      return true;
    }
    return false;
  }
  static get smart_env_configs() {
    if (!this.global_ref.smart_env_configs) this.global_ref.smart_env_configs = {};
    return this.global_ref.smart_env_configs;
  }
  get smart_env_configs() {
    if (!this.global_ref.smart_env_configs) this.global_ref.smart_env_configs = {};
    return this.global_ref.smart_env_configs;
  }
  /**
   * Serializes all collection data in the environment into a plain object.
   * @returns {object}
   */
  to_json() {
    return Object.fromEntries(
      Object.entries(this).filter(([, val]) => typeof val?.collection_key !== "undefined").map(([key, collection]) => [key, collection_to_plain(collection)])
    );
  }
  /**
   * Waits for either a specific main to be registered in the environment,
   * or (if `opts.main` is not specified) waits for environment collections to load.
   * @param {object} opts
   * @param {object} [opts.main] - if set, the function waits until that main is found.
   * @returns {Promise<SmartEnv>} Resolves with the environment instance
   */
  static wait_for(opts = {}) {
    return new Promise((resolve) => {
      if (opts.main) {
        const interval = setInterval(() => {
          if (this.global_env && this.global_env[opts.main]) {
            clearInterval(interval);
            resolve(this.global_env);
          }
        }, 1e3);
      } else {
        const interval = setInterval(() => {
          if (this.global_env && this.global_env.state === "loaded") {
            clearInterval(interval);
            resolve(this.global_env);
          }
        }, 100);
      }
    });
  }
  /**
   * Creates or updates a SmartEnv instance.
   * - If a global environment exists and is an older version or lacks 'init_main', it is replaced.
   * @param {Object} main - The main object to be added to the SmartEnv instance.
   * @param {Object} [env_config] - Options for configuring the SmartEnv instance.
   * @returns {SmartEnv} The SmartEnv instance.
   * @throws {TypeError} If an invalid main object is provided.
   * @throws {Error} If there's an error creating or updating the SmartEnv instance.
   */
  static async create(main, env_config) {
    if (!main || typeof main !== "object") {
      throw new TypeError("SmartEnv: Invalid main object provided");
    }
    if (!env_config) throw new Error("SmartEnv.create: 'env_config' parameter is required.");
    env_config.version = this.version;
    const existing_env = this.global_env;
    this.add_main(main, env_config);
    if (existing_env && (existing_env.state === "loaded" || is_global_env_locked(this.global_ref))) {
      return existing_env;
    }
    if (this.should_reload) {
      const opts = {};
      const reload_env = this.global_env;
      if (reload_env && reload_env.state !== "loaded" && !is_global_env_locked(this.global_ref) && compare_versions(this.version, reload_env.constructor?.version || 0) > 0) {
        reload_env.state = "superceded";
        opts.primary_main_key = camel_case_to_snake_case(main.constructor.name);
      }
      if (this.global_env?.load_timeout) clearTimeout(this.global_env.load_timeout);
      this.global_env = new this(opts);
      const g = this.global_ref;
      if (!g.all_envs) g.all_envs = [];
      g.all_envs.push(this.global_env);
    }
    clearTimeout(this.global_env.load_timeout);
    this.global_env.load_timeout = setTimeout(async () => {
      await this.global_env.load();
      this.global_env.load_timeout = null;
    }, this.global_env.env_start_wait_time);
    return this.global_env;
  }
  static add_main(main, env_config = null) {
    if (this.global_env) {
      this.global_env._config = null;
      this.global_env._collections_version_signature = null;
    }
    const main_key = camel_case_to_snake_case(main.constructor.name);
    this.smart_env_configs[main_key] = { main, opts: env_config };
    this.create_env_getter(main);
  }
  /**
   * Creates a dynamic environment getter on any instance object.
   * The returned 'env' property will yield the global `smart_env`.
   * @param {Object} instance_to_receive_getter
   */
  static create_env_getter(instance_to_receive_getter) {
    Object.defineProperty(instance_to_receive_getter, "env", {
      configurable: true,
      get: () => this.global_env
    });
  }
  create_env_getter(instance_to_receive_getter) {
    this.constructor.create_env_getter(instance_to_receive_getter);
  }
  async load() {
    if (this._load_promise) return this._load_promise;
    if (this.state === "superceded") {
      throw new Error("This environment instance has been superceded by a newer version and cannot be loaded.");
    }
    this.state = "loading";
    this._load_promise = this.run_load();
    try {
      await this._load_promise;
      if (this.state === "superceded") return this;
      await this.after_load();
      if (this.state === "superceded") return this;
      this.state = "loaded";
      const _instance = this;
      Object.defineProperty(this.global_ref, "smart_env", {
        get() {
          return _instance;
        },
        set(incoming_env) {
          _instance.handle_env_load_attempt_after_loaded(incoming_env);
        },
        configurable: false
      });
      return this;
    } catch (e) {
      if (this.state === "superceded") {
        console.warn("SmartEnv load aborted because this environment was superceded by a newer version.");
        return this;
      }
      console.error("Error loading SmartEnv:", e);
      this.state = "load_error";
    } finally {
    }
  }
  /**
   * Handle load attempts after load
   */
  handle_env_load_attempt_after_loaded(incoming_env) {
    console.warn("Received attempt to load another SmartEnv after one has already loaded", incoming_env);
    console.warn(new Error("Stacktrace of SmartEnv load attempt after environment is already loaded"));
  }
  async run_load() {
    await this.fs.load_files();
    if (!this.settings) await SmartSettings.create(this);
    if (this.config.default_settings) {
      deep_merge_no_overwrite(this.settings, this.config.default_settings);
    }
    this.smart_settings.save();
    await this.init_collections();
    for (const [main_key, { main }] of Object.entries(this.smart_env_configs)) {
      this[main_key] = main;
    }
    await this.ready_to_load_collections();
    await this.load_collections();
    return this;
  }
  /**
   * Must call before calling `load()`. If it returns false, the load process is aborted.
   * @returns {Promise<void>}
   */
  async before_load() {
    return true;
  }
  /**
   * Called automatically after `load()` completes. Override to perform any actions that require a fully loaded environment (e.g. registering source watchers, workspace events, etc).
   * Completes before state is set to 'loaded', so any async operations here will delay the environment becoming available.
   *
   * @returns {Promise<void>}
   */
  async after_load() {
  }
  /**
   * Initializes collection classes if they have an 'init' function.
   * @param {Object} [config=this.config]
   */
  async init_collections(config = this.config) {
    for (const key of Object.keys(config.collections || {})) {
      const collection_config = config.collections[key] || {};
      const _class = collection_config.class;
      if (_class?.default_settings) {
        deep_merge_no_overwrite(
          this.settings,
          {
            [key]: _class.default_settings
          }
        );
      }
      if (!_class || typeof _class.init !== "function") continue;
      const existing_collection = this[key];
      if (existing_collection) {
        const should_replace_collection = existing_collection.constructor !== _class || get_version_number(_class) > get_version_number(existing_collection.constructor);
        if (should_replace_collection) {
          existing_collection.unload?.();
          this[key] = null;
          this.collections[key] = null;
        } else if (this.collections[key]) {
          continue;
        }
      }
      await _class.init(this, { ...collection_config });
      this.collections[key] = "init";
    }
  }
  /**
   * Hook/Override this method to wait for any conditions before loading collections.
   * @param {Object} main
   */
  async ready_to_load_collections() {
  }
  /**
   * Loads any available collections, processing their load queues.
   * @param {Object} [collections=this.collections] - Key-value map of collection instances.
   */
  async load_collections(collections = this.collections) {
    const collection_keys = Object.keys(collections || {}).filter((key) => collections[key] !== "loaded").sort((a, b) => {
      const order_a = this.config.collections?.[a]?.load_order || 0;
      const order_b = this.config.collections?.[b]?.load_order || 0;
      return order_a - order_b;
    });
    for (const key of collection_keys) {
      const time_start = Date.now();
      if (typeof this[key]?.process_load_queue === "function") {
        await this[key].process_load_queue();
        this[key].load_time_ms = Date.now() - time_start;
        this.collections[key] = "loaded";
        console.log(`Loaded ${this[key].collection_key} in ${this[key].load_time_ms}ms`);
      }
    }
  }
  /**
   * Removes a main from the global.smart_env_configs to exclude it on reload
   * @param {Class} main
   * @param {Object|null} [unload_config=null]
   */
  static unload_main(main) {
    const main_key = camel_case_to_snake_case(main.constructor.name);
    this.smart_env_configs[main_key] = null;
    delete this.smart_env_configs[main_key];
  }
  unload_main(main) {
    this.constructor.unload_main(main);
  }
  /**
   * Triggers a save event in all known collections.
   */
  save() {
    for (const key of Object.keys(this.collections)) {
      this[key].process_save_queue?.();
    }
  }
  /**
   * Initialize a module from the configured `this.opts.modules`.
   * @param {string} module_key
   * @param {object} opts
   * @deprecated smart_env_config.modules is deprecated (2026-04-12)
   * @returns {object|null} instance of the requested module or null if not found
   */
  init_module(module_key, opts = {}) {
    const module_config = this.opts.modules[module_key];
    if (!module_config) {
      return console.warn(`SmartEnv: module ${module_key} not found`);
    }
    opts = {
      ...{ ...module_config, class: null },
      ...opts
    };
    return new module_config.class(opts);
  }
  /**
   * @deprecated 2026-03-31
   */
  get notices() {
    if (!this._notices) {
      const SmartNoticesClass = this.config.modules.smart_notices.class;
      this._notices = new SmartNoticesClass(this, {
        adapter: this.config.modules.smart_notices.adapter
      });
    }
    return this._notices;
  }
  /**
   * Renders a named component using an optional scope and options.
   * @deprecated use env.smart_components.render instead (2025-10-11)
   * @param {string} component_key
   * @param {Object} scope
   * @param {Object} [opts]
   * @returns {Promise<HTMLElement>}
   */
  async render_component(component_key, scope, opts = {}) {
    return this.smart_components.render_component(component_key, scope, opts);
  }
  /**
   * A built-in settings schema for this environment.
   * @abstract
   * @returns {Object}
   */
  get settings_config() {
    return {};
  }
  get global_prop() {
    return this.opts.global_prop ?? "smart_env";
  }
  get fs_module_config() {
    return this.opts.modules.smart_fs;
  }
  get fs() {
    if (!this.smart_fs) {
      this.smart_fs = new this.fs_module_config.class(this, {
        adapter: this.fs_module_config.adapter,
        fs_path: this.opts.env_path || ""
      });
    }
    return this.smart_fs;
  }
  get env_data_dir() {
    const env_settings_files = this.fs.file_paths?.filter((path) => path.endsWith("smart_env.json")) || [];
    let env_data_dir = ".smart-env";
    if (env_settings_files.length > 0) {
      if (env_settings_files.length > 1) {
        const env_data_dir_counts = env_settings_files.map((path) => {
          const dir = path.split("/").slice(-2, -1)[0];
          return {
            dir,
            count: this.fs.file_paths.filter((p) => p.includes(dir)).length
          };
        });
        env_data_dir = env_data_dir_counts.reduce(
          (max, dir_obj) => dir_obj.count > max.count ? dir_obj : max,
          env_data_dir_counts[0]
        ).dir;
      } else {
        env_data_dir = env_settings_files[0].split("/").slice(-2, -1)[0];
      }
    }
    return env_data_dir;
  }
  get data_fs() {
    if (!this._fs) {
      this._fs = new this.fs_module_config.class(this, {
        adapter: this.fs_module_config.adapter,
        fs_path: this.data_fs_path
      });
    }
    return this._fs;
  }
  get data_fs_path() {
    if (!this._data_fs_path) {
      this._data_fs_path = (this.opts.env_path + (this.opts.env_path ? this.opts.env_path.includes("\\") ? "\\" : "/" : "") + this.env_data_dir).replace(/\\\\/g, "\\").replace(/\/\//g, "/");
    }
    return this._data_fs_path;
  }
  /**
   * Saves the current settings to the file system.
   * @param {Object|null} [settings=null] - Optional settings to override the current settings before saving.
   * @returns {Promise<void>}
   */
  async save_settings(settings) {
    this._saved = false;
    if (!await this.data_fs.exists("")) {
      await this.data_fs.mkdir("");
    }
    await this.data_fs.write("smart_env.json", JSON.stringify(settings, null, 2));
    this._saved = true;
  }
  /**
   * Loads settings from the file system, merging with any `default_settings`
   * @returns {Promise<Object>} the loaded settings
   */
  async load_settings() {
    if (!await this.data_fs.exists("smart_env.json")) await this.save_settings({});
    let settings = JSON.parse(JSON.stringify(this.config.default_settings || {}));
    deep_merge(settings, JSON.parse(await this.data_fs.read("smart_env.json")));
    this._saved = true;
    if (this.fs.auto_excluded_files) {
      const existing_file_exclusions = settings.smart_sources.file_exclusions.split(",").map((s) => s.trim()).filter(Boolean);
      settings.smart_sources.file_exclusions = [...existing_file_exclusions, ...this.fs.auto_excluded_files].filter((value, index, self) => self.indexOf(value) === index).join(",");
    }
    return settings;
  }
  /**
   * Refreshes file-system state if exclusions changed,
   * then re-renders relevant settings UI
   */
  async update_exclusions() {
    this.smart_sources._fs = null;
    await this.smart_sources.init_fs();
  }
  // DEPRECATED
  /**
   * Lazily instantiate the module 'smart_view'.
   * @deprecated use env.smart_components instead (2025-09-30)
   * @returns {object}
   */
  get smart_view() {
    if (!this._smart_view) {
      this._smart_view = this.init_module("smart_view");
    }
    return this._smart_view;
  }
  /** @deprecated access `this.state` and `collection.state` directly instead */
  get collections_loaded() {
    return this.state === "loaded";
  }
  /** @deprecated Use this['main_class_name'] instead of this.main/this.plugin */
  get main() {
    return this.smart_env_configs[this.mains[0]]?.main;
  }
  /**
   * @deprecated use component pattern instead
   */
  get ejs() {
    return this.opts.ejs;
  }
  /**
   * @deprecated use component pattern instead
   */
  get templates() {
    return this.opts.templates;
  }
  /**
   * @deprecated use component pattern instead
   */
  get views() {
    return this.opts.views;
  }
  /**
   * @deprecated use this.config instead
   */
  get opts() {
    return this.config;
  }
  /**
   * @deprecated Use this.main_class_name instead of this.plugin
   */
  get plugin() {
    return this.main;
  }
};
function collection_to_plain(collection) {
  return {
    items: Object.fromEntries(
      Object.entries(collection.items || {}).map(([key, item]) => [key, item.data])
    )
  };
}
function build_events_opts(module_config) {
  if (!module_config) return {};
  if (typeof module_config === "function") {
    return { adapter_class: module_config };
  }
  const adapter_class = module_config.adapter_class || module_config.adapter;
  return adapter_class ? { adapter_class } : {};
}
function get_version_number(subject) {
  return typeof subject?.version === "number" ? subject.version : 0;
}
function is_global_env_locked(global_ref) {
  return Object.getOwnPropertyDescriptor(global_ref, "smart_env")?.configurable === false;
}

// node_modules/obsidian-smart-env/node_modules/smart-file-system/utils/glob_to_regex.js
function create_regex(pattern, { case_sensitive, extended_glob, windows_paths }) {
  const regex_pattern = glob_to_regex_pattern(pattern, extended_glob);
  const adjusted_pattern = adjust_for_windows_paths(regex_pattern, windows_paths);
  const flags = case_sensitive ? "" : "i";
  return new RegExp(`^${adjusted_pattern}$`, flags);
}
function adjust_for_windows_paths(pattern, windows_paths) {
  return windows_paths ? pattern.replace(/\\\//g, "[\\\\/]").replace(/\\\\\\/g, "[\\\\/]") : pattern;
}
function glob_to_regex_pattern(pattern, extended_glob) {
  let in_class = false;
  let in_brace = 0;
  let result = "";
  for (let i = 0; i < pattern.length; i++) {
    const char = pattern[i];
    switch (char) {
      case "\\":
        if (i + 1 < pattern.length) {
          result += `\\${pattern[i + 1]}`;
          i++;
        } else {
          result += "\\\\";
        }
        break;
      case "/":
        result += "\\/";
        break;
      case "[":
        if (!in_class) {
          const closingIndex = pattern.indexOf("]", i + 1);
          if (closingIndex === -1) {
            result += "\\[";
          } else {
            in_class = true;
            if (pattern[i + 1] === "!") {
              result += "[^";
              i++;
            } else {
              result += "[";
            }
          }
        } else {
          result += "\\[";
        }
        break;
      case "]":
        if (in_class) {
          in_class = false;
          result += "]";
        } else {
          result += "\\]";
        }
        break;
      case "{":
        if (!in_class) {
          const closingIndex = pattern.indexOf("}", i + 1);
          if (closingIndex === -1) {
            result += "\\{";
          } else {
            in_brace++;
            result += "(";
          }
        } else {
          result += "\\{";
        }
        break;
      case "}":
        if (!in_class && in_brace > 0) {
          in_brace--;
          result += ")";
        } else {
          result += "\\}";
        }
        break;
      case ",":
        if (!in_class && in_brace > 0) {
          result += "|";
        } else {
          result += ",";
        }
        break;
      case "*":
        if (!in_class) {
          if (i + 1 < pattern.length && pattern[i + 1] === "*") {
            result += ".*";
            i++;
          } else {
            result += "[^/]*";
          }
        } else {
          result += "\\*";
        }
        break;
      case "?":
        if (!in_class) {
          result += "[^/]";
        } else {
          result += "\\?";
        }
        break;
      // We escape these to ensure they remain literal
      case "(":
      case ")":
      case "+":
      case "|":
      case "^":
      case "$":
      case ".":
        result += `\\${char}`;
        break;
      default:
        result += char;
        break;
    }
  }
  if (in_class) {
    result += "]";
    in_class = false;
  }
  if (extended_glob) {
    result = result.replace(/\\\+\\\((.*?)\\\)/g, "($1)+").replace(/\\\@\\\((.*?)\\\)/g, "($1)").replace(/\\\!\\\((.*?)\\\)/g, "(?!$1).*").replace(/\\\?\\\((.*?)\\\)/g, "($1)?").replace(/\\\*\\\((.*?)\\\)/g, "($1)*");
  }
  return result;
}
function glob_to_regex(pattern, options = {}) {
  const default_options = {
    case_sensitive: true,
    extended_glob: false,
    windows_paths: false
  };
  const merged_options = { ...default_options, ...options };
  if (pattern === "") {
    return /^$/;
  }
  if (pattern === "*" && !merged_options.windows_paths) {
    return /^[^/]+$/;
  }
  if (pattern === "**" && !merged_options.windows_paths) {
    return /^.+$/;
  }
  return create_regex(pattern, merged_options);
}

// node_modules/obsidian-smart-env/node_modules/smart-file-system/utils/fuzzy_search.js
function fuzzy_search(arr, search_term) {
  let matches = [];
  for (let i = 0; i < arr.length; i++) {
    const search_chars = search_term.toLowerCase().split("");
    let match = true;
    let distance = 0;
    const name = arr[i];
    const label_name = name.toLowerCase();
    for (let j = 0; j < search_chars.length; j++) {
      const search_index = label_name.substring(distance).indexOf(search_chars[j]);
      if (search_index >= 0) {
        distance += search_index + 1;
      } else {
        match = false;
        break;
      }
    }
    if (match) matches.push({ name, distance });
  }
  matches.sort((a, b) => a.distance - b.distance);
  return matches.map((match) => match.name);
}

// node_modules/obsidian-smart-env/node_modules/smart-file-system/smart_fs.js
var SmartFs = class {
  /**
   * Create a new SmartFs instance
   * 
   * @param {Object} env - The Smart Environment instance
   * @param {Object} [opts={}] - Optional configuration
   * @param {string} [opts.fs_path] - Custom environment path
   */
  constructor(env, opts = {}) {
    this.env = env;
    this.opts = opts;
    this.fs_path = opts.fs_path || opts.env_path || "";
    if (!opts.adapter) throw new Error("SmartFs requires an adapter");
    this.adapter = new opts.adapter(this);
    this.excluded_patterns = [];
    if (Array.isArray(opts.exclude_patterns)) {
      opts.exclude_patterns.forEach((pattern) => this.add_ignore_pattern(pattern));
    }
    this.folders = {};
    this.files = {};
    this.file_paths = [];
    this.folder_paths = [];
    this.auto_excluded_files = [];
  }
  async refresh() {
    this.files = {};
    this.file_paths = [];
    this.folders = {};
    this.folder_paths = [];
    await this.init();
  }
  async init() {
    await this.load_exclusions();
    await this.load_files();
  }
  async load_files() {
    const all = await this.list_recursive();
    this.file_paths = [];
    this.folder_paths = [];
    all.forEach((file) => {
      if (file.type === "file") {
        this.files[file.path] = file;
        this.file_paths.push(file.path);
      } else if (file.type === "folder") {
        this.folders[file.path] = file;
        this.folder_paths.push(file.path);
      }
    });
  }
  include_file(file_path) {
    const file = this.adapter.get_file(file_path);
    this.files[file.path] = file;
    this.file_paths.push(file.path);
    return file;
  }
  /**
   * Load .gitignore patterns
   * 
   * @returns {Promise<RegExp[]>} Array of RegExp patterns
   */
  async load_exclusions() {
    const gitignore_path = ".gitignore";
    const gitignore_exists = await this.adapter.exists(gitignore_path);
    if (gitignore_exists && !this.env.settings.skip_excluding_gitignore) {
      const gitignore_content = await this.adapter.read(gitignore_path, "utf-8");
      gitignore_content.split("\n").filter((line) => !line.startsWith("#")).filter(Boolean).forEach((pattern) => this.add_ignore_pattern(pattern));
    }
    this.add_ignore_pattern(".**");
    this.add_ignore_pattern("**/.**");
    this.add_ignore_pattern("**/.*/**");
    this.add_ignore_pattern("**/*.ajson");
  }
  /**
   * Add a new ignore pattern
   * 
   * @param {string} pattern - The pattern to add
   */
  add_ignore_pattern(pattern, opts = {}) {
    this.excluded_patterns.push(glob_to_regex(pattern.trim(), opts));
  }
  /**
   * Check if a path is ignored based on gitignore patterns
   * 
   * @param {string} _path - The path to check
   * @returns {boolean} True if the path is ignored, false otherwise
   */
  is_excluded(_path) {
    try {
      if (_path.includes("#")) return true;
      if (!this.excluded_patterns.length) return false;
      return this.excluded_patterns.some((pattern) => pattern.test(_path));
    } catch (e) {
      console.error(`Error checking if path is excluded: ${e.message}`);
      console.error(`Path: `, _path);
      throw e;
    }
  }
  /**
   * Check if any path in an array of paths is excluded
   * 
   * @param {string[]} paths - Array of paths to check
   * @returns {boolean} True if any path is excluded, false otherwise
   */
  has_excluded_patterns(paths) {
    return paths.some((p) => this.is_excluded(p));
  }
  /**
   * Pre-process an array of paths, throwing an error if any path is excluded
   * 
   * @param {string[]} paths - Array of paths to pre-process
   * @throws {Error} If any path in the array is excluded
   * @returns {string[]} The array of paths
   */
  pre_process(paths) {
    if (this.has_excluded_patterns(paths)) {
      throw new Error(`Path is excluded: ${paths.find((p) => this.is_excluded(p))}`);
    }
    return paths;
  }
  /**
   * Post-process the result of an operation
   * 
   * @param {any} returned_value - The value returned by the operation
   * @returns {any} The post-processed value
   */
  post_process(returned_value) {
    if (this.adapter.post_process) return this.adapter.post_process(returned_value);
    if (Array.isArray(returned_value)) {
      returned_value = returned_value.filter((r) => {
        if (typeof r === "string") return !this.is_excluded(r);
        if (typeof r === "object" && r.path) return !this.is_excluded(r.path);
        return true;
      });
    }
    return returned_value;
  }
  // v2
  /**
   * Use the adapter for a method
   * runs pre_process and post_process (checks exclusions)
   * @param {string} method - The method to use
   * @param {string[]} paths - The paths to use
   * @param {...any} args - Additional arguments for the method
   * @returns {Promise<any>} The result of the method
   */
  async use_adapter(method, paths, ...args) {
    if (!this.adapter[method]) throw new Error(`Method ${method} not found in adapter`);
    paths = this.pre_process(paths ?? []);
    let resp = await this.adapter[method](...paths, ...args);
    return this.post_process(resp);
  }
  use_adapter_sync(method, paths, ...args) {
    if (!this.adapter[method]) throw new Error(`Method ${method} not found in adapter`);
    paths = this.pre_process(paths ?? []);
    let resp = this.adapter[method](...paths, ...args);
    return this.post_process(resp);
  }
  /**
   * Append content to a file
   * 
   * @param {string} rel_path - The relative path of the file to append to
   * @param {string|Buffer} content - The content to append
   * @returns {Promise<void>} A promise that resolves when the operation is complete
   */
  async append(rel_path, content) {
    return await this.use_adapter("append", [rel_path], content);
  }
  /**
   * Create a new directory
   * 
   * @param {string} rel_path - The relative path of the directory to create
   * @returns {Promise<void>} A promise that resolves when the operation is complete
   */
  async mkdir(rel_path, opts = { recursive: true }) {
    return await this.use_adapter("mkdir", [rel_path], opts);
  }
  /**
   * Check if a file or directory exists
   * 
   * @param {string} rel_path - The relative path to check
   * @returns {Promise<boolean>} True if the path exists, false otherwise
   */
  async exists(rel_path) {
    return await this.use_adapter("exists", [rel_path]);
  }
  exists_sync(rel_path) {
    return this.use_adapter_sync("exists_sync", [rel_path]);
  }
  /**
   * List files in a directory
   * 
   * @param {string} rel_path - The relative path to list
   * @returns {Promise<string[]>} Array of file paths
   */
  async list(rel_path = "/") {
    return await this.use_adapter("list", [rel_path]);
  }
  async list_recursive(rel_path = "/") {
    return await this.use_adapter("list_recursive", [rel_path]);
  }
  async list_files(rel_path = "/") {
    return await this.use_adapter("list_files", [rel_path]);
  }
  async list_files_recursive(rel_path = "/") {
    return await this.use_adapter("list_files_recursive", [rel_path]);
  }
  async list_folders(rel_path = "/") {
    return await this.use_adapter("list_folders", [rel_path]);
  }
  async list_folders_recursive(rel_path = "/") {
    return await this.use_adapter("list_folders_recursive", [rel_path]);
  }
  /**
   * Read the contents of a file
   * 
   * @param {string} rel_path - The relative path of the file to read
   * @returns {Promise<string|Buffer>} The contents of the file
   */
  async read(rel_path, encoding = "utf-8") {
    try {
      const content = await this.adapter.read(rel_path, encoding);
      return content;
    } catch (error) {
      console.warn("Error during read: " + error.message, rel_path);
      if (error.code === "ENOENT") return null;
      return { error: error.message };
    }
  }
  /**
   * Remove a file
   * 
   * @param {string} rel_path - The relative path of the file to remove
   * @returns {Promise<void>} A promise that resolves when the operation is complete
   */
  async remove(rel_path) {
    return await this.use_adapter("remove", [rel_path]);
  }
  /**
   * Remove a directory
   * 
   * @param {string} rel_path - The relative path of the directory to remove
   * @returns {Promise<void>} A promise that resolves when the operation is complete
   */
  async remove_dir(rel_path, recursive = false) {
    return await this.use_adapter("remove_dir", [rel_path], recursive);
  }
  /**
   * Rename a file or directory
   * 
   * @param {string} rel_path - The current relative path
   * @param {string} new_rel_path - The new relative path
   * @returns {Promise<void>} A promise that resolves when the operation is complete
   */
  async rename(rel_path, new_rel_path) {
    await this.use_adapter("rename", [rel_path, new_rel_path]);
    await this.refresh();
  }
  /**
   * Get file or directory statistics
   * 
   * @param {string} rel_path - The relative path to get statistics for
   * @returns {Promise<Object>} An object containing file or directory statistics
   */
  async stat(rel_path) {
    return await this.use_adapter("stat", [rel_path]);
  }
  /**
   * Write content to a file
   * Should handle when target path is within a folder that doesn't exist
   * 
   * @param {string} rel_path - The relative path of the file to write to
   * @param {string|Buffer} content - The content to write
   * @returns {Promise<void>} A promise that resolves when the operation is complete
   */
  async write(rel_path, content) {
    try {
      await this.adapter.write(rel_path, content);
    } catch (error) {
      console.error("Error during write:", error);
      throw error;
    }
  }
  // // aliases
  // async create(rel_path, content) { return await this.use_adapter('write', [rel_path], content); }
  // async update(rel_path, content) { return await this.use_adapter('write', [rel_path], content); }
  get_link_target_path(link_target, source_path) {
    if (this.adapter.get_link_target_path) return this.adapter.get_link_target_path(link_target, source_path);
    if (!this.file_paths) return console.warn("get_link_target_path: file_paths not found");
    const matching_file_paths = this.file_paths.filter((path) => path.includes(link_target));
    return fuzzy_search(matching_file_paths, link_target)[0];
  }
  get sep() {
    return this.adapter.sep || "/";
  }
  get_full_path(rel_path = "") {
    return this.adapter.get_full_path(rel_path);
  }
  get base_path() {
    return this.adapter.get_base_path();
  }
};

// node_modules/obsidian-smart-env/src/adapters/smart-fs/obsidian.js
var obsidian = __toESM(require("obsidian"), 1);
var ObsidianFsAdapter = class {
  /**
   * Create an ObsidianFsAdapter instance
   * 
   * @param {Object} smart_fs - The SmartFs instance
   */
  constructor(smart_fs) {
    this.smart_fs = smart_fs;
    this.obsidian = obsidian;
    this.obsidian_app = smart_fs.env.main.app;
    this.obsidian_adapter = smart_fs.env.main.app.vault.adapter;
  }
  get fs_path() {
    return this.smart_fs.fs_path;
  }
  get_file(file_path) {
    const file = {};
    file.path = file_path.replace(/\\/g, "/").replace(this.smart_fs.fs_path, "").replace(/^\//, "");
    file.type = "file";
    file.extension = file.path.split(".").pop().toLowerCase();
    file.name = file.path.split("/").pop();
    file.basename = file.name.split(".").shift();
    Object.defineProperty(file, "stat", {
      get: () => {
        const tfile = this.obsidian_app.vault.getAbstractFileByPath(file_path);
        if (tfile) {
          return {
            ctime: tfile.stat.ctime,
            mtime: tfile.stat.mtime,
            size: tfile.stat.size,
            isDirectory: () => tfile instanceof this.obsidian.TFolder,
            isFile: () => tfile instanceof this.obsidian.TFile
          };
        }
        return null;
      }
    });
    return file;
  }
  /**
   * Append content to a file
   * 
   * @param {string} rel_path - The relative path of the file to append to
   * @param {string} data - The content to append
   * @returns {Promise<void>} A promise that resolves when the operation is complete
   */
  async append(rel_path, data) {
    if (!rel_path.startsWith(this.fs_path)) rel_path = this.fs_path + "/" + rel_path;
    return await this.obsidian_adapter.append(rel_path, data);
  }
  /**
   * Create a new directory
   * 
   * @param {string} rel_path - The relative path of the directory to create
   * @returns {Promise<void>} A promise that resolves when the operation is complete
   */
  async mkdir(rel_path) {
    if (!rel_path.startsWith(this.fs_path)) rel_path = this.fs_path + "/" + rel_path;
    return await this.obsidian_adapter.mkdir(rel_path);
  }
  /**
   * Check if a file or directory exists
   * 
   * @param {string} rel_path - The relative path to check
   * @returns {Promise<boolean>} True if the path exists, false otherwise
   */
  async exists(rel_path) {
    if (!rel_path.startsWith(this.fs_path)) rel_path = this.fs_path + "/" + rel_path;
    return await this.obsidian_adapter.exists(rel_path);
  }
  exists_sync(rel_path) {
    return !!this.obsidian_app.vault.getAbstractFileByPath(rel_path);
  }
  /**
   * List files in a directory (NOT up-to-date with list_recursive)
   * 
   * @param {string} rel_path - The relative path to list
   * @returns {Promise<string[]>} Array of file paths
   */
  async list(rel_path, opts = {}) {
    if (!rel_path.startsWith(this.fs_path)) rel_path = this.fs_path + "/" + rel_path;
    if (rel_path.startsWith("/")) rel_path = rel_path.slice(1);
    if (rel_path.endsWith("/")) rel_path = rel_path.slice(0, -1);
    if (rel_path.includes(".")) {
      const { files: file_paths } = await this.obsidian_adapter.list(rel_path);
      const files2 = file_paths.map((file_path) => {
        if (this.smart_fs.fs_path) file_path = file_path.replace(this.smart_fs.fs_path, "").slice(1);
        const file_name = file_path.split("/").pop();
        const file = {
          basename: file_name.split(".")[0],
          extension: file_name.split(".").pop().toLowerCase(),
          name: file_name,
          path: file_path
        };
        return file;
      });
      return files2;
    }
    const files = this.obsidian_app.vault.getAllLoadedFiles().filter((file) => {
      const last_slash = file.path.lastIndexOf("/");
      if (last_slash === -1 && rel_path !== "") return false;
      const folder_path = file.path.slice(0, last_slash);
      if (folder_path !== rel_path) return false;
      return true;
    });
    return files;
  }
  // NOTE: currently does not handle hidden files and folders
  async list_recursive(rel_path, opts = {}) {
    if (!rel_path.startsWith(this.fs_path)) rel_path = this.fs_path + "/" + rel_path;
    if (rel_path.startsWith("/")) rel_path = rel_path.slice(1);
    if (rel_path.endsWith("/")) rel_path = rel_path.slice(0, -1);
    const files = this.obsidian_app.vault.getAllLoadedFiles().filter((file) => {
      if (file.path.length > 200) {
        this.smart_fs.auto_excluded_files.push(file.path);
        return false;
      }
      if (rel_path !== "" && !file.path.startsWith(rel_path)) return false;
      if (file instanceof this.obsidian.TFile) {
        if (opts.type === "folder") return false;
        file.type = "file";
      } else if (file instanceof this.obsidian.TFolder) {
        if (opts.type === "file") return false;
        delete file.basename;
        delete file.extension;
        file.type = "folder";
      }
      if (this.smart_fs.fs_path) file.path = file.path.replace(this.smart_fs.fs_path, "").slice(1);
      return true;
    });
    return files;
  }
  async list_files(rel_path) {
    return await this.list(rel_path, { type: "file" });
  }
  async list_files_recursive(rel_path) {
    return await this.list_recursive(rel_path, { type: "file" });
  }
  async list_folders(rel_path) {
    return await this.list(rel_path, { type: "folder" });
  }
  async list_folders_recursive(rel_path) {
    return await this.list_recursive(rel_path, { type: "folder" });
  }
  /**
   * Read the contents of a file
   * 
   * @param {string} rel_path - The relative path of the file to read
   * @returns {Promise<string>} The contents of the file
   */
  async read(rel_path, encoding, opts = {}) {
    if (!rel_path.startsWith(this.fs_path)) rel_path = this.fs_path + "/" + rel_path;
    if (encoding === "utf-8") {
      if (!opts.no_cache) {
        const tfile = this.obsidian_app.vault.getFileByPath(rel_path);
        if (tfile) return await this.obsidian_app.vault.cachedRead(tfile);
      }
      return await this.obsidian_adapter.read(rel_path);
    }
    if (encoding === "base64") {
      const array_buffer2 = await this.obsidian_adapter.readBinary(rel_path, "base64");
      const base642 = this.obsidian.arrayBufferToBase64(array_buffer2);
      return base642;
    }
    const array_buffer = await this.obsidian_adapter.readBinary(rel_path);
    return array_buffer;
  }
  /**
   * Rename a file or directory
   * 
   * @param {string} old_path - The current path of the file or directory
   * @param {string} new_path - The new path for the file or directory
   * @returns {Promise<void>} A promise that resolves when the operation is complete
   */
  async rename(old_path, new_path) {
    if (!old_path.startsWith(this.fs_path)) old_path = this.fs_path + "/" + old_path;
    if (!new_path.startsWith(this.fs_path)) new_path = this.fs_path + "/" + new_path;
    return await this.obsidian_adapter.rename(old_path, new_path);
  }
  /**
   * Remove a file
   * 
   * @param {string} rel_path - The relative path of the file to remove
   * @returns {Promise<void>} A promise that resolves when the operation is complete
   */
  async remove(rel_path) {
    if (!rel_path.startsWith(this.fs_path)) rel_path = this.fs_path + "/" + rel_path;
    try {
      return await this.obsidian_adapter.remove(rel_path);
    } catch (error) {
      console.warn(`Error removing file: ${rel_path}`, error);
    }
  }
  /**
   * Remove a directory
   * 
   * @param {string} rel_path - The relative path of the directory to remove
   * @returns {Promise<void>} A promise that resolves when the operation is complete
   */
  async remove_dir(rel_path, recursive = false) {
    if (!rel_path.startsWith(this.fs_path)) rel_path = this.fs_path + "/" + rel_path;
    return await this.obsidian_adapter.rmdir(rel_path, recursive);
  }
  /**
   * Get file or directory information
   * 
   * @param {string} rel_path - The relative path of the file or directory
   * @returns {Promise<Object>} An object containing file or directory information
   */
  async stat(rel_path) {
    if (!rel_path.startsWith(this.fs_path)) rel_path = this.fs_path + "/" + rel_path;
    return await this.obsidian_adapter.stat(rel_path);
  }
  /**
   * Write content to a file
   * 
   * @param {string} rel_path - The relative path of the file to write to
   * @param {string} data - The content to write
   * @returns {Promise<void>} A promise that resolves when the operation is complete
   */
  async write(rel_path, data) {
    if (!data) data = "";
    if (!rel_path.startsWith(this.fs_path)) rel_path = this.fs_path + "/" + rel_path;
    const folder_path = rel_path.split("/").slice(0, -1).join("/");
    if (!await this.exists(folder_path)) {
      await this.mkdir(folder_path);
      console.log(`Created folder: ${folder_path}`);
    }
    return await this.obsidian_adapter.write(rel_path, data);
  }
  get_link_target_path(link_path, file_path) {
    return this.obsidian_app.metadataCache.getFirstLinkpathDest(link_path, file_path)?.path;
  }
  get_base_path() {
    return this.obsidian_adapter.basePath;
  }
  get_full_path(rel_path = "") {
    const sep = rel_path.includes("/") ? "/" : "\\";
    return this.get_base_path() + sep + rel_path;
  }
  /**
   * Registers Obsidian vault/workspace listeners that emit Smart Environment events for Smart Sources.
   * @param {import('smart-sources').SmartSources} sources_collection
   * @returns {boolean}
   */
  register_source_watchers(sources_collection) {
    if (this._source_watchers_registered) return this._source_watchers_registered;
    const plugin = this.smart_fs.env?.main;
    if (!plugin?.registerEvent) {
      console.warn("ObsidianFsAdapter: Unable to register source watchers without plugin context");
      return false;
    }
    const { app: app2 } = plugin;
    const emit_event = (event_key, payload) => {
      if (!payload?.path && !payload?.item_key) return;
      this.smart_fs.env.events?.emit(event_key, {
        collection_key: sources_collection.collection_key,
        item_key: payload.item_key || payload.path,
        ...payload
      });
    };
    plugin.registerEvent(
      app2.vault.on("create", (file) => {
        emit_event("sources:created", {
          path: file.path,
          event_source: "obsidian:vault.create"
        });
      })
    );
    plugin.registerEvent(
      app2.vault.on("modify", (file) => {
        emit_event("sources:modified", {
          path: file.path,
          event_source: "obsidian:vault.modify"
        });
      })
    );
    plugin.registerEvent(
      app2.vault.on("rename", (file, old_path) => {
        emit_event("sources:renamed", {
          path: file.path,
          old_path,
          event_source: "obsidian:vault.rename"
        });
      })
    );
    plugin.registerEvent(
      app2.vault.on("delete", (file) => {
        emit_event("sources:deleted", {
          path: file.path,
          event_source: "obsidian:vault.delete"
        });
      })
    );
    plugin.registerEvent(
      app2.workspace.on("editor-change", (_editor, info) => {
        const file = info?.file;
        if (!file) return;
        emit_event("sources:modified", {
          path: file.path,
          event_source: "obsidian:workspace.editor-change"
        });
      })
    );
    this._source_watchers_registered = true;
    return true;
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-view/utils/empty.js
function empty(elm) {
  const range = document.createRange();
  range.selectNodeContents(elm);
  range.deleteContents();
}

// node_modules/obsidian-smart-env/node_modules/smart-view/utils/replace_html.js
var replace_html = /* @__PURE__ */ (() => {
  const cache = /* @__PURE__ */ new Map();
  return (container, html_snippet) => {
    const key = html_snippet.trim();
    let tpl = cache.get(key);
    if (!tpl) {
      tpl = document.createElement("template");
      tpl.innerHTML = key;
      cache.set(key, tpl);
    }
    container.replaceChildren(tpl.content.cloneNode(true));
  };
})();

// node_modules/obsidian-smart-env/node_modules/smart-view/utils/replace_with_fragment.js
var replace_with_fragment = (container, html_snippet) => {
  const range = document.createRange();
  const frag = range.createContextualFragment(html_snippet.trim());
  container.replaceChildren(frag);
};

// node_modules/obsidian-smart-env/node_modules/smart-view/utils/safe_inner_html.js
var restricted_re = /<(td|th|tr|thead|tbody|tfoot|caption|col|colgroup|option|optgroup|li|dt|dd|source|track)\b/i;
var safe_inner_html = (container, html_snippet) => {
  const trimmed = html_snippet.trim();
  (restricted_re.test(trimmed) ? replace_with_fragment : replace_html)(container, trimmed);
};

// node_modules/obsidian-smart-env/node_modules/smart-utils/escape_html.js
function escape_html(value = "") {
  return String(value).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
}

// node_modules/obsidian-smart-env/node_modules/smart-utils/create_hash.js
function murmur_hash_32(input_string, seed = 0) {
  let remainder = input_string.length & 3;
  let bytes = input_string.length - remainder;
  let h1 = seed;
  let c1 = 3432918353;
  let c2 = 461845907;
  let i = 0;
  let k1 = 0;
  let chunk = 0;
  while (i < bytes) {
    chunk = input_string.charCodeAt(i) & 255 | (input_string.charCodeAt(i + 1) & 255) << 8 | (input_string.charCodeAt(i + 2) & 255) << 16 | (input_string.charCodeAt(i + 3) & 255) << 24;
    i += 4;
    k1 = chunk;
    k1 = multiply_32(k1, c1);
    k1 = rotate_left_32(k1, 15);
    k1 = multiply_32(k1, c2);
    h1 ^= k1;
    h1 = rotate_left_32(h1, 13);
    h1 = h1 * 5 + 3864292196 | 0;
  }
  k1 = 0;
  switch (remainder) {
    case 3:
      k1 ^= (input_string.charCodeAt(i + 2) & 255) << 16;
    // falls through
    case 2:
      k1 ^= (input_string.charCodeAt(i + 1) & 255) << 8;
    // falls through
    case 1:
      k1 ^= input_string.charCodeAt(i) & 255;
      k1 = multiply_32(k1, c1);
      k1 = rotate_left_32(k1, 15);
      k1 = multiply_32(k1, c2);
      h1 ^= k1;
      break;
  }
  h1 ^= input_string.length;
  h1 = fmix_32(h1);
  return h1 | 0;
}
function murmur_hash_32_alphanumeric(input_string, seed = 0) {
  const signed_hash = murmur_hash_32(input_string, seed);
  const unsigned_hash = signed_hash >>> 0;
  return unsigned_hash.toString(36);
}
function multiply_32(a, b) {
  return (a & 65535) * b + ((a >>> 16) * b << 16) | 0;
}
function rotate_left_32(value, shift) {
  return value << shift | value >>> 32 - shift;
}
function fmix_32(h) {
  h ^= h >>> 16;
  h = multiply_32(h, 2246822507);
  h ^= h >>> 13;
  h = multiply_32(h, 3266489909);
  h ^= h >>> 16;
  return h | 0;
}

// node_modules/obsidian-smart-env/node_modules/smart-utils/convert_to_time_ago.js
function convert_to_time_ago(timestamp) {
  const now = Date.now();
  const ms = timestamp < 1e12 ? timestamp * 1e3 : timestamp;
  const diff_ms = now - ms;
  const is_future = diff_ms < 0;
  const seconds = Math.floor(Math.abs(diff_ms) / 1e3);
  const intervals = [
    { label: "year", seconds: 31536e3 },
    { label: "month", seconds: 2592e3 },
    { label: "day", seconds: 86400 },
    { label: "hour", seconds: 3600 },
    { label: "minute", seconds: 60 },
    { label: "second", seconds: 1 }
  ];
  for (const interval of intervals) {
    const count = Math.floor(seconds / interval.seconds);
    if (count >= 1) {
      const suffix = `${count} ${interval.label}${count > 1 ? "s" : ""}`;
      return is_future ? `in ${suffix}` : `${suffix} ago`;
    }
  }
  return "just now";
}

// node_modules/obsidian-smart-env/node_modules/smart-utils/cos_sim.js
function cos_sim(vector1 = [], vector2 = []) {
  if (vector1.length !== vector2.length) {
    throw new Error("Vectors must have the same length");
  }
  let dot_product = 0;
  let magnitude1 = 0;
  let magnitude2 = 0;
  const epsilon = 1e-8;
  for (let i = 0; i < vector1.length; i++) {
    dot_product += vector1[i] * vector2[i];
    magnitude1 += vector1[i] * vector1[i];
    magnitude2 += vector2[i] * vector2[i];
  }
  magnitude1 = Math.sqrt(magnitude1);
  magnitude2 = Math.sqrt(magnitude2);
  if (magnitude1 < epsilon || magnitude2 < epsilon) return 0;
  return dot_product / (magnitude1 * magnitude2);
}

// node_modules/obsidian-smart-env/node_modules/smart-utils/get_by_path.js
function get_by_path(obj, path, scope = null) {
  if (!path) return "";
  const keys = path.split(".");
  if (scope) {
    keys.unshift(scope);
  }
  const final_key = keys.pop();
  const instance = keys.reduce((acc, key) => acc && acc[key], obj);
  if (instance && typeof instance[final_key] === "function") {
    return instance[final_key].bind(instance);
  }
  return instance ? instance[final_key] : void 0;
}

// node_modules/obsidian-smart-env/node_modules/smart-utils/set_by_path.js
function set_by_path(obj, path, value, scope = null) {
  const keys = path.split(".");
  if (scope) {
    keys.unshift(scope);
  }
  const final_key = keys.pop();
  const target = keys.reduce((acc, key) => {
    if (!acc[key] || typeof acc[key] !== "object") {
      acc[key] = {};
    }
    return acc[key];
  }, obj);
  target[final_key] = value;
}

// node_modules/obsidian-smart-env/node_modules/smart-utils/delete_by_path.js
function delete_by_path(obj, path, scope = null) {
  const keys = path.split(".");
  if (scope) {
    keys.unshift(scope);
  }
  const final_key = keys.pop();
  const instance = keys.reduce((acc, key) => acc && acc[key], obj);
  if (instance) {
    delete instance[final_key];
  }
}

// node_modules/obsidian-smart-env/node_modules/smart-utils/geom.js
function compute_centroid(points) {
  if (!points || points.length === 0) {
    return null;
  }
  const n = points.length;
  const dim = points[0].length;
  const sums = new Float64Array(dim);
  for (let i = 0; i < n; i++) {
    const p = points[i];
    for (let d = 0; d < dim; d++) {
      sums[d] += p[d];
    }
  }
  for (let d = 0; d < dim; d++) {
    sums[d] /= n;
  }
  return Array.from(sums);
}
function compute_medoid(points) {
  if (!points || points.length === 0) {
    return null;
  }
  if (points.length === 1) {
    return points[0];
  }
  const n = points.length;
  const dim = points[0].length;
  const sum_of_distances = new Float64Array(n);
  for (let i = 0; i < n - 1; i++) {
    const p_i = points[i];
    for (let j = i + 1; j < n; j++) {
      const p_j = points[j];
      let dist_sq = 0;
      for (let d = 0; d < dim; d++) {
        const diff = p_i[d] - p_j[d];
        dist_sq += diff * diff;
      }
      const dist = Math.sqrt(dist_sq);
      sum_of_distances[i] += dist;
      sum_of_distances[j] += dist;
    }
  }
  let min_index = 0;
  let min_sum = sum_of_distances[0];
  for (let i = 1; i < n; i++) {
    if (sum_of_distances[i] < min_sum) {
      min_sum = sum_of_distances[i];
      min_index = i;
    }
  }
  return points[min_index];
}

// node_modules/obsidian-smart-env/node_modules/smart-view/smart_view.js
var get_style_sheet_id = (css_text) => {
  if (typeof css_text !== "string") return null;
  return `style-sheet-${murmur_hash_32_alphanumeric(css_text)}`;
};
var element_disposers = /* @__PURE__ */ new WeakMap();
var smart_setting_listeners = /* @__PURE__ */ new WeakMap();
var SmartView = class {
  static version = 0.1;
  /**
   * @constructor
   * @param {object} opts - Additional options or overrides for rendering.
   */
  constructor(opts = {}) {
    this.opts = opts;
    this._adapter = null;
  }
  /**
   * Renders all setting components within a container.
   * @async
   * @param {HTMLElement|DocumentFragment} container - The container element.
   * @param {Object} opts - Additional options for rendering.
   * @returns {Promise<void>}
   */
  async render_setting_components(container, opts = {}) {
    const components = container.querySelectorAll(".setting-component");
    const promises = [];
    for (const component of components) {
      promises.push(this.render_setting_component(component, opts));
    }
    await Promise.all(promises);
    return container;
  }
  /**
   * Creates a document fragment from HTML string.
   * @param {string} html - The HTML string.
   * @returns {DocumentFragment}
   */
  create_doc_fragment(html) {
    return document.createRange().createContextualFragment(html);
  }
  /**
   * Gets the adapter instance used for rendering (e.g., Obsidian or Node, etc.).
   * @returns {Object} The adapter instance.
   */
  get adapter() {
    if (!this._adapter) {
      if (!this.opts.adapter) {
        throw new Error("No adapter provided to SmartView. Provide a 'smart_view.adapter' in env config.");
      }
      const AdapterClass = this.opts.adapter;
      this._adapter = new AdapterClass(this);
    }
    return this._adapter;
  }
  /**
   * Gets an icon (implemented in the adapter).
   * @param {string} icon_name - Name of the icon to get.
   * @returns {string} The icon HTML string.
   */
  get_icon_html(icon_name) {
    return this.adapter.get_icon_html(icon_name);
  }
  /**
   * Renders a single setting component (implemented in adapter).
   * @async
   * @param {HTMLElement} setting_elm - The DOM element for the setting.
   * @param {Object} opts - Additional options for rendering.
   * @returns {Promise<*>}
   */
  async render_setting_component(setting_elm, opts = {}) {
    return await this.adapter.render_setting_component(setting_elm, opts);
  }
  /**
   * Renders markdown content (implemented in adapter).
   * @param {string} markdown - The markdown content.
   * @param {object|null} scope - The scope to pass for rendering.
   * @returns {Promise<DocumentFragment>}
   */
  async render_markdown(markdown, scope = null) {
    return await this.adapter.render_markdown(markdown, scope);
  }
  /**
   * Gets a value from an object by path.
   * @param {Object} obj - The object to search in.
   * @param {string} path - The path to the value.
   * @returns {*}
   */
  get_by_path(obj, path, settings_scope = null) {
    return get_by_path(obj, path, settings_scope);
  }
  /**
   * Sets a value in an object by path.
   * @param {Object} obj - The object to modify.
   * @param {string} path - The path to set the value.
   * @param {*} value - The value to set.
   */
  set_by_path(obj, path, value, settings_scope = null) {
    set_by_path(obj, path, value, settings_scope);
  }
  /**
   * Deletes a value from an object by path.
   * @param {Object} obj - The object to modify.
   * @param {string} path - The path to delete the value.
   */
  delete_by_path(obj, path, settings_scope = null) {
    delete_by_path(obj, path, settings_scope);
  }
  /**
   * Escapes HTML special characters in a string.
   * @param {string} str - The string to escape.
   * @returns {string} The escaped string.
   */
  escape_html(str) {
    return escape_html(str);
  }
  /**
   * A convenience method to build a setting HTML snippet from a config object.
   * @param {Object} setting_config
   * @returns {string}
   */
  render_setting_html(setting_config) {
    if (setting_config.type === "html") {
      return setting_config.value;
    }
    const attributes = Object.entries(setting_config).map(([attr, value]) => {
      if (attr.includes("class")) return "";
      if (typeof value === "number") return `data-${attr.replace(/_/g, "-")}=${value}`;
      return `data-${attr.replace(/_/g, "-")}="${value}"`;
    }).join("\n");
    return `<div class="setting-component${setting_config.scope_class ? " " + setting_config.scope_class : ""}"
data-setting="${setting_config.setting}"
${attributes}
></div>`;
  }
  /**
   * Renders settings from a config, returning a fragment.
   * @async
   * @deprecated Use render_settings_config utility in Obsidian (obsidian-smart-env)
   * @param {Object} settings_config
   * @param {Object} opts
   * @param {Object} [opts.scope={}] - The scope to use when rendering settings (should have settings property).
   * @returns {Promise<DocumentFragment>}
   */
  async render_settings(settings_config13, opts = {}) {
    const is_fx = typeof settings_config13 === "function";
    const html = Object.entries(is_fx ? await settings_config13(opts.scope) : settings_config13).map(([setting_key, setting_config]) => {
      if (!setting_config.setting) {
        setting_config.setting = setting_key;
      }
      return this.render_setting_html(setting_config);
    }).join("\n");
    const frag = this.create_doc_fragment(`<div>${html}</div>`);
    return await this.render_setting_components(frag, opts);
  }
  /**
   * Scans the given container for elements that have `data-smart-setting` and attaches
   * a 'change' event listener that updates the corresponding path in `scope.settings`.
   *
   * Listener bookkeeping is done via WeakMap, not DOM attributes, to avoid
   * clone/attribute-related bugs on re-render.
   * 
   * @param {Object} scope - An object containing a `settings` property, where new values will be stored.
   * @param {HTMLElement|Document} [container=document] - The DOM element to scan. Defaults to the entire document.
   */
  add_settings_listeners(scope, container = document) {
    if (!container || typeof container.querySelectorAll !== "function") return;
    const elements = container.querySelectorAll("[data-smart-setting]");
    elements.forEach((elm) => {
      const path = elm.dataset.smartSetting;
      if (!path) return;
      if (smart_setting_listeners.has(elm)) {
        return;
      }
      const handler = () => {
        let new_value;
        if (elm instanceof HTMLInputElement) {
          if (elm.type === "checkbox") {
            new_value = elm.checked;
          } else if (elm.type === "radio") {
            if (elm.checked) {
              new_value = elm.value;
            } else {
              return;
            }
          } else {
            new_value = elm.value;
          }
        } else if (elm instanceof HTMLSelectElement || elm instanceof HTMLTextAreaElement) {
          new_value = elm.value;
        } else {
          new_value = elm.value ?? elm.textContent;
        }
        this.set_by_path(scope.settings, path, new_value);
      };
      smart_setting_listeners.set(elm, handler);
      elm.addEventListener("change", handler);
      if (elm instanceof HTMLElement) {
        this.attach_disposer(elm, () => {
          const existing = smart_setting_listeners.get(elm);
          if (existing) {
            elm.removeEventListener("change", existing);
            smart_setting_listeners.delete(elm);
          }
        });
      }
    });
  }
  apply_style_sheet(sheet) {
    if (typeof sheet === "string") {
      const style_sheet_id = get_style_sheet_id(sheet);
      if (!style_sheet_id) return;
      if (document.getElementById(style_sheet_id)) {
        return;
      }
      const styleEl = document.createElement("style");
      styleEl.id = style_sheet_id;
      styleEl.textContent = sheet;
      document.head.appendChild(styleEl);
      return;
    }
    if ("adoptedStyleSheets" in Document.prototype) {
      document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
    } else {
      const styleEl = document.createElement("style");
      if (sheet.cssRules) {
        styleEl.textContent = Array.from(sheet.cssRules).map((rule) => rule.cssText).join("\n");
      }
      document.head.appendChild(styleEl);
    }
  }
  empty(elm) {
    empty(elm);
  }
  safe_inner_html(elm, html) {
    safe_inner_html(elm, html);
  }
  /**
   * Attaches one or more disposer functions to an element that will be called
   * when that element has been observed in the DOM and is later removed.
   *
   * - Multiple calls for the same element accumulate disposer functions.
   * - Disposers are only invoked once, on the first removal after the
   *   element has been in the DOM.
   * - No DOM attributes or properties are used for bookkeeping; everything
   *   is tracked via WeakMap.
   *
   * @param {HTMLElement} el - The element to monitor.
   * @param {Function|Function[]} dispose - The disposer function or array of functions to call on removal.
   */
  attach_disposer(el, dispose) {
    if (!el) return;
    const doc = el.ownerDocument;
    const win = doc && doc.defaultView;
    const MutationObserverCtor = win && win.MutationObserver;
    if (!doc || !win || !MutationObserverCtor || !doc.body) return;
    let dispose_fns;
    if (typeof dispose === "function") {
      dispose_fns = [dispose];
    } else if (Array.isArray(dispose)) {
      dispose_fns = dispose.filter((fn) => typeof fn === "function");
    } else {
      console.warn("[smart-view] attach_disposer called with invalid disposer");
      return;
    }
    if (!dispose_fns.length) {
      console.warn("[smart-view] attach_disposer called with no valid disposer functions");
      return;
    }
    let entry = element_disposers.get(el);
    if (!entry) {
      entry = {
        dispose_fns: /* @__PURE__ */ new Set(),
        observer: null,
        has_been_in_dom: false,
        disposed: false
      };
      element_disposers.set(el, entry);
    }
    if (entry.disposed) {
      entry.disposed = false;
      entry.has_been_in_dom = false;
    }
    for (const fn of dispose_fns) {
      entry.dispose_fns.add(fn);
    }
    if (!entry.observer) {
      const observer = new MutationObserverCtor(() => {
        const in_dom = doc.body.contains(el);
        if (in_dom) {
          entry.has_been_in_dom = true;
          return;
        }
        if (!entry.has_been_in_dom || entry.disposed) {
          return;
        }
        entry.disposed = true;
        try {
          for (const fn of entry.dispose_fns) {
            try {
              fn();
            } catch (err) {
              console.error("[smart-view] disposer error", err);
            }
          }
        } finally {
          try {
            observer.disconnect();
          } catch (e) {
          }
          if (element_disposers.get(el) === entry) {
            element_disposers.delete(el);
          }
        }
      });
      entry.observer = observer;
      observer.observe(doc.body, { childList: true, subtree: true });
    }
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-view/adapters/_adapter.js
var SmartViewAdapter = class {
  constructor(main) {
    this.main = main;
  }
  // NECESSARY OVERRIDES
  /**
   * Retrieves the class used for settings.
   * Must be overridden by subclasses to return the appropriate setting class.
   * @abstract
   * @returns {Function} The setting class constructor.
   * @throws Will throw an error if not implemented in the subclass.
   */
  get setting_class() {
    throw new Error("setting_class() not implemented");
  }
  /**
   * Generates the HTML for a specified icon.
   * Must be overridden by subclasses to provide the correct icon HTML.
   * @abstract
   * @param {string} icon_name - The name of the icon to generate HTML for.
   * @returns {string} The HTML string representing the icon.
   * @throws Will throw an error if not implemented in the subclass.
   */
  get_icon_html(icon_name) {
    throw new Error("get_icon_html() not implemented");
  }
  /**
   * Renders Markdown content within a specific scope.
   * Must be overridden by subclasses to handle Markdown rendering appropriately.
   * @abstract
   * @param {string} markdown - The Markdown content to render.
   * @param {object|null} [scope=null] - The scope within which to render the Markdown.
   * @returns {Promise<void>} A promise that resolves when rendering is complete.
   * @throws Will throw an error if not implemented in the subclass.
   */
  async render_markdown(markdown, scope = null) {
    throw new Error("render_markdown() not implemented");
  }
  /**
   * Opens a specified URL.
   * Should be overridden by subclasses to define how URLs are opened.
   * @abstract
   * @param {string} url - The URL to open.
   */
  open_url(url) {
    throw new Error("open_url() not implemented");
  }
  /**
   * Handles the selection of a folder by invoking the folder selection dialog and updating the setting.
   * @abstract
   * @param {string} setting - The path of the setting being modified.
   * @param {string} value - The current value of the setting.
   * @param {HTMLElement} elm - The HTML element associated with the setting.
   * @param {object} scope - The current scope containing settings and actions.
   */
  handle_folder_select(path, value, elm, scope) {
    throw new Error("handle_folder_select not implemented");
  }
  /**
   * Handles the selection of a file by invoking the file selection dialog and updating the setting.
   * @abstract
   * @param {string} setting - The path of the setting being modified.
   * @param {string} value - The current value of the setting.
   * @param {HTMLElement} elm - The HTML element associated with the setting.
   * @param {object} scope - The current scope containing settings and actions.
   */
  handle_file_select(path, value, elm, scope) {
    throw new Error("handle_file_select not implemented");
  }
  /**
   * Performs actions before a setting is changed, such as clearing notices and updating the UI.
   * @abstract
   * @param {string} setting - The path of the setting being changed.
   * @param {*} value - The new value for the setting.
   * @param {HTMLElement} elm - The HTML element associated with the setting.
   * @param {object} scope - The current scope containing settings and actions.
   */
  pre_change(path, value, elm) {
  }
  /**
   * Performs actions after a setting is changed, such as updating UI elements.
   * @abstract
   * @param {string} setting - The path of the setting that was changed.
   * @param {*} value - The new value for the setting.
   * @param {HTMLElement} elm - The HTML element associated with the setting.
   * @param {object} changed - Additional information about the change.
   */
  post_change(path, value, elm) {
  }
  /**
   * Reverts a setting to its previous value in case of validation failure or error.
   * @abstract
   * @param {string} setting - The path of the setting to revert.
   * @param {HTMLElement} elm - The HTML element associated with the setting.
   * @param {object} scope - The current scope containing settings.
   */
  revert_setting(path, elm, scope) {
    console.warn("revert_setting() not implemented");
  }
  // DEFAULT IMPLEMENTATIONS (may be overridden)
  get setting_renderers() {
    return {
      text: this.render_text_component,
      string: this.render_text_component,
      password: this.render_password_component,
      number: this.render_number_component,
      dropdown: this.render_dropdown_component,
      toggle: this.render_toggle_component,
      textarea: this.render_textarea_component,
      textarea_array: this.render_textarea_array_component,
      button: this.render_button_component,
      remove: this.render_remove_component,
      folder: this.render_folder_select_component,
      "text-file": this.render_file_select_component,
      file: this.render_file_select_component,
      slider: this.render_slider_component,
      html: this.render_html_component,
      button_with_confirm: this.render_button_with_confirm_component,
      json: this.render_json_component,
      array: this.render_array_component
    };
  }
  async render_setting_component(elm, opts = {}) {
    this.empty(elm);
    const path = elm.dataset.setting;
    const scope = opts.scope || this.main.main;
    const settings_scope = opts.settings_scope || null;
    try {
      let value = elm.dataset.value ?? this.main.get_by_path(scope.settings, path, settings_scope);
      if (typeof value === "undefined" && typeof elm.dataset.default !== "undefined") {
        value = elm.dataset.default;
        if (typeof value === "string") value = value.toLowerCase() === "true" ? true : value === "false" ? false : value;
        this.main.set_by_path(scope.settings, path, value, settings_scope);
      }
      const renderer = this.setting_renderers[elm.dataset.type];
      if (!renderer) {
        console.warn(`Unsupported setting type: ${elm.dataset.type}`);
        return elm;
      }
      const setting = renderer.call(this, elm, path, value, scope, settings_scope);
      if (elm.dataset.name) setting.setName(elm.dataset.name);
      if (elm.dataset.description) {
        const frag = this.main.create_doc_fragment(`<span>${elm.dataset.description}</span>`);
        setting.setDesc(frag);
      }
      if (elm.dataset.tooltip) setting.setTooltip(elm.dataset.tooltip);
      this.add_button_if_needed(setting, elm, path, scope);
      this.handle_disabled_and_hidden(elm);
      return elm;
    } catch (e) {
      console.error(JSON.stringify({ path, elm }, null, 2));
      console.error(JSON.stringify(e, null, 2));
    }
  }
  render_dropdown_component(elm, path, value, scope, settings_scope) {
    const smart_setting = new this.setting_class(elm);
    let options;
    smart_setting.addDropdown((dropdown) => {
      if (elm.dataset.required) dropdown.selectEl.setAttribute("required", true);
      const opts_callback = elm.dataset.optionsCallback ? this.main.get_by_path(scope, elm.dataset.optionsCallback) : null;
      if (typeof opts_callback === "function") {
        console.log(`getting options callback: ${elm.dataset.optionsCallback}`);
        Promise.resolve(opts_callback()).then((opts) => {
          opts.forEach((option) => {
            const opt = dropdown.addOption(option.value, option.label ?? option.name ?? option.value);
            opt.selected = option.value === value;
            if (opts.length === 1 && opt.selected) dropdown.selectEl.classList.add("dropdown-no-options");
          });
          dropdown.setValue(value);
        });
      } else {
        if (!options || !options.length) {
          options = this.get_dropdown_options(elm);
        }
        options.forEach((option) => {
          const opt = dropdown.addOption(option.value, option.label ?? option.name ?? option.value);
          opt.selected = option.value === value;
          if (options.length === 1 && opt.selected) dropdown.selectEl.classList.add("dropdown-no-options");
        });
        dropdown.setValue(value);
      }
      dropdown.onChange((value2) => {
        this.handle_on_change(path, value2, elm, scope, settings_scope);
      });
    });
    return smart_setting;
  }
  render_text_component(elm, path, value, scope, settings_scope) {
    const smart_setting = new this.setting_class(elm);
    smart_setting.addText((text) => {
      text.setPlaceholder(elm.dataset.placeholder || "");
      if (value) text.setValue(value);
      let debounceTimer;
      if (elm.dataset.button) {
        smart_setting.addButton((button) => {
          button.setButtonText(elm.dataset.button);
          button.onClick(async () => this.handle_on_change(path, text.getValue(), elm, scope));
        });
      } else {
        text.onChange(async (value2) => {
          clearTimeout(debounceTimer);
          debounceTimer = setTimeout(() => this.handle_on_change(path, value2.trim(), elm, scope, settings_scope), 2e3);
        });
      }
    });
    return smart_setting;
  }
  render_password_component(elm, path, value, scope, settings_scope) {
    const smart_setting = new this.setting_class(elm);
    smart_setting.addText((text) => {
      text.inputEl.type = "password";
      text.setPlaceholder(elm.dataset.placeholder || "");
      if (value) text.setValue(value);
      let debounceTimer;
      text.onChange(async (value2) => {
        clearTimeout(debounceTimer);
        debounceTimer = setTimeout(() => this.handle_on_change(path, value2, elm, scope, settings_scope), 2e3);
      });
    });
    return smart_setting;
  }
  render_number_component(elm, path, value, scope, settings_scope) {
    const smart_setting = new this.setting_class(elm);
    smart_setting.addText((number) => {
      number.inputEl.type = "number";
      number.setPlaceholder(elm.dataset.placeholder || "");
      if (typeof value !== "undefined") number.inputEl.value = parseInt(value);
      number.inputEl.min = elm.dataset.min || 0;
      if (elm.dataset.max) number.inputEl.max = elm.dataset.max;
      let debounceTimer;
      number.onChange(async (value2) => {
        clearTimeout(debounceTimer);
        debounceTimer = setTimeout(() => this.handle_on_change(path, parseInt(value2), elm, scope, settings_scope), 2e3);
      });
    });
    return smart_setting;
  }
  render_toggle_component(elm, path, value, scope, settings_scope) {
    const smart_setting = new this.setting_class(elm);
    smart_setting.addToggle((toggle) => {
      let checkbox_val = value ?? false;
      if (typeof checkbox_val === "string") {
        checkbox_val = checkbox_val.toLowerCase() === "true";
      }
      toggle.setValue(checkbox_val);
      toggle.onChange(async (value2) => this.handle_on_change(path, value2, elm, scope, settings_scope));
    });
    return smart_setting;
  }
  render_textarea_component(elm, path, value, scope, settings_scope) {
    const smart_setting = new this.setting_class(elm);
    smart_setting.addTextArea((textarea) => {
      textarea.setPlaceholder(elm.dataset.placeholder || "");
      textarea.setValue(value || "");
      let debounceTimer;
      textarea.onChange(async (value2) => {
        clearTimeout(debounceTimer);
        debounceTimer = setTimeout(() => this.handle_on_change(path, value2, elm, scope, settings_scope), 2e3);
      });
    });
    return smart_setting;
  }
  render_textarea_array_component(elm, path, value, scope, settings_scope) {
    const smart_setting = new this.setting_class(elm);
    smart_setting.addTextArea((textarea) => {
      textarea.setPlaceholder(elm.dataset.placeholder || "");
      textarea.setValue(Array.isArray(value) ? value.join("\n") : value || "");
      let debounceTimer;
      textarea.onChange(async (value2) => {
        value2 = value2.split("\n").map((v) => v.trim()).filter((v) => v);
        clearTimeout(debounceTimer);
        debounceTimer = setTimeout(() => this.handle_on_change(path, value2, elm, scope, settings_scope), 2e3);
      });
    });
    return smart_setting;
  }
  render_button_component(elm, path, value, scope, settings_scope) {
    const smart_setting = new this.setting_class(elm);
    smart_setting.addButton((button) => {
      button.setButtonText(elm.dataset.btnText || elm.dataset.name);
      button.onClick(async () => {
        if (elm.dataset.confirm && !confirm(elm.dataset.confirm)) return;
        if (elm.dataset.href) this.open_url(elm.dataset.href);
        if (elm.dataset.callback) {
          const callback = this.main.get_by_path(scope, elm.dataset.callback);
          if (callback) callback(path, value, elm, scope, settings_scope);
        }
      });
    });
    return smart_setting;
  }
  render_remove_component(elm, path, value, scope, settings_scope) {
    const smart_setting = new this.setting_class(elm);
    smart_setting.addButton((button) => {
      button.setButtonText(elm.dataset.btnText || elm.dataset.name || "Remove");
      button.onClick(async () => {
        this.main.delete_by_path(scope.settings, path, settings_scope);
        if (elm.dataset.callback) {
          const callback = this.main.get_by_path(scope, elm.dataset.callback);
          if (callback) callback(path, value, elm, scope, settings_scope);
        }
      });
    });
    return smart_setting;
  }
  render_folder_select_component(elm, path, value, scope, settings_scope) {
    const smart_setting = new this.setting_class(elm);
    smart_setting.addFolderSelect((folder_select) => {
      folder_select.setPlaceholder(elm.dataset.placeholder || "");
      if (value) folder_select.setValue(value);
      folder_select.inputEl.closest("div").addEventListener("click", () => {
        this.handle_folder_select(path, value, elm, scope);
      });
      folder_select.inputEl.querySelector("input").addEventListener("change", (e) => {
        const folder = e.target.value;
        this.handle_on_change(path, folder, elm, scope, settings_scope);
      });
    });
    return smart_setting;
  }
  render_file_select_component(elm, path, value, scope, settings_scope) {
    const smart_setting = new this.setting_class(elm);
    smart_setting.addFileSelect((file_select) => {
      file_select.setPlaceholder(elm.dataset.placeholder || "");
      if (value) file_select.setValue(value);
      file_select.inputEl.closest("div").addEventListener("click", () => {
        this.handle_file_select(path, value, elm, scope, settings_scope);
      });
    });
    return smart_setting;
  }
  render_slider_component(elm, path, value, scope, settings_scope) {
    const smart_setting = new this.setting_class(elm);
    smart_setting.addSlider((slider) => {
      const min = parseFloat(elm.dataset.min) || 0;
      const max = parseFloat(elm.dataset.max) || 100;
      const step = parseFloat(elm.dataset.step) || 1;
      const currentValue = typeof value !== "undefined" ? parseFloat(value) : min;
      slider.setLimits(min, max, step);
      slider.setValue(currentValue);
      slider.onChange((newVal) => {
        const numericVal = parseFloat(newVal);
        this.handle_on_change(path, numericVal, elm, scope, settings_scope);
      });
    });
    return smart_setting;
  }
  render_html_component(elm, path, value, scope) {
    this.safe_inner_html(elm, value);
    return elm;
  }
  /**
   * Renders an array setting component for managing a list of strings.
   * @param {HTMLElement} elm - Container element for the setting.
   * @param {string} path - Dot-notation path to store the array.
   * @param {Array<string>} value - Initial array value.
   * @param {object} scope - Scope containing settings and actions.
   * @param {object|null} settings_scope - Optional nested settings scope.
   * @returns {object} smart_setting instance.
   */
  render_array_component(elm, path, value, scope, settings_scope) {
    const smart_setting = new this.setting_class(elm);
    let arr = Array.isArray(value) ? [...value] : [];
    const items_container = document.createElement("div");
    items_container.className = "array-items-container";
    const render_items = () => {
      items_container.innerHTML = "";
      arr.forEach((val, idx) => {
        const row = document.createElement("div");
        row.className = "array-item-row";
        const input = document.createElement("input");
        input.type = "text";
        input.value = val;
        input.placeholder = "Value";
        const remove_btn = document.createElement("button");
        remove_btn.textContent = "\u2715";
        remove_btn.title = "Remove";
        input.addEventListener("change", () => {
          arr[idx] = input.value;
          trigger_change();
        });
        remove_btn.addEventListener("click", () => {
          arr.splice(idx, 1);
          render_items();
          trigger_change();
        });
        row.appendChild(input);
        row.appendChild(remove_btn);
        items_container.appendChild(row);
      });
    };
    const add_row = document.createElement("div");
    add_row.className = "array-add-row";
    const new_input = document.createElement("input");
    new_input.type = "text";
    new_input.placeholder = "Value";
    const add_btn = document.createElement("button");
    add_btn.textContent = "+";
    add_btn.title = "Add value";
    add_btn.addEventListener("click", () => {
      const v = new_input.value.trim();
      if (!v) return;
      arr.push(v);
      new_input.value = "";
      render_items();
      trigger_change();
    });
    add_row.appendChild(new_input);
    add_row.appendChild(add_btn);
    smart_setting.controlEl.appendChild(items_container);
    smart_setting.controlEl.appendChild(add_row);
    const trigger_change = () => {
      this.handle_on_change(path, [...arr], elm, scope, settings_scope);
    };
    render_items();
    elm.appendChild(smart_setting.settingEl);
    return smart_setting;
  }
  render_json_component(elm, path, value, scope, settings_scope) {
    try {
      const smart_setting = new this.setting_class(elm);
      let obj = typeof value === "object" && value !== null ? { ...value } : {};
      const pairs_container = document.createElement("div");
      pairs_container.className = "json-pairs-container";
      const renderPairs = () => {
        pairs_container.innerHTML = "";
        Object.entries(obj).forEach(([key, val], idx) => {
          const pair_div = document.createElement("div");
          pair_div.className = "json-pair-row";
          const key_i = document.createElement("input");
          key_i.type = "text";
          key_i.value = key;
          key_i.placeholder = "Property";
          const value_i = document.createElement("input");
          value_i.type = "text";
          value_i.value = val;
          value_i.placeholder = "Value";
          const remove_btn = document.createElement("button");
          remove_btn.textContent = "\u2715";
          remove_btn.title = "Remove";
          key_i.addEventListener("change", () => {
            const newKey = key_i.value.trim();
            if (!newKey) return;
            if (newKey !== key) {
              obj[newKey] = obj[key];
              delete obj[key];
              renderPairs();
              triggerChange();
            }
          });
          value_i.addEventListener("change", () => {
            obj[key_i.value] = value_i.value;
            triggerChange();
          });
          remove_btn.addEventListener("click", () => {
            delete obj[key_i.value];
            renderPairs();
            triggerChange();
          });
          pair_div.appendChild(key_i);
          pair_div.appendChild(value_i);
          pair_div.appendChild(remove_btn);
          pairs_container.appendChild(pair_div);
        });
      };
      const add_div = document.createElement("div");
      add_div.className = "json-add-row";
      const new_key_i = document.createElement("input");
      new_key_i.type = "text";
      new_key_i.placeholder = "Property";
      const new_val_i = document.createElement("input");
      new_val_i.type = "text";
      new_val_i.placeholder = "Value";
      const add_btn = document.createElement("button");
      add_btn.textContent = "+";
      add_btn.title = "Add property";
      add_btn.addEventListener("click", () => {
        const k = new_key_i.value.trim();
        if (!k || k in obj) return;
        obj[k] = new_val_i.value;
        new_key_i.value = "";
        new_val_i.value = "";
        renderPairs();
        triggerChange();
      });
      add_div.appendChild(new_key_i);
      add_div.appendChild(new_val_i);
      add_div.appendChild(add_btn);
      smart_setting.controlEl.appendChild(pairs_container);
      smart_setting.controlEl.appendChild(add_div);
      const triggerChange = () => {
        this.handle_on_change(path, { ...obj }, elm, scope, settings_scope);
      };
      renderPairs();
      elm.appendChild(smart_setting.settingEl);
      return smart_setting;
    } catch (e) {
      console.error(e);
    }
  }
  add_button_if_needed(smart_setting, elm, path, scope) {
    if (elm.dataset.btn) {
      smart_setting.addButton((button) => {
        button.setButtonText(elm.dataset.btn);
        if (elm.dataset.btnCallback || elm.dataset.btnHref || elm.dataset.callback || elm.dataset.href) {
          button.inputEl.addEventListener("click", (e) => {
            if (elm.dataset.btnCallback && typeof scope[elm.dataset.btnCallback] === "function") {
              if (elm.dataset.btnCallbackArg) scope[elm.dataset.btnCallback](elm.dataset.btnCallbackArg);
              else scope[elm.dataset.btnCallback](path, null, smart_setting, scope);
            } else if (elm.dataset.btnHref) {
              this.open_url(elm.dataset.btnHref);
            } else if (elm.dataset.callback && typeof this.main.get_by_path(scope, elm.dataset.callback) === "function") {
              this.main.get_by_path(scope, elm.dataset.callback)(path, null, smart_setting, scope);
            } else if (elm.dataset.href) {
              this.open_url(elm.dataset.href);
            } else {
              console.error("No callback or href found for button.");
            }
          });
        }
        if (elm.dataset.btnDisabled || elm.dataset.disabled && elm.dataset.btnDisabled !== "false") {
          button.inputEl.disabled = true;
        }
      });
    }
  }
  handle_disabled_and_hidden(elm) {
    if (elm.dataset.disabled && elm.dataset.disabled !== "false") {
      elm.classList.add("disabled");
      elm.querySelector("input, select, textarea, button").disabled = true;
    }
    if (elm.dataset.hidden && elm.dataset.hidden !== "false") {
      elm.style.display = "none";
    }
  }
  get_dropdown_options(elm) {
    return Object.entries(elm.dataset).reduce((acc, [k, v]) => {
      if (!k.startsWith("option")) return acc;
      const [value, name] = v.split("|");
      acc.push({ value, name: name || value });
      return acc;
    }, []);
  }
  handle_on_change(path, value, elm, scope, settings_scope) {
    this.pre_change(path, value, elm, scope);
    if (elm.dataset.validate) {
      const valid = this[elm.dataset.validate](path, value, elm, scope);
      if (!valid) {
        elm.querySelector(".setting-item").style.border = "2px solid red";
        this.revert_setting(path, elm, scope);
        return;
      }
    }
    this.main.set_by_path(scope.settings, path, value, settings_scope);
    if (elm.dataset.callback) {
      const callback = this.main.get_by_path(scope, elm.dataset.callback);
      if (callback) callback(path, value, elm, scope);
    }
    this.post_change(path, value, elm, scope);
  }
  render_button_with_confirm_component(elm, path, value, scope) {
    const smart_setting = new this.setting_class(elm);
    smart_setting.addButton((button) => {
      button.setButtonText(elm.dataset.btnText || elm.dataset.name);
      elm.appendChild(this.main.create_doc_fragment(`
        <div class="sc-inline-confirm-row" style="
          display: none;
        ">
          <span style="margin-right: 10px;">
            ${elm.dataset.confirm || "Are you sure?"}
          </span>
          <span class="sc-inline-confirm-row-buttons">
            <button class="sc-inline-confirm-yes">Yes</button>
            <button class="sc-inline-confirm-cancel">Cancel</button>
          </span>
        </div>
      `));
      const confirm_row = elm.querySelector(".sc-inline-confirm-row");
      const confirm_yes = confirm_row.querySelector(".sc-inline-confirm-yes");
      const confirm_cancel = confirm_row.querySelector(".sc-inline-confirm-cancel");
      button.onClick(async () => {
        confirm_row.style.display = "block";
        elm.querySelector(".setting-item").style.display = "none";
      });
      confirm_yes.addEventListener("click", async () => {
        if (elm.dataset.href) this.open_url(elm.dataset.href);
        if (elm.dataset.callback) {
          const callback = this.main.get_by_path(scope, elm.dataset.callback);
          if (callback) callback(path, value, elm, scope);
        }
        elm.querySelector(".setting-item").style.display = "block";
        confirm_row.style.display = "none";
      });
      confirm_cancel.addEventListener("click", () => {
        confirm_row.style.display = "none";
        elm.querySelector(".setting-item").style.display = "block";
      });
    });
    return smart_setting;
  }
  empty(elm) {
    empty(elm);
  }
  safe_inner_html(elm, html) {
    safe_inner_html(elm, html);
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-view/adapters/obsidian.js
var import_obsidian = require("obsidian");
var SmartViewObsidianAdapter = class extends SmartViewAdapter {
  get setting_class() {
    return import_obsidian.Setting;
  }
  open_url(url) {
    window.open(url);
  }
  async render_file_select_component(elm, path, value) {
    return super.render_text_component(elm, path, value);
  }
  async render_markdown(markdown, scope) {
    const component = scope.env.smart_connections_plugin?.connections_view || new import_obsidian.Component();
    if (!scope) return console.warn("Scope required for rendering markdown in Obsidian adapter");
    const frag = this.main.create_doc_fragment("<div><div class='inner'></div></div>");
    const container = frag.querySelector(".inner");
    try {
      await import_obsidian.MarkdownRenderer.render(
        scope.env.plugin.app,
        markdown,
        container,
        scope?.file_path || "",
        component
      );
    } catch (e) {
      console.warn("Error rendering markdown in Obsidian adapter", e);
    }
    return frag;
  }
  get_icon_html(name) {
    return (0, import_obsidian.getIcon)(name).outerHTML;
  }
  render_folder_select_component(elm, path, value, scope, settings_scope) {
    const smart_setting = new this.setting_class(elm);
    const folders = scope.env.plugin.app.vault.getAllFolders().sort((a, b) => a.path.localeCompare(b.path));
    smart_setting.addDropdown((dropdown) => {
      if (elm.dataset.required) dropdown.inputEl.setAttribute("required", true);
      dropdown.addOption("", "No folder selected");
      folders.forEach((folder) => {
        dropdown.addOption(folder.path, folder.path);
      });
      dropdown.onChange((value2) => {
        this.handle_on_change(path, value2, elm, scope, settings_scope);
      });
      dropdown.setValue(value);
    });
    return smart_setting;
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-collections/utils/collection_instance_name_from.js
function collection_instance_name_from(class_name) {
  if (class_name.endsWith("Item")) {
    return class_name.replace(/Item$/, "").replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase();
  }
  return class_name.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase().replace(/y$/, "ie") + "s";
}

// node_modules/obsidian-smart-env/node_modules/smart-collections/utils/helpers.js
function create_uid(data) {
  const str = JSON.stringify(data);
  let hash = 0;
  if (str.length === 0) return hash;
  for (let i = 0; i < str.length; i++) {
    const char = str.charCodeAt(i);
    hash = (hash << 5) - hash + char;
    hash = hash & hash;
    if (hash < 0) hash = hash * -1;
  }
  return hash.toString() + str.length;
}

// node_modules/obsidian-smart-env/node_modules/smart-collections/utils/deep_equal.js
function deep_equal(obj1, obj2, visited = /* @__PURE__ */ new WeakMap()) {
  if (obj1 === obj2) return true;
  if (obj1 === null || obj2 === null || obj1 === void 0 || obj2 === void 0) return false;
  if (typeof obj1 !== typeof obj2 || Array.isArray(obj1) !== Array.isArray(obj2)) return false;
  if (Array.isArray(obj1)) {
    if (obj1.length !== obj2.length) return false;
    return obj1.every((item, index) => deep_equal(item, obj2[index], visited));
  }
  if (typeof obj1 === "object") {
    if (visited.has(obj1)) return visited.get(obj1) === obj2;
    visited.set(obj1, obj2);
    const keys1 = Object.keys(obj1);
    const keys2 = Object.keys(obj2);
    if (keys1.length !== keys2.length) return false;
    return keys1.every((key) => deep_equal(obj1[key], obj2[key], visited));
  }
  return obj1 === obj2;
}

// node_modules/obsidian-smart-env/node_modules/smart-collections/utils/get_item_display_name.js
function get_item_display_name(key, show_full_path) {
  if (show_full_path) {
    return key.split("/").join(" > ").replace(".md", "");
  }
  return key.split("/").pop().replace(".md", "");
}

// node_modules/obsidian-smart-env/node_modules/smart-collections/utils/create_actions_proxy.js
function create_actions_proxy(ctx, actions_source) {
  const input = actions_source || {};
  const is_plain_object7 = (val) => typeof val === "object" && val !== null && !Array.isArray(val);
  const is_function = (val) => typeof val === "function";
  const is_class_export = (val) => is_function(val) && /^class\s/.test(Function.prototype.toString.call(val));
  const is_action_object = (val) => is_plain_object7(val) && is_function(val.action);
  const is_action_candidate = (val) => is_function(val) || is_action_object(val) || is_class_export(val);
  const ignored_meta_keys = /* @__PURE__ */ new Set(["length", "name", "prototype"]);
  const clone_with_descriptors = (obj) => {
    if (!is_plain_object7(obj)) return obj;
    const out = Object.create(Object.getPrototypeOf(obj) || null);
    for (const key of Reflect.ownKeys(obj)) {
      const descriptor = Object.getOwnPropertyDescriptor(obj, key);
      if (!descriptor) continue;
      const next = { ...descriptor };
      if ("value" in next && is_plain_object7(next.value)) {
        next.value = clone_with_descriptors(next.value);
      }
      try {
        Object.defineProperty(out, key, next);
      } catch {
        out[key] = next.value;
      }
    }
    return out;
  };
  const should_bucket_actions = (val) => {
    if (!is_plain_object7(val)) return false;
    if (is_action_object(val)) return false;
    const keys = Reflect.ownKeys(val);
    if (keys.length === 0) return false;
    let found_candidate = false;
    for (const key of keys) {
      const descriptor = Object.getOwnPropertyDescriptor(val, key);
      if (!descriptor) continue;
      if ("value" in descriptor) {
        const entry = descriptor.value;
        if (is_action_candidate(entry)) {
          found_candidate = true;
          continue;
        }
        if (is_plain_object7(entry)) {
          if (should_bucket_actions(entry)) {
            found_candidate = true;
            continue;
          }
          return false;
        }
        if (typeof entry === "undefined") continue;
        return false;
      }
      return false;
    }
    return found_candidate;
  };
  const clone_descriptor = (descriptor) => {
    if (!descriptor) return descriptor;
    if (!("value" in descriptor)) return { ...descriptor };
    const cloned = is_plain_object7(descriptor.value) ? clone_with_descriptors(descriptor.value) : descriptor.value;
    return { ...descriptor, value: cloned };
  };
  const build_sources = (src) => {
    const global_source2 = /* @__PURE__ */ Object.create(null);
    const scoped_sources2 = /* @__PURE__ */ new Map();
    for (const key of Reflect.ownKeys(src)) {
      const descriptor = Object.getOwnPropertyDescriptor(src, key);
      if (!descriptor) continue;
      if ("value" in descriptor && should_bucket_actions(descriptor.value)) {
        scoped_sources2.set(key, clone_with_descriptors(descriptor.value));
        continue;
      }
      try {
        Object.defineProperty(global_source2, key, clone_descriptor(descriptor));
      } catch {
        global_source2[key] = descriptor.value;
      }
    }
    return { global_source: global_source2, scoped_sources: scoped_sources2 };
  };
  const { global_source, scoped_sources } = build_sources(input);
  const has_own = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
  const cache = /* @__PURE__ */ Object.create(null);
  const copy_metadata = (source, target, omit = []) => {
    if (!source || !target) return;
    const skips = /* @__PURE__ */ new Set([...ignored_meta_keys, ...omit]);
    for (const key of Reflect.ownKeys(source)) {
      if (skips.has(key)) continue;
      const descriptor = Object.getOwnPropertyDescriptor(source, key);
      if (!descriptor) continue;
      try {
        Object.defineProperty(target, key, descriptor);
      } catch {
        target[key] = descriptor.value;
      }
    }
  };
  const instantiate_class = (Ctor) => {
    const instance = new Ctor(ctx);
    const candidate = instance.action || instance.run || instance.execute || instance.call;
    if (is_function(candidate)) {
      const bound = candidate.bind(instance);
      copy_metadata(Ctor, bound);
      copy_metadata(instance, bound);
      bound.instance = instance;
      return bound;
    }
    copy_metadata(Ctor, instance);
    return instance;
  };
  const bind_or_clone = (val) => {
    if (is_class_export(val)) {
      return instantiate_class(val);
    }
    if (is_action_object(val)) {
      const bound = val.action.bind(ctx);
      copy_metadata(val, bound, ["action"]);
      return bound;
    }
    if (is_function(val)) {
      const bound = val.bind(ctx);
      copy_metadata(val, bound);
      return bound;
    }
    if (is_plain_object7(val)) {
      return clone_with_descriptors(val);
    }
    return val;
  };
  const scope_actions_for = () => {
    const scope_key = ctx?.constructor?.key;
    if (typeof scope_key === "undefined" || scope_key === null) return null;
    const bucket = scoped_sources.get(scope_key);
    return bucket && is_plain_object7(bucket) ? bucket : null;
  };
  const cache_result = (target, prop, value) => {
    target[prop] = value;
    return value;
  };
  const compute_and_cache = (target, prop) => {
    const scoped = scope_actions_for();
    if (scoped && has_own(scoped, prop)) {
      return cache_result(target, prop, bind_or_clone(scoped[prop]));
    }
    if (has_own(global_source, prop)) {
      return cache_result(target, prop, bind_or_clone(global_source[prop]));
    }
    return cache_result(target, prop, void 0);
  };
  const union_keys = () => {
    const scoped = scope_actions_for();
    const keys = new Set(Reflect.ownKeys(cache));
    for (const key of Reflect.ownKeys(global_source)) {
      keys.add(key);
    }
    if (scoped) {
      for (const key of Reflect.ownKeys(scoped)) {
        keys.add(key);
      }
    }
    return Array.from(keys);
  };
  const descriptor_for = (target, prop) => ({
    configurable: true,
    enumerable: true,
    value: target[prop]
  });
  return new Proxy(cache, {
    get: (target, prop) => {
      if (prop === Symbol.toStringTag) return "ActionsProxy";
      if (prop in target) return target[prop];
      return compute_and_cache(target, prop);
    },
    has: (target, prop) => {
      if (prop in target) return true;
      const scoped = scope_actions_for();
      if (scoped && has_own(scoped, prop)) return true;
      return has_own(global_source, prop);
    },
    ownKeys: () => union_keys(),
    getOwnPropertyDescriptor: (target, prop) => {
      if (has_own(target, prop)) {
        return descriptor_for(target, prop);
      }
      const scoped = scope_actions_for();
      if (scoped && has_own(scoped, prop)) {
        if (!has_own(target, prop)) {
          compute_and_cache(target, prop);
        }
        return descriptor_for(target, prop);
      }
      if (has_own(global_source, prop)) {
        if (!has_own(target, prop)) {
          compute_and_cache(target, prop);
        }
        return descriptor_for(target, prop);
      }
      return void 0;
    },
    defineProperty: (target, prop, descriptor) => {
      if ("value" in descriptor) {
        target[prop] = descriptor.value;
        return true;
      }
      return false;
    },
    set: (target, prop, value) => {
      target[prop] = value;
      return true;
    },
    deleteProperty: (target, prop) => {
      if (has_own(target, prop)) {
        delete target[prop];
      }
      return true;
    }
  });
}

// node_modules/obsidian-smart-env/node_modules/smart-collections/item.js
var CollectionItem = class _CollectionItem {
  static version = 2e-3;
  /**
   * Default properties for an instance of CollectionItem.
   * Override in subclasses to define different defaults.
   * @returns {Object}
   */
  static get defaults() {
    return {
      data: {}
    };
  }
  /**
   * @param {Object} env - The environment/context.
   * @param {Object|null} [data=null] - Initial data for the item.
   */
  constructor(env, data = null) {
    env.create_env_getter(this);
    this.config = this.env?.config;
    this.merge_defaults();
    if (data) deep_merge(this.data, data);
    if (!this.data.class_name) this.data.class_name = this.collection.item_class_name;
  }
  /**
   * Loads an item from data and initializes it.
   * @param {Object} env
   * @param {Object} data
   * @returns {CollectionItem}
   */
  static load(env, data) {
    const item = new this(env, data);
    item.init();
    return item;
  }
  /**
   * Merge default properties from the entire inheritance chain.
   * @private
   */
  merge_defaults() {
    let current_class = this.constructor;
    while (current_class) {
      for (let key in current_class.defaults) {
        const default_val = current_class.defaults[key];
        if (typeof default_val === "object") {
          this[key] = { ...default_val, ...this[key] };
        } else {
          this[key] = this[key] === void 0 ? default_val : this[key];
        }
      }
      current_class = Object.getPrototypeOf(current_class);
    }
  }
  /**
   * Generates or retrieves a unique key for the item.
   * Key syntax supports:
   * - `[i]` for sequences
   * - `/` for super-sources (groups, directories, clusters)
   * - `#` for sub-sources (blocks)
   * @returns {string} The unique key
   */
  get_key() {
    return create_uid(this.data);
  }
  /**
   * Updates the item data and returns true if changed.
   * @param {Object} data
   * @returns {boolean} True if data changed.
   */
  update_data(data) {
    const sanitized_data = this.sanitize_data(data);
    const current_data = { ...this.data };
    deep_merge(current_data, sanitized_data);
    const changed = !deep_equal(this.data, current_data);
    if (!changed) return false;
    this.data = current_data;
    return true;
  }
  /**
   * Sanitizes data for saving. Ensures no circular references.
   * @param {*} data
   * @returns {*} Sanitized data.
   */
  sanitize_data(data) {
    if (data instanceof _CollectionItem) return data.ref;
    if (Array.isArray(data)) return data.map((val) => this.sanitize_data(val));
    if (typeof data === "object" && data !== null) {
      return Object.keys(data).reduce((acc, key) => {
        acc[key] = this.sanitize_data(data[key]);
        return acc;
      }, {});
    }
    return data;
  }
  /**
   * Initializes the item. Override as needed.
   * @param {Object} [input_data] - Additional data that might be provided on creation.
   */
  init(input_data) {
  }
  /**
   * Queues this item for saving.
   */
  queue_save() {
    this._queue_save = true;
  }
  /**
   * Saves this item using its data adapter.
   * @returns {Promise<void>}
   */
  async save() {
    try {
      await this.data_adapter.save_item(this);
      this.init();
    } catch (err) {
      this._queue_save = true;
      console.error(err, err.stack);
    }
  }
  /**
   * Queues this item for loading.
   */
  queue_load() {
    this._queue_load = true;
  }
  /**
   * Loads this item using its data adapter.
   * @returns {Promise<void>}
   */
  async load() {
    try {
      await this.data_adapter.load_item(this);
      this.init();
    } catch (err) {
      this._load_error = err;
      this.on_load_error(err);
    }
  }
  /**
   * Handles load errors by re-queuing for load.
   * Override if needed.
   * @param {Error} err
   */
  on_load_error(err) {
    this.queue_load();
  }
  /**
   * Validates the item before saving. Checks for presence and validity of key.
   * @deprecated should be better handled 2025-12-17 (wrong scope?)
   * @returns {boolean}
   */
  validate_save() {
    if (!this.key) return false;
    if (this.key.trim() === "") return false;
    if (this.key === "undefined") return false;
    return true;
  }
  /**
   * Marks this item as deleted. This does not immediately remove it from memory,
   * but queues a save that will result in the item being removed from persistent storage.
   */
  delete() {
    this.deleted = true;
    this.queue_save();
  }
  /**
   * Filters items in the collection based on provided options.
   * functional filter (returns true or false) for filtering items in collection; called by collection class
   * @param {Object} filter_opts - Filtering options.
   * @param {string} [filter_opts.exclude_key] - A single key to exclude.
   * @param {string[]} [filter_opts.exclude_keys] - An array of keys to exclude. If exclude_key is provided, it's added to this array.
   * @param {string} [filter_opts.exclude_key_starts_with] - Exclude keys starting with this string.
   * @param {string[]} [filter_opts.exclude_key_starts_with_any] - Exclude keys starting with any of these strings.
   * @param {string} [filter_opts.exclude_key_includes] - Exclude keys that include this string.
   * @param {string[]} [filter_opts.exclude_key_includes_any] - Exclude keys that include any of these strings.
   * @param {string} [filter_opts.exclude_key_ends_with] - Exclude keys ending with this string.
   * @param {string[]} [filter_opts.exclude_key_ends_with_any] - Exclude keys ending with any of these strings.
   * @param {string} [filter_opts.key_ends_with] - Include only keys ending with this string.
   * @param {string} [filter_opts.key_starts_with] - Include only keys starting with this string.
   * @param {string[]} [filter_opts.key_starts_with_any] - Include only keys starting with any of these strings.
   * @param {string} [filter_opts.key_includes] - Include only keys that include this string.
   * @returns {boolean} True if the item passes the filter, false otherwise.
   */
  filter(filter_opts = {}) {
    const {
      exclude_key,
      exclude_keys = exclude_key ? [exclude_key] : [],
      exclude_key_starts_with,
      exclude_key_starts_with_any,
      exclude_key_includes,
      exclude_key_includes_any,
      exclude_key_ends_with,
      exclude_key_ends_with_any,
      key_ends_with,
      key_starts_with,
      key_starts_with_any,
      key_includes,
      key_includes_any
    } = filter_opts;
    if (exclude_keys?.includes(this.key)) return false;
    if (exclude_key_starts_with && this.key.startsWith(exclude_key_starts_with)) return false;
    if (exclude_key_starts_with_any && exclude_key_starts_with_any.some((prefix) => this.key.startsWith(prefix))) return false;
    if (exclude_key_includes && this.key.includes(exclude_key_includes)) return false;
    if (exclude_key_includes_any && exclude_key_includes_any.some((include) => this.key.includes(include))) return false;
    if (exclude_key_ends_with && this.key.endsWith(exclude_key_ends_with)) return false;
    if (exclude_key_ends_with_any && exclude_key_ends_with_any.some((suffix) => this.key.endsWith(suffix))) return false;
    if (key_ends_with && !this.key.endsWith(key_ends_with)) return false;
    if (key_starts_with && !this.key.startsWith(key_starts_with)) return false;
    if (key_starts_with_any && !key_starts_with_any.some((prefix) => this.key.startsWith(prefix))) return false;
    if (key_includes && !this.key.includes(key_includes)) return false;
    if (key_includes_any && !key_includes_any.some((include) => this.key.includes(include))) return false;
    return true;
  }
  filter_and_score(params = {}) {
    if (this.filter(params.filter) === false) return null;
    return this.score(params);
  }
  score(params = {}) {
    const score_action = this.actions[params.score_algo_key];
    if (typeof score_action !== "function") throw new Error(`Missing score action: ${params.score_algo_key}`);
    return {
      ...score_action(params) || {},
      item: this
    };
  }
  get actions() {
    if (!this._actions) {
      this._actions = create_actions_proxy(this, {
        ...this.env.config.actions || {},
        // main actions scope for actions/ exports
        ...this.env.opts.items?.[this.item_type_key]?.actions || {}
        // DEPRECATED OR KEEP?
      });
    }
    return this._actions;
  }
  /**
   * Derives the collection key from the class name.
   * @returns {string}
   */
  static get collection_key() {
    let name = this.name;
    if (name.match(/\d$/)) name = name.slice(0, -1);
    return collection_instance_name_from(name);
  }
  /**
   * @returns {string} The collection key for this item.
   */
  get collection_key() {
    let name = this.constructor.name;
    if (name.match(/\d$/)) name = name.slice(0, -1);
    return collection_instance_name_from(name);
  }
  /**
   * Retrieves the parent collection from the environment.
   * @returns {Collection}
   */
  get collection() {
    return this.env[this.collection_key];
  }
  /**
   * @returns {string} The item's key.
   */
  get key() {
    return this.data?.key || this.get_key();
  }
  get item_type_key() {
    let name = this.constructor.name;
    if (name.match(/\d$/)) name = name.slice(0, -1);
    return camel_case_to_snake_case(name);
  }
  /**
   * Emits an event with item metadata.
   *
   * @param {string} event_key
   * @param {Object} [payload={}]
   * @returns {void}
   */
  emit_event(event_key, payload = {}) {
    this.env.events?.emit(event_key, { collection_key: this.collection_key, item_key: this.key, ...payload });
  }
  emit_info_event(event_key, payload = {}) {
    this.emit_event(event_key, { level: "info", ...payload });
  }
  emit_error_event(event_key, payload = {}) {
    this.emit_event(event_key, { level: "error", ...payload });
  }
  on_event(event_key, callback) {
    return this.env.events?.on(event_key, (payload) => {
      if (payload?.item_key && payload.item_key !== this.key) return;
      callback(payload);
    });
  }
  once_event(event_key, callback) {
    return this.env.events?.once(event_key, (payload) => {
      if (payload?.item_key && payload.item_key !== this.key) return;
      callback(payload);
    });
  }
  /**
   * @returns {Object} The data adapter for this item's collection.
   */
  get data_adapter() {
    return this.collection.data_adapter;
  }
  /**
   * @returns {Object} The filesystem adapter.
   */
  get data_fs() {
    return this.collection.data_fs;
  }
  /**
   * Access to collection-level settings.
   * @returns {Object}
   */
  get settings() {
    if (!this.env.settings[this.collection_key]) this.env.settings[this.collection_key] = {};
    return this.env.settings[this.collection_key];
  }
  set settings(settings) {
    this.env.settings[this.collection_key] = settings;
    this.env.smart_settings.save();
  }
  /**
   * A simple reference object for this item.
   * @deprecated 2025-11-11 lacks adoption
   * @returns {{collection_key: string, key: string}}
   */
  get ref() {
    return { collection_key: this.collection_key, key: this.key };
  }
  /**
   * @deprecated use env.smart_components~~env.smart_view~~ instead
   */
  get smart_view() {
    if (!this._smart_view) this._smart_view = this.env.init_module("smart_view");
    return this._smart_view;
  }
  /**
   * Retrieves the display name of the collection item.
   * @readonly
   * @deprecated Use `get_item_display_name(key, show_full_path)` instead (keep UI logic out of collections).
   * @returns {string} The display name.
   */
  get name() {
    return get_item_display_name(
      this.key,
      this.env.settings.smart_view_filter?.show_full_path
    );
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-collections/collection.js
var AsyncFunction = Object.getPrototypeOf(async function() {
}).constructor;
var Collection = class {
  static version = 1e-3;
  /**
   * Constructs a new Collection instance.
   *
   * @param {Object} env - The environment context containing configurations and adapters.
   * @param {Object} [opts={}] - Optional configuration.
   * @param {string} [opts.collection_key] - Custom key to override default collection name.
   * @param {string} [opts.data_dir] - Custom data directory path.
   */
  constructor(env, opts = {}) {
    env.create_env_getter(this);
    this.opts = opts;
    if (opts.collection_key) this.collection_key = opts.collection_key;
    this.env[this.collection_key] = this;
    this.config = this.env.config;
    this.items = {};
    this.loaded = null;
    this._loading = false;
    this.load_time_ms = null;
    this.settings_container = null;
  }
  /**
   * Initializes a new collection in the environment. Override in subclass if needed.
   *
   * @param {Object} env
   * @param {Object} [opts={}]
   * @returns {Promise<void>}
   */
  static async init(env, opts = {}) {
    env[this.collection_key] = new this(env, opts);
    await env[this.collection_key].init();
    env.collections[this.collection_key] = "init";
  }
  /**
   * The unique collection key derived from the class name.
   * @returns {string}
   */
  static get collection_key() {
    let name = this.name;
    if (name.match(/\d$/)) name = name.slice(0, -1);
    return name.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase();
  }
  /**
   * Instance-level init. Override in subclasses if necessary.
   * @returns {Promise<void>}
   */
  async init() {
  }
  /**
   * Creates or updates an item in the collection.
   * - If `data` includes a key that matches an existing item, that item is updated.
   * - Otherwise, a new item is created.
   * After updating or creating, the item is validated. If validation fails, the item is logged and returned without being saved.
   * If validation succeeds for a new item, it is added to the collection and marked for saving.
   *
   * If the item’s `init()` method is async, a promise is returned that resolves once init completes.
   * 
   * NOTE: wrapping in try/catch seems to fail to catch errors thrown in async init functions when awaiting create_or_update
   *
   * @param {Object} [data={}] - Data for creating/updating an item.
   * @returns {Promise<Item>|Item} The created or updated item. May return a promise if `init()` is async.
   */
  create_or_update(data = {}) {
    const existing_item = this.find_by(data);
    const item = existing_item ? existing_item : new this.item_type(this.env);
    item._queue_save = !existing_item;
    const data_changed = item.update_data(data);
    if (!existing_item && !item.validate_save()) {
      return item;
    }
    if (!existing_item) {
      this.set(item);
    }
    if (existing_item && !data_changed) return existing_item;
    if (item.init instanceof AsyncFunction) {
      return new Promise((resolve) => {
        item.init(data).then(() => resolve(item));
      });
    }
    item.init(data);
    return item;
  }
  /**
   * Finds an item by partial data match (first checks key). If `data.key` provided,
   * returns the item with that key; otherwise attempts a match by merging data.
   *
   * @param {Object} data - Data to match against.
   * @returns {Item|null}
   */
  find_by(data) {
    if (data.key) return this.get(data.key);
    const temp = new this.item_type(this.env);
    const temp_data = JSON.parse(JSON.stringify(data, temp.sanitize_data(data)));
    deep_merge(temp.data, temp_data);
    return temp.key ? this.get(temp.key) : null;
  }
  /**
   * Filters items based on provided filter options or a custom function.
   *
   * @param {Object|Function} [filter_opts={}] - Filter options or a predicate function.
   * @returns {Item[]} Array of filtered items.
   */
  filter(filter_opts = {}) {
    if (typeof filter_opts === "function") {
      return Object.values(this.items).filter(filter_opts);
    }
    const results = [];
    const { first_n } = filter_opts;
    for (const item of Object.values(this.items)) {
      if (first_n && results.length >= first_n) break;
      if (item.filter(filter_opts)) results.push(item);
    }
    return results;
  }
  /**
   * Alias for `filter()`
   * @param {Object|Function} filter_opts
   * @returns {Item[]}
   */
  list(filter_opts) {
    return this.filter(filter_opts);
  }
  /**
   * Retrieves an item by key.
   * @param {string} key
   * @returns {Item|undefined}
   */
  get(key) {
    return this.items[key];
  }
  /**
   * Retrieves multiple items by an array of keys.
   * @param {string[]} keys
   * @returns {Item[]}
   */
  get_many(keys = []) {
    if (!Array.isArray(keys)) {
      console.error("get_many called with non-array keys:", keys);
      return [];
    }
    return keys.map((key) => this.get(key)).filter(Boolean);
  }
  /**
   * Retrieves a random item from the collection, optionally filtered by options.
   * @param {Object} [opts]
   * @returns {Item|undefined}
   */
  get_rand(opts = null) {
    if (opts) {
      const filtered = this.filter(opts);
      return filtered[Math.floor(Math.random() * filtered.length)];
    }
    const keys = this.keys;
    return this.items[keys[Math.floor(Math.random() * keys.length)]];
  }
  /**
   * Adds or updates an item in the collection.
   * @param {Item} item
   */
  set(item) {
    if (!item.key) throw new Error("Item must have a key property");
    this.items[item.key] = item;
  }
  /**
   * Updates multiple items by their keys.
   * @param {string[]} keys
   * @param {Object} data
   */
  update_many(keys = [], data = {}) {
    this.get_many(keys).forEach((item) => item.update_data(data));
  }
  /**
   * Clears all items from the collection.
   */
  clear() {
    this.items = {};
  }
  /**
   * @returns {string} The collection key, can be overridden by opts.collection_key
   */
  get collection_key() {
    return this._collection_key ? this._collection_key : this.constructor.collection_key;
  }
  set collection_key(key) {
    this._collection_key = key;
  }
  /**
   * Lazily initializes and returns the data adapter instance for this collection.
   * @returns {Object} The data adapter instance.
   */
  get data_adapter() {
    if (!this._data_adapter) {
      const AdapterClass = this.get_adapter_class("data");
      this._data_adapter = new AdapterClass(this);
    }
    return this._data_adapter;
  }
  /**
   * @private
   * @param {string} type
   * @returns {Function}
   */
  get_adapter_class(type) {
    const config = this.env.opts.collections?.[this.collection_key];
    const adapter_key = type + "_adapter";
    const adapter_module = config?.[adapter_key] ?? this.env.opts.collections?.smart_collections?.[adapter_key];
    if (typeof adapter_module === "function") return adapter_module;
    if (typeof adapter_module?.collection === "function") return adapter_module.collection;
    throw new Error(`No '${type}' adapter class found for ${this.collection_key} or smart_collections`);
  }
  /**
   * Data directory strategy for this collection. Defaults to 'multi'.
   * @deprecated should be handled in adapters (2025-12-09)
   * @returns {string}
   */
  get data_dir() {
    return this.collection_key;
  }
  /**
   * File system adapter from the environment.
   * @returns {Object}
   */
  get data_fs() {
    return this.env.data_fs;
  }
  /**
   * Derives the corresponding item class name based on this collection's class name.
   * @returns {string}
   */
  get item_class_name() {
    let name = this.constructor.name;
    if (name.match(/\d$/)) name = name.slice(0, -1);
    if (name.endsWith("ies")) return name.slice(0, -3) + "y";
    else if (name.endsWith("s")) return name.slice(0, -1);
    return name + "Item";
  }
  /**
   * Derives a readable item name from the item class name.
   * @returns {string}
   */
  get item_name() {
    return this.item_class_name.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase();
  }
  /**
   * Retrieves the item type (constructor) from the environment.
   * @deprecated replace with item_class with strict adherence to conventions (2025-10-28)
   * @returns {Function} Item constructor.
   */
  get item_type() {
    if (!this._item_type) this._item_type = this.resolve_item_type();
    return this._item_type;
  }
  // TEMP resolver (2025-11-03): until better handled on merging configs at obsidian-smart-env startup
  /**
   * @private
   * @returns {Function}
   */
  resolve_item_type() {
    const available = [
      this.env.config?.items?.[this.item_name],
      this.opts.item_type
      // Is this necessary? (2026-04-11 needs review - ideally should be able to rely on env.config for this)
    ].filter(Boolean).sort((a, b) => {
      const a_version = a?.class?.version || a.version || 0;
      const b_version = b?.class?.version || b.version || 0;
      return b_version - a_version;
    });
    if (available.length === 0) {
      throw new Error(`No item_type found for collection '${this.collection_key}' with item_name '${this.item_name}' or class_name '${this.item_class_name}'`);
    }
    return available[0].class || available[0];
  }
  /**
   * Returns an array of all keys in the collection.
   * @returns {string[]}
   */
  get keys() {
    return Object.keys(this.items);
  }
  /**
   * @deprecated use data_adapter instead (2024-09-14)
   */
  get adapter() {
    return this.data_adapter;
  }
  /**
   * @method process_save_queue
   * @description 
   * Saves items flagged for saving (_queue_save) back to AJSON or SQLite. This ensures persistent storage 
   * of any updates made since last load/import. This method also writes changes to disk (AJSON files or DB).
   */
  async process_save_queue(opts = {}) {
    if (opts.force) {
      Object.values(this.items).forEach((item) => item._queue_save = true);
    }
    await this.data_adapter.process_save_queue(opts);
  }
  /**
   * @alias process_save_queue
   * @returns {Promise<void>}
   */
  async save(opts = {}) {
    await this.process_save_queue(opts);
  }
  /**
   * @method process_load_queue
   * @description 
   * Loads items that have been flagged for loading (_queue_load). This may involve 
   * reading from AJSON/SQLite or re-importing from markdown if needed. 
   * Called once initial environment is ready and collections are known.
   */
  async process_load_queue() {
    await this.data_adapter.process_load_queue();
  }
  /**
   * Retrieves processed settings configuration.
   * @returns {Object}
   */
  get settings_config() {
    return this.process_settings_config({});
  }
  /**
   * Processes given settings config, adding prefixes and handling conditionals.
   * @deprecated removing settings_config from collections (2025-11-24)
   *
   * @private
   * @param {Object} _settings_config
   * @param {string} [prefix='']
   * @returns {Object}
   */
  process_settings_config(_settings_config, prefix = "") {
    const add_prefix = (key) => prefix && !key.includes(`${prefix}.`) ? `${prefix}.${key}` : key;
    return Object.entries(_settings_config).reduce((acc, [key, val]) => {
      let new_val = { ...val };
      if (new_val.conditional) {
        if (!new_val.conditional(this)) return acc;
        delete new_val.conditional;
      }
      if (new_val.callback) new_val.callback = add_prefix(new_val.callback);
      if (new_val.btn_callback) new_val.btn_callback = add_prefix(new_val.btn_callback);
      if (new_val.options_callback) new_val.options_callback = add_prefix(new_val.options_callback);
      const new_key = add_prefix(this.process_setting_key(key));
      acc[new_key] = new_val;
      return acc;
    }, {});
  }
  /**
   * Processes an individual setting key. Override if needed.
   * @param {string} key
   * @returns {string}
   */
  process_setting_key(key) {
    return key;
  }
  /**
   * Default settings for this collection. Override in subclasses as needed.
   * @returns {Object}
   */
  get default_settings() {
    return {};
  }
  /**
   * Current settings for the collection.
   * Initializes with default settings if none exist.
   * @returns {Object}
   */
  get settings() {
    if (!this.env.settings[this.collection_key]) {
      this.env.settings[this.collection_key] = this.default_settings;
    }
    return this.env.settings[this.collection_key];
  }
  /**
   * Unloads collection data from memory.
   */
  unload() {
    this.clear();
    this.unloaded = true;
    this.env.collections[this.collection_key] = null;
  }
  /**
   * Emits an event with collection metadata.
   *
   * @param {string} event_key
   * @param {Object} [payload={}]
   * @returns {void}
   */
  emit_event(event_key, payload = {}) {
    this.env.events?.emit(event_key, { collection_key: this.collection_key, ...payload });
  }
  emit_info_event(event_key, payload = {}) {
    this.emit_event(event_key, { level: "info", ...payload });
  }
  emit_warning_event(event_key, payload = {}) {
    this.emit_event(event_key, { level: "warning", ...payload });
  }
  emit_error_event(event_key, payload = {}) {
    this.emit_event(event_key, { level: "error", ...payload });
  }
  on_event(event_key, callback) {
    return this.env.events?.on(event_key, (payload) => {
      if (payload?.collection_key && payload.collection_key !== this.collection_key) return;
      callback(payload);
    });
  }
  /**
   * Lazily binds action functions to the collection instance.
   *
   * @returns {Object} Bound action functions keyed by name.
   */
  get actions() {
    if (!this.constructor.key) this.constructor.key = this.collection_key;
    if (!this._actions) {
      const actions_modules = {
        ...this.env?.config?.actions || {},
        ...this.env?.config?.collections?.[this.collection_key]?.actions || {},
        ...this.env?.opts?.collections?.[this.collection_key]?.actions || {},
        ...this.opts?.actions || {}
      };
      this._actions = create_actions_proxy(this, actions_modules);
    }
    return this._actions;
  }
  /**
   * Clears cached actions proxy and rebuilds on next access.
   * @returns {Object} Rebuilt proxy with latest source snapshot.
   */
  refresh_actions() {
    this._actions = null;
    return this.actions;
  }
  // debounce running process save queue
  queue_save() {
    if (this._debounce_queue_save) clearTimeout(this._debounce_queue_save);
    this._debounce_queue_save = setTimeout(() => {
      this.process_save_queue();
    }, 750);
  }
  // BEGIN DEPRECATED
  /**
   * @deprecated use env.smart_components~~env.smart_view~~ instead (2026-02-11)
   * @returns {Object} smart_view instance
   */
  get smart_view() {
    if (!this._smart_view) this._smart_view = this.env.init_module("smart_view");
    return this._smart_view;
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-entities/adapters/_adapter.js
var EntitiesVectorAdapter = class {
  /**
   * @constructor
   * @param {import('smart-entities').SmartEntities} collection - The collection (SmartEntities or derived class) instance.
   */
  constructor(collection) {
    this.collection = collection;
  }
  /**
   * Find the nearest entities to the given vector.
   * @async
   * @param {number[]} vec - The reference vector.
   * @param {Object} [filter={}] - Optional filters (limit, exclude, etc.)
   * @returns {Promise<Array<{item: import('smart-entities').SmartEntity, score:number}>>} Array of results sorted by score descending.
   * @abstract
   * @throws {Error} Not implemented by default.
   */
  async nearest(vec, filter = {}) {
    throw new Error("EntitiesVectorAdapter.nearest() not implemented");
  }
  /**
   * Find the furthest entities from the given vector.
   * @async
   * @param {number[]} vec - The reference vector.
   * @param {Object} [filter={}] - Optional filters (limit, exclude, etc.)
   * @returns {Promise<Array<{item: import('smart-entities').SmartEntity, score:number}>>} Array of results sorted by score ascending (furthest).
   * @abstract
   * @throws {Error} Not implemented by default.
   */
  async furthest(vec, filter = {}) {
    throw new Error("EntitiesVectorAdapter.furthest() not implemented");
  }
  /**
   * Embed a batch of entities.
   * @async
   * @param {Object[]} entities - Array of entity instances to embed.
   * @returns {Promise<void>}
   * @abstract
   * @throws {Error} Not implemented by default.
   */
  async embed_batch(entities) {
    throw new Error("EntitiesVectorAdapter.embed_batch() not implemented");
  }
  /**
   * Process a queue of entities waiting to be embedded.
   * Typically, this will call embed_batch in batches and update entities.
   * @async
   * @param {Object[]} embed_queue - Array of entities to embed.
   * @returns {Promise<void>}
   * @abstract
   * @throws {Error} Not implemented by default.
   */
  async process_embed_queue(embed_queue) {
    throw new Error("EntitiesVectorAdapter.process_embed_queue() not implemented");
  }
};
var EntityVectorAdapter = class {
  /**
   * @constructor
   * @param {import('smart-entities').SmartEntity} item - The SmartEntity instance that this adapter is associated with.
   */
  constructor(item) {
    this.item = item;
  }
  /**
   * Retrieve the current vector embedding for this entity.
   * @async
   * @returns {Promise<number[]|undefined>} The entity's vector or undefined if not set.
   * @abstract
   * @throws {Error} Not implemented by default.
   */
  async get_vec() {
    throw new Error("EntityVectorAdapter.get_vec() not implemented");
  }
  /**
   * Store/update the vector embedding for this entity.
   * @async
   * @param {number[]} vec - The vector to set.
   * @returns {Promise<void>}
   * @abstract
   * @throws {Error} Not implemented by default.
   */
  async set_vec(vec) {
    throw new Error("EntityVectorAdapter.set_vec() not implemented");
  }
  /**
   * Delete/remove the vector embedding for this entity.
   * @async
   * @returns {Promise<void>}
   * @abstract
   * @throws {Error} Not implemented by default.
   */
  async delete_vec() {
    throw new Error("EntityVectorAdapter.delete_vec() not implemented");
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-utils/results_acc.js
function results_acc(_acc, result, ct = 10) {
  if (_acc.results.size < ct) {
    _acc.results.add(result);
    if (_acc.results.size === ct && _acc.min === Number.POSITIVE_INFINITY) {
      let { minScore, minObj } = find_min(_acc.results);
      _acc.min = minScore;
      _acc.minResult = minObj;
    }
  } else if (result.score > _acc.min) {
    _acc.results.add(result);
    _acc.results.delete(_acc.minResult);
    let { minScore, minObj } = find_min(_acc.results);
    _acc.min = minScore;
    _acc.minResult = minObj;
  }
}
function furthest_acc(_acc, result, ct = 10) {
  if (_acc.results.size < ct) {
    _acc.results.add(result);
    if (_acc.results.size === ct && _acc.max === Number.NEGATIVE_INFINITY) {
      let { maxScore, maxObj } = find_max(_acc.results);
      _acc.max = maxScore;
      _acc.maxResult = maxObj;
    }
  } else if (result.score < _acc.max) {
    _acc.results.add(result);
    _acc.results.delete(_acc.maxResult);
    let { maxScore, maxObj } = find_max(_acc.results);
    _acc.max = maxScore;
    _acc.maxResult = maxObj;
  }
}
function find_min(results) {
  let minScore = Number.POSITIVE_INFINITY;
  let minObj = null;
  for (const obj of results) {
    if (obj.score < minScore) {
      minScore = obj.score;
      minObj = obj;
    }
  }
  return { minScore, minObj };
}
function find_max(results) {
  let maxScore = Number.NEGATIVE_INFINITY;
  let maxObj = null;
  for (const obj of results) {
    if (obj.score > maxScore) {
      maxScore = obj.score;
      maxObj = obj;
    }
  }
  return { maxScore, maxObj };
}

// node_modules/obsidian-smart-env/node_modules/smart-utils/sort_by_score.js
function sort_by_score(a, b) {
  const epsilon = 1e-9;
  const score_diff = a.score - b.score;
  if (Math.abs(score_diff) < epsilon) return 0;
  return score_diff > 0 ? -1 : 1;
}
function sort_by_score_descending(a, b) {
  return sort_by_score(a, b);
}
function sort_by_score_ascending(a, b) {
  return sort_by_score(a, b) * -1;
}

// node_modules/obsidian-smart-env/node_modules/smart-entities/adapters/default.js
var DefaultEntitiesVectorAdapter = class extends EntitiesVectorAdapter {
  constructor(collection) {
    super(collection);
    this._is_processing_embed_queue = false;
    this._resume_after_pause = false;
    this._resume_after_pause_delay = 0;
    this._resume_embed_timeout = null;
    this._reset_embed_queue_stats();
  }
  /**
   * Find the nearest entities to the given vector.
   * @async
   * @param {number[]} vec - The reference vector.
   * @param {Object} [filter={}] - Optional filters (limit, exclude, etc.)
   * @returns {Promise<Array<{item:Object, score:number}>>} Array of results sorted by score descending.
   */
  async nearest(vec, filter = {}) {
    if (!vec || !Array.isArray(vec)) {
      throw new Error("Invalid vector input to nearest()");
    }
    const {
      limit = 50
    } = filter;
    const nearest = this.collection.filter(filter).reduce((acc, item) => {
      if (!item.vec) return acc;
      const result = { item, score: cos_sim(vec, item.vec) };
      results_acc(acc, result, limit);
      return acc;
    }, { min: 0, results: /* @__PURE__ */ new Set() });
    return Array.from(nearest.results).sort(sort_by_score_descending);
  }
  /**
   * Find the furthest entities from the given vector.
   * @async
   * @param {number[]} vec - The reference vector.
   * @param {Object} [filter={}] - Optional filters (limit, exclude, etc.)
   * @returns {Promise<Array<{item:Object, score:number}>>} Array of results sorted by score ascending (furthest).
   */
  async furthest(vec, filter = {}) {
    if (!vec || !Array.isArray(vec)) {
      throw new Error("Invalid vector input to furthest()");
    }
    const {
      limit = 50
    } = filter;
    const furthest = this.collection.filter(filter).reduce((acc, item) => {
      if (!item.vec) return acc;
      const result = { item, score: cos_sim(vec, item.vec) };
      furthest_acc(acc, result, limit);
      return acc;
    }, { max: 0, results: /* @__PURE__ */ new Set() });
    return Array.from(furthest.results).sort(sort_by_score_ascending);
  }
  /**
   * Embed a batch of entities.
   * @async
   * @param {Object[]} entities - Array of entity instances to embed.
   * @returns {Promise<void>}
   */
  async embed_batch(entities) {
    if (!this.collection.embed_model) {
      throw new Error("No embed_model found in collection for embedding");
    }
    await Promise.all(entities.map((entity) => entity.get_embed_input()));
    const embeddings = await this.collection.embed_model.embed_batch(entities);
    embeddings.forEach((embedding, index) => {
      const entity = entities[index];
      entity.vec = embedding.vec;
      entity.data.last_embed = entity.data.last_read;
      if (embedding.tokens !== void 0) entity.tokens = embedding.tokens;
    });
  }
  /**
   * Process a queue of entities waiting to be embedded.
   * Prevents multiple concurrent runs by using `_is_processing_embed_queue`.
   * Paused queues fail closed and do not restart until resume explicitly clears
   * the paused state.
   * @async
   * @returns {Promise<void>}
   */
  async process_embed_queue() {
    if (this._is_processing_embed_queue) {
      console.log("process_embed_queue is already running, skipping concurrent call.");
      return;
    }
    if (this.is_embed_queue_paused() && !this._resume_after_pause) {
      console.log("process_embed_queue is paused, skipping restart until resume.");
      return;
    }
    this._is_processing_embed_queue = true;
    this._embed_run_error = false;
    try {
      if (!this.collection.embed_model.is_loaded) {
        await this.collection.embed_model.load();
      }
    } catch (error) {
      this.collection.emit_event("embed_model:load_failed", {
        event_source: "process_embed_queue"
      });
      this._emit_embedding_error({
        message: `Failed to load embedding model ${this.collection.embed_model_key}.`,
        details: error?.message || String(error || "")
      });
      return;
    }
    try {
      const datetime_start = Date.now();
      console.log(`Getting embed queue for ${this.collection.collection_key}...`);
      await new Promise((resolve) => setTimeout(resolve, 1));
      const embed_queue = this.collection.embed_queue;
      this._reset_embed_queue_stats();
      const embedded_keys_by_collection = {};
      if (this.collection.embed_model_key === "None") {
        console.log(`Smart Connections: No active embedding model for ${this.collection.collection_key}, skipping embedding`);
        return;
      }
      if (!this.collection.embed_model) {
        console.log(`Smart Connections: No active embedding model for ${this.collection.collection_key}, skipping embedding`);
        return;
      }
      if (!embed_queue.length) {
        console.log(`Smart Connections: No items in ${this.collection.collection_key} embed queue`);
        return;
      }
      console.log(`Time spent getting embed queue: ${Date.now() - datetime_start}ms`);
      console.log(`Processing ${this.collection.collection_key} embed queue: ${embed_queue.length} items`);
      this.current_queue_total = embed_queue.length;
      this._start_embed_progress_state(embed_queue.length);
      for (let index = 0; index < embed_queue.length; index += this.collection.embed_model.batch_size) {
        if (this.is_queue_halted) {
          break;
        }
        const batch = embed_queue.slice(index, index + this.collection.embed_model.batch_size);
        await Promise.all(batch.map((item) => item.get_embed_input()));
        try {
          const start_time = Date.now();
          await this.embed_batch(batch);
          this.total_time += Date.now() - start_time;
        } catch (error) {
          console.error(error);
          console.error(`Error processing ${this.collection.collection_key} embed queue: ` + JSON.stringify(error || {}, null, 2));
          this._emit_embedding_error({
            message: `Embedding failed while processing ${this.collection.collection_key}.`,
            details: error?.message || JSON.stringify(error || {}, null, 2)
          });
          break;
        }
        batch.forEach((item) => {
          item.embed_hash = item.read_hash;
          item._queue_save = true;
          embedded_keys_by_collection[item.collection_key] ||= [];
          embedded_keys_by_collection[item.collection_key].push(item.key);
        });
        this.embedded_total += batch.length;
        this.total_tokens += batch.reduce((acc, item) => acc + (item.tokens || 0), 0);
        const processed_all2 = this.embedded_total >= embed_queue.length;
        if (this.is_queue_halted && !processed_all2) {
          this._update_paused_progress_state(embed_queue.length, this.progress_state?.reason || "");
        } else {
          this._update_embed_progress_state(embed_queue.length);
        }
        if (this.is_queue_halted && processed_all2) {
          this.is_queue_halted = false;
        }
        if (this.should_show_embed_progress_notice || processed_all2) {
          this._show_embed_progress_notice(embed_queue.length);
        }
        if (this.embedded_total - this.last_save_total > 99) {
          this.last_save_total = this.embedded_total;
          await this.collection.process_save_queue();
          if (this.collection.block_collection) {
            console.log(`Saving ${this.collection.block_collection.collection_key} block collection`);
            await this.collection.block_collection.process_save_queue();
          }
        }
      }
      Object.entries(embedded_keys_by_collection).forEach(([collection_key, keys]) => {
        this.collection.env.events?.emit("items:embedded", {
          collection_key,
          keys,
          event_source: "process_embed_queue",
          skip_save_log_collection: true
        });
      });
      const processed_all = this.embedded_total >= embed_queue.length;
      const is_paused = Boolean(this.progress_state?.paused) && !processed_all;
      if (!is_paused && !this._embed_run_error) {
        this._show_embed_completion_notice(embed_queue.length);
      }
      await this.collection.process_save_queue();
      if (this.collection.block_collection) {
        await this.collection.block_collection.process_save_queue();
      }
    } finally {
      this._is_processing_embed_queue = false;
      const should_resume_after_pause = this._resume_after_pause && this.is_embed_queue_paused();
      const resume_delay = this._resume_after_pause_delay || 0;
      this._resume_after_pause = false;
      this._resume_after_pause_delay = 0;
      if (should_resume_after_pause) {
        this.resume_embed_queue_processing(resume_delay);
      }
    }
  }
  get should_show_embed_progress_notice() {
    if (Date.now() - (this.last_notice_time ?? 0) > 2e4) {
      return true;
    }
    return this.embedded_total - this.last_notice_embedded_total >= 100;
  }
  /**
   * @returns {object|null}
   */
  get_progress_state() {
    return this.progress_state ? { ...this.progress_state } : null;
  }
  /**
   * Displays embed progress via env events and internal state.
   * @private
   * @param {number} embed_queue_length
   * @returns {void}
   */
  _show_embed_progress_notice(embed_queue_length) {
    this.last_notice_time = Date.now();
    this.last_notice_embedded_total = this.embedded_total;
    this._update_embed_progress_state(embed_queue_length);
    this.collection.emit_event("embedding:progress", {
      progress: this.embedded_total,
      total: embed_queue_length,
      tokens_per_second: this._calculate_embed_tokens_per_second(),
      model_name: this.collection.embed_model_key,
      event_source: "process_embed_queue",
      skip_save_log_collection: true
    });
  }
  /**
   * Displays the embedding completion notice.
   * @private
   * @param {number} embed_queue_length
   * @returns {void}
   */
  _show_embed_completion_notice(embed_queue_length) {
    const payload = {
      total_embeddings: this.embedded_total,
      total: embed_queue_length,
      tokens_per_second: this._calculate_embed_tokens_per_second(),
      model_name: this.collection.embed_model_key,
      event_source: "process_embed_queue"
    };
    this._set_progress_state(null);
    if (this.embedded_total > 100) {
      this.collection.emit_event("embedding:completed", {
        level: "info",
        message: `Embedding completed for ${this.embedded_total} item${this.embedded_total === 1 ? "" : "s"}.`,
        ...payload
      });
      return;
    }
    this.collection.emit_event("embedding:completed", payload);
  }
  /**
   * Halts the embed queue processing.
   * The current batch is allowed to finish, then the next loop iteration latches
   * the paused state and exits. This keeps the status bar stable and prevents a
   * half-finished batch from corrupting queue state.
   * Duplicate pause requests fail closed and do not emit extra paused events.
   *
   * @param {string|null} msg - Optional message.
   * @returns {void}
   */
  halt_embed_queue_processing(msg = null) {
    const total = this.progress_state?.total || this.current_queue_total || 0;
    const next_reason = msg || this.progress_state?.reason || "";
    if (this.is_embed_queue_paused()) {
      this._update_paused_progress_state(total, next_reason);
      return;
    }
    this.is_queue_halted = true;
    this._set_progress_state({
      active: true,
      paused: true,
      progress: this.embedded_total,
      total,
      tokens_per_second: this._calculate_embed_tokens_per_second(),
      model_name: this.collection.embed_model_key,
      reason: next_reason
    });
    this.collection.emit_event("embedding:paused", {
      level: "attention",
      message: `Embedding paused at ${this.embedded_total}/${total}.`,
      details: next_reason,
      progress: this.embedded_total,
      total,
      tokens_per_second: this._calculate_embed_tokens_per_second(),
      model_name: this.collection.embed_model_key,
      event_source: "halt_embed_queue_processing"
    });
  }
  /**
   * Returns whether the adapter is currently paused.
   * Paused state remains sticky until resume explicitly clears it.
   * @returns {boolean}
   */
  is_embed_queue_paused() {
    return Boolean(this.progress_state?.paused);
  }
  /**
   * Resumes the embed queue processing after a delay.
   * If the active batch has not yet latched the pause request, resume is deferred
   * until the current run exits cleanly.
   * @param {number} [delay=0] - The delay in milliseconds before resuming.
   * @returns {void}
   */
  resume_embed_queue_processing(delay = 0) {
    console.log("resume_embed_queue_processing");
    if (this._resume_embed_timeout) {
      clearTimeout(this._resume_embed_timeout);
      this._resume_embed_timeout = null;
    }
    if (this._is_processing_embed_queue && this.is_queue_halted) {
      this._resume_after_pause = true;
      this._resume_after_pause_delay = delay;
      return;
    }
    this.is_queue_halted = false;
    this._set_progress_state(null);
    this.collection.emit_event("embedding:resumed", {
      model_name: this.collection.embed_model_key,
      event_source: "resume_embed_queue_processing"
    });
    this._resume_embed_timeout = setTimeout(() => {
      this._resume_embed_timeout = null;
      this.embedded_total = 0;
      this.process_embed_queue();
    }, delay);
  }
  /**
   * Calculates the number of tokens processed per second.
   * @private
   * @returns {number} Tokens per second.
   */
  _calculate_embed_tokens_per_second() {
    const elapsed_time = this.total_time / 1e3;
    return Math.round(this.total_tokens / (elapsed_time || 1));
  }
  /**
   * Resets the statistics related to embed queue processing.
   * @private
   * @returns {void}
   */
  _reset_embed_queue_stats() {
    this.collection._embed_queue = [];
    this.embedded_total = 0;
    this.is_queue_halted = false;
    this.last_save_total = 0;
    this.last_notice_embedded_total = 0;
    this.last_notice_time = 0;
    this.total_tokens = 0;
    this.total_time = 0;
    this.current_queue_total = 0;
    this.progress_state = null;
    this._embed_run_error = false;
    this._resume_after_pause = false;
    this._resume_after_pause_delay = 0;
    if (this._resume_embed_timeout) {
      clearTimeout(this._resume_embed_timeout);
      this._resume_embed_timeout = null;
    }
  }
  /**
   * @private
   * @param {object|null} next_state
   * @returns {void}
   */
  _set_progress_state(next_state = null) {
    this.progress_state = next_state ? {
      ...next_state,
      updated_at: Date.now()
    } : null;
  }
  /**
   * @private
   * @param {number} total
   * @returns {void}
   */
  _start_embed_progress_state(total) {
    this._set_progress_state({
      active: true,
      paused: false,
      progress: 0,
      total,
      tokens_per_second: 0,
      model_name: this.collection.embed_model_key
    });
    this.collection.emit_event("embedding:started", {
      progress: 0,
      total,
      model_name: this.collection.embed_model_key,
      event_source: "process_embed_queue"
    });
  }
  /**
   * @private
   * @param {number} total
   * @returns {void}
   */
  _update_embed_progress_state(total) {
    this._set_progress_state({
      active: true,
      paused: false,
      progress: this.embedded_total,
      total,
      tokens_per_second: this._calculate_embed_tokens_per_second(),
      model_name: this.collection.embed_model_key
    });
  }
  /**
   * @private
   * @param {number} total
   * @param {string} reason
   * @returns {void}
   */
  _update_paused_progress_state(total, reason = "") {
    this._set_progress_state({
      active: true,
      paused: true,
      progress: this.embedded_total,
      total,
      tokens_per_second: this._calculate_embed_tokens_per_second(),
      model_name: this.collection.embed_model_key,
      reason
    });
  }
  /**
   * @private
   * @param {object} [params={}]
   * @param {string} [params.message]
   * @param {string} [params.details]
   * @returns {void}
   */
  _emit_embedding_error(params = {}) {
    const {
      message = "Embedding failed.",
      details = ""
    } = params;
    this._embed_run_error = true;
    this.is_queue_halted = true;
    this._set_progress_state(null);
    this.collection.emit_event("embedding:error", {
      level: "error",
      message,
      details,
      model_name: this.collection.embed_model_key,
      event_source: "process_embed_queue"
    });
  }
  get notices() {
    return this.collection.env.notices;
  }
};
var DefaultEntityVectorAdapter = class extends EntityVectorAdapter {
  get data() {
    return this.item.data;
  }
  /**
   * Retrieve the current vector embedding for this entity.
   * @async
   * @returns {Promise<number[]|undefined>} The entity's vector or undefined if not set.
   */
  async get_vec() {
    return this.vec;
  }
  /**
   * Store/update the vector embedding for this entity.
   * @async
   * @param {number[]} vec - The vector to set.
   * @returns {Promise<void>}
   */
  async set_vec(vec) {
    this.vec = vec;
  }
  /**
   * Delete/remove the vector embedding for this entity.
   * @async
   * @returns {Promise<void>}
   */
  async delete_vec() {
    if (this.item.data?.embeddings?.[this.item.embed_model_key]) {
      delete this.item.data.embeddings[this.item.embed_model_key].vec;
    }
  }
  get vec() {
    return this.item.data?.embeddings?.[this.item.embed_model_key]?.vec;
  }
  set vec(vec) {
    if (!this.item.data.embeddings) {
      this.item.data.embeddings = {};
    }
    if (!this.item.data.embeddings[this.item.embed_model_key]) {
      this.item.data.embeddings[this.item.embed_model_key] = {};
    }
    this.item.data.embeddings[this.item.embed_model_key].vec = vec;
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-entities/actions/find_connections.js
var FRONTMATTER_SUFFIX = "---frontmatter---";
var to_array = (value) => {
  if (Array.isArray(value)) {
    return value.map((entry) => typeof entry === "string" ? entry.trim() : "").filter((entry) => entry.length > 0);
  }
  if (typeof value === "string") {
    const parts = value.includes(",") ? value.split(",") : [value];
    return parts.map((part) => part.trim()).filter((part) => part.length > 0);
  }
  return [];
};
var merge_settings_with_params = (entity, params = {}) => ({
  ...entity.env.settings.smart_view_filter || {},
  ...params,
  entity
});
var remove_limit_fields = (filter_opts) => {
  const next = { ...filter_opts };
  if (typeof next.limit !== "undefined") delete next.limit;
  if (next.filter) {
    next.filter = { ...next.filter };
    if (typeof next.filter.limit !== "undefined") delete next.filter.limit;
  }
  return next;
};
var apply_frontmatter_exclusion = (filter_opts) => {
  if (!filter_opts.exclude_frontmatter_blocks) return filter_opts;
  const next = { ...filter_opts };
  const suffixes = Array.isArray(next.exclude_key_ends_with_any) ? [...next.exclude_key_ends_with_any] : [];
  suffixes.push(FRONTMATTER_SUFFIX);
  next.exclude_key_ends_with_any = suffixes;
  return next;
};
var append_entity_filters = (filter_opts, entity) => {
  if (!entity) return filter_opts;
  const next = { ...filter_opts };
  let exclude_starts = Array.isArray(next.exclude_key_starts_with_any) ? [...next.exclude_key_starts_with_any] : [];
  if (typeof next.exclude_key_starts_with === "string") {
    exclude_starts.push(next.exclude_key_starts_with);
    delete next.exclude_key_starts_with;
  }
  const entity_key = entity.source_key || entity.key;
  if (entity_key) exclude_starts.push(entity_key);
  if (next.exclude_inlinks && Array.isArray(entity.inlinks) && entity.inlinks.length) {
    exclude_starts = [...exclude_starts, ...entity.inlinks.map((i) => i.source_key)];
  }
  if (next.exclude_outlinks && Array.isArray(entity.outlinks) && entity.outlinks.length) {
    exclude_starts = [...exclude_starts, ...entity.outlinks.map((o) => o.key)];
  }
  if (exclude_starts.length) next.exclude_key_starts_with_any = exclude_starts;
  if (next.exclude_filter) {
    const exclude_values = to_array(next.exclude_filter);
    const current = Array.isArray(next.exclude_key_includes_any) ? [...next.exclude_key_includes_any] : [];
    next.exclude_key_includes_any = [...current, ...exclude_values];
  }
  if (next.include_filter) {
    const include_values = to_array(next.include_filter);
    const current = Array.isArray(next.key_includes_any) ? [...next.key_includes_any] : [];
    next.key_includes_any = [...current, ...include_values];
  }
  return next;
};
var create_find_connections_filter_opts = (entity, params = {}) => {
  const merged = merge_settings_with_params(entity, params);
  const without_limits = remove_limit_fields(merged);
  const with_frontmatter = apply_frontmatter_exclusion(without_limits);
  return append_entity_filters(with_frontmatter, entity);
};
var ENTITIES_CONNECTIONS_CACHE = {};
function connections_from_cache(cache_key) {
  return ENTITIES_CONNECTIONS_CACHE[cache_key];
}
function connections_to_cache(cache_key, connections) {
  ENTITIES_CONNECTIONS_CACHE[cache_key] = connections;
}
async function find_connections(params = {}) {
  const limit = params.filter?.limit || params.limit || this.env.settings.smart_view_filter?.results_limit || 10;
  const filter_opts = create_find_connections_filter_opts(this, params);
  if (params.filter?.limit) delete params.filter.limit;
  if (params.limit) delete params.limit;
  const cache_key = this.key + murmur_hash_32_alphanumeric(JSON.stringify({ ...filter_opts, entity: null }));
  if (!ENTITIES_CONNECTIONS_CACHE[cache_key]) {
    const connections = (await this.collection.entities_vector_adapter.nearest(this, filter_opts)).sort(sort_by_score).slice(0, limit);
    connections_to_cache(cache_key, connections);
  }
  return connections_from_cache(cache_key);
}
find_connections.action_type = "connections";

// node_modules/obsidian-smart-env/node_modules/smart-entities/smart_entity.js
var SmartEntity = class extends CollectionItem {
  /**
   * Creates an instance of SmartEntity.
   * @constructor
   * @param {Object} env - The environment instance.
   * @param {Object} [opts={}] - Configuration options.
   */
  constructor(env, opts = {}) {
    super(env, opts);
    this.entity_adapter = new DefaultEntityVectorAdapter(this);
  }
  /**
   * Provides default values for a SmartEntity instance.
   * @static
   * @readonly
   * @returns {Object} The default values.
   */
  static get defaults() {
    return {
      data: {
        path: null,
        last_embed: {
          hash: null
        },
        embeddings: {}
      }
    };
  }
  /**
   * Initializes the SmartEntity instance.
   * Checks if the entity has a vector and if it matches the model dimensions.
   * If not, it queues an embed.
   * Removes embeddings for inactive models.
   * @returns {void}
   */
  init() {
    super.init();
    if (!this.vec?.length) {
      this.entity_adapter.vec = null;
      this.queue_embed();
    }
    Object.entries(this.data.embeddings || {}).forEach(([model, embedding]) => {
      if (model !== this.embed_model_key) {
        this.data.embeddings[model] = null;
        delete this.data.embeddings[model];
      }
    });
  }
  /**
   * Queues the entity for embedding.
   * @returns {void}
   */
  queue_embed() {
    this._queue_embed = this.should_embed;
  }
  /**
   * Prepares the input for embedding.
   * @async
   * @param {string} [content=null] - Optional content to use instead of calling subsequent read()
   * @returns {Promise<void>} Should be overridden in child classes.
   */
  async get_embed_input(content = null) {
  }
  // override in child class
  /**
   * Retrieves the embed input, either from cache or by generating it.
   * @readonly
   * @returns {string|Promise<string>} The embed input string or a promise resolving to it.
   */
  get embed_input() {
    return this._embed_input ? this._embed_input : this.get_embed_input();
  }
  /**
   * Finds connections relevant to this entity based on provided parameters.
   * @async
   * @param {Object} [params={}] - Parameters for finding connections.
   * @deprecated should be in actions (getter) but also see ConnectionsLists (smart-lists) (2026-02-11)
   * @returns {Array<{item:Object, score:number}>} An array of result objects with score and item.
   */
  async find_connections(params = {}) {
    return await this.actions.find_connections(params);
  }
  get read_hash() {
    return this.data.last_read?.hash;
  }
  set read_hash(hash) {
    if (!this.data.last_read) this.data.last_read = {};
    this.data.last_read.hash = hash;
  }
  get embedding_data() {
    if (!this.data.embeddings[this.embed_model_key]) {
      this.data.embeddings[this.embed_model_key] = {};
    }
    return this.data.embeddings[this.embed_model_key];
  }
  get last_embed() {
    if (!this.embedding_data.last_embed) {
      this.embedding_data.last_embed = {};
    }
    return this.embedding_data.last_embed;
  }
  get embed_hash() {
    return this.last_embed?.hash;
  }
  set embed_hash(hash) {
    if (!this.embedding_data.last_embed) this.embedding_data.last_embed = {};
    this.embedding_data.last_embed.hash = hash;
  }
  /**
   * Gets the embed link for the entity.
   * @readonly
   * @returns {string} The embed link.
   */
  get embed_link() {
    return `![[${this.path}]]`;
  }
  /**
   * Gets the key of the embedding model.
   * @readonly
   * @returns {string} The embedding model key.
   */
  get embed_model_key() {
    return this.collection.embed_model_key;
  }
  /**
   * Gets the embedding model instance from the collection.
   * @readonly
   * @returns {Object} The embedding model instance.
   */
  get embed_model() {
    return this.collection.embed_model;
  }
  /**
   * Determines if the entity should be embedded if unembedded. NOT the same as is_unembedded.
   * @readonly
   * @returns {boolean} True if no vector is set, false otherwise.
   */
  get should_embed() {
    return this.size > (this.settings?.min_chars || 300);
  }
  /**
   * Sets the error for the embedding model.
   * @param {string} error - The error message.
   */
  set error(error) {
    this.data.embeddings[this.embed_model_key].error = error;
  }
  /**
   * Gets the number of tokens associated with the entity's embedding.
   * @readonly
   * @returns {number|undefined} The number of tokens, or undefined if not set.
   */
  get tokens() {
    return this.last_embed?.tokens;
  }
  /**
   * Sets the number of tokens for the embedding.
   * @param {number} tokens - The number of tokens.
   */
  set tokens(tokens) {
    this.last_embed.tokens = tokens;
  }
  /**
   * Gets the vector representation from the entity adapter.
   * @readonly
   * @returns {Array<number>|undefined} The vector or undefined if not set.
   */
  get vec() {
    return this.entity_adapter.vec;
  }
  /**
   * Sets the vector representation in the entity adapter.
   * @param {Array<number>} vec - The vector to set.
   */
  set vec(vec) {
    this.entity_adapter.vec = vec;
    this._queue_embed = false;
    this._embed_input = null;
    this.queue_save();
  }
  /**
   * Removes all embeddings from the entity.
   * @returns {void}
   */
  remove_embeddings() {
    this.data.embeddings = null;
    this.queue_save();
  }
  /**
   * Retrieves the key of the entity.
   * @returns {string} The entity key.
   */
  get_key() {
    return this.data.key || this.data.path;
  }
  /**
   * Retrieves the path of the entity.
   * @readonly
   * @returns {string|null} The entity path.
   */
  get path() {
    return this.data.path;
  }
  get is_unembedded() {
    if (!this.vec) return true;
    if (!this.embed_hash || this.embed_hash !== this.read_hash) return true;
    return false;
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-entities/smart_entities.js
var SmartEntities = class extends Collection {
  /**
   * Creates an instance of SmartEntities.
   * @constructor
   * @param {Object} env - The environment instance.
   * @param {Object} opts - Configuration options.
   */
  constructor(env, opts) {
    super(env, opts);
    this.entities_vector_adapter = new DefaultEntitiesVectorAdapter(this);
    this.model_instance_id = null;
    this._embed_queue = [];
  }
  /**
   * Unloads the smart embedding model.
   * @async
   * @returns {Promise<void>}
   */
  async unload() {
    if (typeof this.embed_model?.unload === "function") {
      this.embed_model.unload();
    }
    super.unload();
  }
  /**
   * Gets the key of the embedding model.
   * @readonly
   * @returns {string} The embedding model key.
   */
  get embed_model_key() {
    return this.embed_model?.model_key;
  }
  /**
   * Gets the embedding model instance.
   * @readonly
   * @returns {Object|null} The embedding model instance or null if none.
   */
  get embed_model() {
    if (this.env.embedding_models.default) {
      return this.env.embedding_models.default.instance;
    }
    throw new Error("DEPRECATED SMART ENVIRONMENT LOADED: UPDATE SMART PLUGINS.");
  }
  set embed_model(embed_model) {
    this.env._embed_model = embed_model;
  }
  /**
   * Gets the file name based on collection key and embedding model key.
   * @readonly
   * @deprecated likely unused (2025-09-29)
   * @returns {string} The constructed file name.
   */
  get file_name() {
    return this.collection_key + "-" + this.embed_model_key.split("/").pop();
  }
  /**
   * Looks up entities based on hypothetical content.
   * @deprecated moved to action (type=score) and retrieve using get_results() (pre-process generates hypothetical vecs) (2026-02-11)
   * @async
   * @param {Object} [params={}] - The parameters for the lookup.
   * @param {Array<string>} [params.hypotheticals=[]] - The hypothetical content to lookup.
   * @param {Object} [params.filter] - The filter to use for the lookup.
   * @param {number} [params.k] - Deprecated: Use `filter.limit` instead.
   * @returns {Promise<Array<Result>|Object>} The lookup results or an error object.
   */
  async lookup(params = {}) {
    const { hypotheticals = [] } = params;
    if (!hypotheticals?.length) return { error: "hypotheticals is required" };
    if (!this.embed_model) return { error: "Embedding search is not enabled." };
    const hyp_vecs = await this.embed_model.embed_batch(hypotheticals.map((h) => ({ embed_input: h })));
    const limit = params.filter?.limit || params.k || this.env.settings.lookup_k || 10;
    if (params.filter?.limit) delete params.filter.limit;
    const filter = {
      ...this.env.chats?.current?.scope || {},
      // DEPRECATED: since Smart Chat v1 (remove after removing legacy Smart Chat v0 from obsidian-smart-connections)
      ...params.filter || {}
    };
    const results = await hyp_vecs.reduce(async (acc_promise, embedding, i) => {
      const acc = await acc_promise;
      const results2 = await this.entities_vector_adapter.nearest(embedding.vec, filter);
      results2.forEach((result) => {
        if (!acc[result.item.path] || result.score > acc[result.item.path].score) {
          acc[result.item.path] = {
            key: result.item.key,
            score: result.score,
            item: result.item,
            hypothetical_i: i
          };
        } else {
          result.score = acc[result.item.path].score;
        }
      });
      return acc;
    }, Promise.resolve({}));
    const top_k = Object.values(results).sort(sort_by_score).slice(0, limit);
    console.log(`Found and returned ${top_k.length} ${this.collection_key}.`);
    return top_k;
  }
  /**
   * Gets the configuration for settings.
   * @readonly
   * @returns {Object} The settings configuration.
   */
  get settings_config() {
    return settings_config;
  }
  /**
   * Gets the notices from the environment.
   * @deprecated use event system with levels instead of notices (2026-03-17)
   * @readonly
   * @returns {Object} The notices object.
   */
  get notices() {
    return this.env.smart_connections_plugin?.notices || this.env.main?.notices;
  }
  /**
   * Gets the embed queue containing items to be embedded.
   * @readonly
   * @returns {Array<Object>} The embed queue.
   */
  get embed_queue() {
    if (!this._embed_queue?.length) {
      console.time(`Building embed queue`);
      this._embed_queue = Object.values(this.items).filter((item) => item._queue_embed || item.is_unembedded && item.should_embed);
      console.timeEnd(`Building embed queue`);
    }
    return this._embed_queue;
  }
  /**
   * Processes the embed queue by delegating to the default vector adapter.
   * @async
   * @returns {Promise<void>}
   */
  async process_embed_queue() {
    await this.entities_vector_adapter.process_embed_queue();
  }
  /**
   * @deprecated since v4 2025-11-28
   */
  get connections_filter_config() {
    return connections_filter_config;
  }
};
var settings_config = {
  "min_chars": {
    name: "Minimum length",
    type: "number",
    description: "Minimum length of entity to embed (in characters).",
    placeholder: "Enter number ex. 300",
    default: 300
  }
};
var connections_filter_config = {
  "smart_view_filter.show_full_path": {
    "name": "Show full path",
    "type": "toggle",
    "description": "Turning on will include the folder path in the connections results."
  },
  // "smart_view_filter.render_markdown": {
  //   "name": "Render markdown",
  //   "type": "toggle",
  //   "description": "Turn off to prevent rendering markdown and display connection results as plain text.",
  // },
  "smart_view_filter.results_limit": {
    "name": "Results limit",
    "type": "number",
    "description": "Adjust the number of connections displayed in the connections view (default 20).",
    "default": 20
  },
  "smart_view_filter.exclude_inlinks": {
    "name": "Exclude inlinks (backlinks)",
    "type": "toggle",
    "description": "Exclude notes that already link to the current note from the connections results."
  },
  "smart_view_filter.exclude_outlinks": {
    "name": "Exclude outlinks",
    "type": "toggle",
    "description": "Exclude notes that are already linked from within the current note from appearing in the connections results."
  },
  "smart_view_filter.include_filter": {
    "name": "Include filter",
    "type": "text",
    "description": "Notes must match this value in their file/folder path. Matching notes will be included in the connections results. Separate multiple values with commas."
  },
  "smart_view_filter.exclude_filter": {
    "name": "Exclude filter",
    "type": "text",
    "description": "Notes must *not* match this value in their file/folder path. Matching notes will be *excluded* from the connections results. Separate multiple values with commas."
  },
  // should be better scoped at source-level (leaving here for now since connections_filter_config needs larger refactor)
  "smart_view_filter.exclude_blocks_from_source_connections": {
    "name": "Hide blocks in results",
    "type": "toggle",
    "description": "Show only sources in the connections results (no blocks)."
  }
  // // hide frontmatter blocks from connections results
  // "smart_view_filter.exclude_frontmatter_blocks": {
  //   "name": "Hide frontmatter blocks in results",
  //   "type": "toggle",
  //   "description": "Show only sources in the connections results (no frontmatter blocks).",
  // },
};

// node_modules/obsidian-smart-env/node_modules/smart-entities/utils/frontmatter_filter.js
var to_string = (value) => `${value ?? ""}`.trim();
var to_lower = (value) => to_string(value).toLowerCase();
var get_frontmatter_value = (metadata = {}, key = "") => {
  const metadata_key = Object.keys(metadata || {}).find((candidate_key) => to_lower(candidate_key) === key);
  if (!metadata_key) return void 0;
  return metadata[metadata_key];
};
var matches_entry = (metadata = {}, entry) => {
  const metadata_value = get_frontmatter_value(metadata, entry.key);
  if (metadata_value == null) return false;
  if (entry.value == null) return true;
  if (Array.isArray(metadata_value)) {
    return metadata_value.some((value) => to_lower(value) === entry.value);
  }
  return to_lower(metadata_value) === entry.value;
};
function filter_by_frontmatter(metadata = {}, frontmatter_filter = {}) {
  const include = frontmatter_filter.include || [];
  const exclude = frontmatter_filter.exclude || [];
  if (exclude.length && exclude.some((entry) => matches_entry(metadata, entry))) {
    return false;
  }
  if (!include.length) return true;
  return include.some((entry) => matches_entry(metadata, entry));
}

// node_modules/obsidian-smart-env/node_modules/smart-sources/actions/find_connections.js
var SOURCE_CONNECTIONS_CACHE = {};
function connections_from_cache2(cache_key) {
  return SOURCE_CONNECTIONS_CACHE[cache_key];
}
function connections_to_cache2(cache_key, connections) {
  SOURCE_CONNECTIONS_CACHE[cache_key] = connections;
}
async function find_connections2(params = {}) {
  const filter_settings = this.env.settings.smart_view_filter;
  const exclude_blocks_from_source_connections = params.exclude_blocks_from_source_connections ?? filter_settings?.exclude_blocks_from_source_connections ?? false;
  const limit = params.filter?.limit || params.limit || this.env.settings.smart_view_filter?.results_limit || 20;
  let connections;
  if (this.block_collection.settings.embed_blocks && !exclude_blocks_from_source_connections) connections = [];
  else connections = await find_connections.call(this, params);
  const filter_opts = create_find_connections_filter_opts(this, params);
  if (params.filter?.limit) delete params.filter.limit;
  if (params.limit) delete params.limit;
  if (!exclude_blocks_from_source_connections) {
    const cache_key = this.key + murmur_hash_32_alphanumeric(JSON.stringify({ ...filter_opts, entity: null })) + "_blocks";
    if (!SOURCE_CONNECTIONS_CACHE[cache_key]) {
      const nearest = (await this.env.smart_blocks.entities_vector_adapter.nearest(this.vec, filter_opts)).sort(sort_by_score).slice(0, limit);
      connections_to_cache2(cache_key, nearest);
    }
    connections = [
      ...connections,
      ...connections_from_cache2(cache_key)
    ].sort(sort_by_score).slice(0, limit);
  }
  return connections;
}
find_connections2.action_type = "connections";

// node_modules/obsidian-smart-env/node_modules/smart-sources/smart_source.js
var SmartSource = class extends SmartEntity {
  /**
   * Provides default values for a SmartSource instance.
   * @static
   * @readonly
   * @returns {Object} The default values.
   */
  static get defaults() {
    return {
      data: {
        last_read: {
          hash: null,
          mtime: 0
        },
        embeddings: {}
      },
      _embed_input: null,
      // Stored temporarily
      _queue_load: true
    };
  }
  /**
   * Initializes the SmartSource instance by queuing an import if blocks are missing.
   * @returns {void}
   */
  init() {
    super.init();
    if (!this.data.blocks) this.queue_import();
  }
  /**
   * Queues the SmartSource for import.
   * @returns {void}
   */
  queue_import() {
    this._queue_import = true;
  }
  /**
   * Imports the SmartSource by checking for updates and parsing content.
   * @async
   * @returns {Promise<void>}
   */
  async import() {
    this._queue_import = false;
    try {
      await this.source_adapter?.import();
      this.emit_event("sources:imported");
    } catch (err) {
      if (err.code === "ENOENT") {
        console.log(`Smart Connections: Deleting ${this.path} data because it no longer exists on disk`);
        this.delete();
      } else {
        console.warn("Smart Connections: Error during import: re-queueing import", err);
        this.queue_import();
      }
    }
  }
  /**
   * @deprecated likely extraneous
   */
  async parse_content(content = null) {
    const parse_fns = this.env?.opts?.collections?.smart_sources?.content_parsers || [];
    for (const fn of parse_fns) {
      await fn(this, content);
    }
    if (this.data.last_import?.hash === this.data.last_read?.hash) {
      if (this.data.blocks) return;
    }
  }
  /**
   * Finds connections relevant to this SmartSource based on provided parameters.
   * @async
   * @deprecated use ConnectionsLists
   * @param {Object} [params={}] - Parameters for finding connections.
   * @param {boolean} [params.exclude_blocks_from_source_connections=false] - Whether to exclude block connections from source connections.
   * @param {Object} [params.exclude_frontmatter_blocks=true] - Whether to exclude frontmatter blocks from source connections.
   * @returns {Array<SmartSource>} An array of relevant SmartSource entities.
   */
  async find_connections(params = {}) {
    return await this.actions.find_connections(params);
  }
  /**
   * Prepares the embed input for the SmartSource by reading content and applying exclusions.
   * @async
   * @returns {Promise<string|false>} The embed input string or `false` if already embedded.
   */
  async get_embed_input(content = null) {
    if (typeof this._embed_input === "string" && this._embed_input.length) return this._embed_input;
    if (!content) content = await this.read();
    if (!content) {
      console.warn("SmartSource.get_embed_input: No content available for embedding: " + this.path);
      return "";
    }
    if (this.excluded_lines.length) {
      const content_lines = content.split("\n");
      this.excluded_lines.forEach((lines) => {
        const { start, end } = lines;
        for (let i = start; i <= end; i++) {
          content_lines[i] = "";
        }
      });
      content = content_lines.filter((line) => line.length).join("\n");
    }
    const breadcrumbs = this.path.split("/").join(" > ").replace(".md", "");
    const max_tokens = this.collection.embed_model.model.data.max_tokens || 500;
    const max_chars = Math.floor(max_tokens * 3.7);
    this._embed_input = `${breadcrumbs}:
${content}`.substring(0, max_chars);
    return this._embed_input;
  }
  /**
   * Opens the SmartSource note in the SmartConnections plugin.
   * @returns {void}
   */
  open() {
    this.env.smart_connections_plugin.open_note(this.path);
  }
  /**
   * Retrieves the block associated with a specific line number.
   * @param {number} line - The line number to search for.
   * @returns {SmartBlock|null} The corresponding SmartBlock or `null` if not found.
   */
  get_block_by_line(line) {
    return Object.entries(this.data.blocks || {}).reduce((acc, [sub_key, range]) => {
      if (acc) return acc;
      if (range[0] <= line && range[1] >= line) {
        const block = this.block_collection.get(this.key + sub_key);
        if (block?.vec) return block;
      }
      return acc;
    }, null);
  }
  /**
   * Checks if the source file exists in the file system.
   * @async
   * @returns {Promise<boolean>} A promise that resolves to `true` if the file exists, `false` otherwise.
   */
  async has_source_file() {
    return await this.fs.exists(this.path);
  }
  // CRUD
  /**
   * FILTER/SEARCH METHODS
   */
  /**
   * Searches for keywords within the entity's data and content.
   * @async
   * @param {Object} search_filter - The search filter object.
   * @param {string[]} search_filter.keywords - An array of keywords to search for.
   * @param {string} [search_filter.type='any'] - The type of search to perform. 'any' counts all matching keywords, 'all' counts only if all keywords match.
   * @returns {Promise<number>} A promise that resolves to the number of matching keywords.
   */
  async search(search_filter = {}) {
    const { keywords, type = "any", limit } = search_filter;
    if (!keywords || !Array.isArray(keywords)) {
      console.warn("Entity.search: keywords not set or is not an array");
      return 0;
    }
    if (limit && this.collection.search_results_ct >= limit) return 0;
    const lowercased_keywords = keywords.map((keyword) => keyword.toLowerCase());
    const content = await this.read();
    if (!content || typeof content !== "string" || !content.length) {
      if (content.mime_type) {
        console.warn(`Entity.search: No content available for searching: ${this.path}, mime_type: ${content.mime_type}`);
      } else {
        console.warn(`Entity.search: No content available for searching: ${this.path}, content: ${content ? JSON.stringify(content) : "empty"}`);
      }
      return 0;
    }
    const lowercased_content = content.toLowerCase();
    const lowercased_path = this.path.toLowerCase();
    const matching_keywords = lowercased_keywords.filter(
      (keyword) => lowercased_path.includes(keyword) || lowercased_content.includes(keyword)
    );
    if (type === "all") {
      return matching_keywords.length === lowercased_keywords.length ? matching_keywords.length : 0;
    } else {
      return matching_keywords.length;
    }
  }
  /**
   * Filters source using base key filters and optional frontmatter include/exclude filters.
   * @param {Object} [filter_opts={}]
   * @param {Object} [filter_opts.frontmatter]
   * @returns {boolean}
   */
  filter(filter_opts = {}) {
    if (!super.filter(filter_opts)) return false;
    if (!filter_opts.frontmatter) return true;
    return filter_by_frontmatter(this.metadata || {}, filter_opts.frontmatter);
  }
  /**
   * ADAPTER METHODS
   */
  use_source_adapter(method, ...args) {
    if (!this.source_adapter) {
      console.warn(`No source adapter available for ${this.key}. Cannot use method ${method}.`);
      return;
    }
    if (typeof this.source_adapter[method] !== "function") {
      console.warn(`Source adapter for ${this.key} does not implement method ${method}.`);
      return;
    }
    return this.source_adapter[method](...args);
  }
  /**
   * Appends content to the end of the source file.
   * @async
   * @param {string} content - The content to append to the file.
   * @returns {Promise<void>} A promise that resolves when the operation is complete.
   */
  async append(content) {
    await this.use_source_adapter("append", content);
    await this.import();
  }
  /**
   * Updates the entire content of the source file.
   * @async
   * @param {string} full_content - The new content to write to the file.
   * @param {Object} [opts={}] - Additional options for the update.
   * @returns {Promise<void>} A promise that resolves when the operation is complete.
   */
  async update(full_content, opts = {}) {
    try {
      await this.use_source_adapter("update", full_content, opts);
      await this.import();
    } catch (error) {
      console.error(`Error during update for ${this.key}:`, error);
    }
  }
  /**
   * Reads the entire content of the source file.
   * @async
   * @param {Object} [opts={}] - Additional options for reading.
   * @returns {Promise<string>} A promise that resolves with the content of the file.
   */
  async read(opts = {}) {
    try {
      return await this.use_source_adapter("read", opts) || "";
    } catch (error) {
      console.error(`Error during reading ${this.key} (returning empty string)`, error);
      return "";
    }
  }
  /**
   * Removes the source file from the file system and deletes the entity.
   * This is different from `delete()` because it also removes the source file.
   * @async
   * @returns {Promise<void>} A promise that resolves when the operation is complete.
   */
  async remove() {
    try {
      await this.use_source_adapter("remove");
    } catch (error) {
      console.error(`Error during remove for ${this.key}:`, error);
    }
  }
  /**
   * Moves the current source to a new location.
   * Handles the destination as a string (new path) or entity (block or source).
   *
   * @async
   * @param {string|SmartEntity} entity_ref - The destination path or entity to move to.
   * @throws {Error} If the entity reference is invalid.
   * @returns {Promise<void>} A promise that resolves when the move operation is complete.
   */
  async move_to(entity_ref) {
    try {
      await this.use_source_adapter("move_to", entity_ref);
    } catch (error) {
      console.error(`Error during move for ${this.key}:`, error);
    }
  }
  /**
   * Merges the given content into the current source.
   * Parses the content into blocks and either appends to existing blocks, replaces blocks, or replaces all content.
   *
   * @async
   * @param {string} content - The content to merge into the current source.
   * @param {Object} [opts={}] - Options object.
   * @param {string} [opts.mode='append'] - The merge mode: 'append', 'replace_blocks', or 'replace_all'.
   * @returns {Promise<void>}
   */
  async merge(content, opts = {}) {
    try {
      await this.use_source_adapter("merge", content, opts);
      await this.import();
    } catch (error) {
      console.error(`Error during merge for ${this.key}:`, error);
    }
  }
  /**
   * Handles errors during the load process.
   * @param {Error} err - The error encountered during load.
   * @returns {void}
   */
  on_load_error(err) {
    super.on_load_error(err);
    if (err.code === "ENOENT") {
      this._queue_load = false;
      this.queue_import();
    }
  }
  // GETTERS
  /**
   * Retrieves the block collection associated with SmartSources.
   * @readonly
   * @returns {SmartBlocks} The block collection instance.
   */
  get block_collection() {
    return this.env.smart_blocks;
  }
  /**
   * Retrieves the vector representations of all blocks within the SmartSource.
   * @readonly
   * @returns {Array<Array<number>>} An array of vectors.
   */
  get block_vecs() {
    return this.blocks.map((block) => block.vec).filter((vec) => vec);
  }
  /**
   * Retrieves all blocks associated with the SmartSource.
   * @readonly
   * @returns {Array<SmartBlock>} An array of SmartBlock instances.
   * @description
   * Uses block refs (Fastest) to get blocks without iterating over all blocks
   */
  get blocks() {
    if (this.data.blocks) return this.block_collection.get_many(Object.keys(this.data.blocks).map((key) => this.key + key));
    return [];
  }
  /**
   * Determines if the SmartSource is excluded from processing.
   * @readonly
   * @returns {boolean} `true` if excluded, `false` otherwise.
   */
  get excluded() {
    return this.fs.is_excluded(this.path);
  }
  /**
   * Retrieves the lines excluded from embedding.
   * @readonly
   * @returns {Array<Object>} An array of objects with `start` and `end` line numbers.
   */
  get excluded_lines() {
    return this.blocks.filter((block) => block.excluded).map((block) => block.lines);
  }
  /**
   * Retrieves the file system instance from the SmartSource's collection.
   * @readonly
   * @returns {SmartFS} The file system instance.
   */
  get fs() {
    return this.collection.fs;
  }
  /**
   * Retrieves the file object associated with the SmartSource.
   * @deprecated should be replaced with adapter methods
   * @readonly
   * @returns {Object} The file object.
   */
  get file() {
    return this.fs.files[this.path];
  }
  /**
   * Retrieves the file name of the SmartSource.
   * @readonly
   * @returns {string} The file name.
   */
  get file_name() {
    return this.path.split("/").pop();
  }
  /**
   * Retrieves the file path of the SmartSource.
   * @readonly
   * @returns {string} The file path.
   */
  get file_path() {
    return this.path;
  }
  /**
   * Retrieves the file type based on the file extension.
   * @readonly
   * @returns {string} The file type in lowercase.
   */
  get file_type() {
    if (!this._ext) {
      this._ext = this.collection.get_extension_for_path(this.path) || "md";
    }
    return this._ext;
  }
  /**
   * Retrieves the modification time of the SmartSource.
   * @deprecated should be replaced with adapter methods (see get size)
   * @readonly
   * @returns {number} The modification time.
   */
  get mtime() {
    return this.file?.stat?.mtime || 0;
  }
  /**
   * Retrieves the size of the SmartSource.
   * @readonly
   * @returns {number} The size.
   */
  get size() {
    return this.source_adapter?.size || 0;
  }
  /**
   * Retrieves the last import stat of the SmartSource.
   * @readonly
   * @returns {Object} The last import stat.
   */
  get last_import() {
    return this.data?.last_import;
  }
  /**
   * Retrieves the last import modification time of the SmartSource.
   * @readonly
   * @returns {number} The last import modification time.
   */
  get last_import_mtime() {
    return this.last_import?.mtime || 0;
  }
  /**
   * Retrieves the last import size of the SmartSource.
   * @readonly
   * @returns {number} The last import size.
   */
  get last_import_size() {
    return this.last_import?.size || 0;
  }
  /**
   * Retrieves the paths of inlinks to this SmartSource.
   * @readonly
   * @returns {Array<LinkObject>} An array of inlink paths.
   */
  get inlinks() {
    return Object.entries(this.collection.links?.[this.key] || {}).map(([link_source_key, link_data]) => {
      return {
        source_key: link_source_key,
        ...link_data
      };
    });
  }
  get is_media() {
    return this.source_adapter.is_media || false;
  }
  /**
   * Determines if the SmartSource is gone (i.e., the file no longer exists).
   * @readonly
   * @returns {boolean} `true` if gone, `false` otherwise.
   */
  get is_gone() {
    return !this.file;
  }
  /**
   * Retrieves the last read hash of the SmartSource.
   * @readonly
   * @returns {string|undefined} The last read hash or `undefined` if not set.
   */
  get last_read() {
    return this.data.last_read;
  }
  get metadata() {
    return this.data.metadata;
  }
  get outdated() {
    return this.source_adapter.outdated;
  }
  /**
   * Retrieves the outlink paths from the SmartSource.
   * @readonly
   * @returns {Array<import('smart-types').LinkObject>} An array of outlink objects.
   */
  get outlinks() {
    return (this.data.outlinks || []).map((link) => {
      const link_ref = link?.target || link;
      if (typeof link_ref !== "string") return null;
      if (link_ref.startsWith("http")) return null;
      const link_path = this.fs.get_link_target_path(link_ref, this.file_path);
      return {
        ...link,
        key: link_path,
        embedded: link.embedded || false,
        source_key: this.key
      };
    }).filter((link_path) => link_path);
  }
  /**
   * @deprecated path should be derived from key (stable key principle)
   */
  get path() {
    return this.data.path || this.data.key;
  }
  get source_adapters() {
    return this.collection.source_adapters;
  }
  get source_adapter() {
    if (this._source_adapter) return this._source_adapter;
    if (this.source_adapters[this.file_type]) this._source_adapter = new this.source_adapters[this.file_type](this);
    else {
      for (const Adapter of Object.values(this.source_adapters)) {
        if (typeof Adapter.detect_type !== "function") continue;
        if (Adapter.detect_type(this)) {
          this._source_adapter = new Adapter(this);
          break;
        }
      }
    }
    return this._source_adapter;
  }
  // COMPONENTS
  /**
   * Calculates the mean vector of all blocks within the SmartSource.
   * @readonly
   * @returns {Array<number>|null} The mean vector or `null` if no vectors are present.
   */
  get mean_block_vec() {
    if (this._mean_block_vec) {
      this._mean_block_vec = compute_centroid(this.block_vecs);
    }
    return this._mean_block_vec;
  }
  /**
   * Calculates the median vector of all blocks within the SmartSource.
   * @readonly
   * @returns {Array<number>|null} The median vector or `null` if no vectors are present.
   */
  get median_block_vec() {
    if (this._median_block_vec) {
      this._median_block_vec = compute_medoid(this.block_vecs);
    }
    return this._median_block_vec;
  }
  // DEPRECATED methods
  /**
   * @async
   * @deprecated Use `read` instead.
   * @returns {Promise<string>} A promise that resolves with the content of the file.
   */
  async _read() {
    return await this.source_adapter._read();
  }
  /**
   * @async
   * @deprecated Use `remove` instead.
   * @returns {Promise<void>} A promise that resolves when the entity is destroyed.
   */
  async destroy() {
    await this.remove();
  }
  /**
   * @async
   * @deprecated Use `update` instead.
   * @param {string} content - The content to update.
   * @returns {Promise<void>}
   */
  async _update(content) {
    await this.source_adapter.update(content);
  }
  /**
   * @deprecated Use `source` instead.
   * @readonly
   * @returns {SmartSource} The associated SmartSource instance.
   */
  get t_file() {
    return this.fs.files[this.path];
  }
};
var smart_source_default = {
  class: SmartSource,
  actions: {
    find_connections: find_connections2
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-sources/smart_sources.js
var SmartSources = class extends SmartEntities {
  /**
   * Creates an instance of SmartSources.
   * @constructor
   * @param {Object} env - The environment instance.
   * @param {Object} [opts={}] - Configuration options.
   */
  constructor(env, opts = {}) {
    super(env, opts);
    this.search_results_ct = 0;
    this._excluded_headings = null;
    this.env_event_unsubscribers = [];
    this.sources_re_import_queue = {};
    this.sources_re_import_timeout = null;
    this.sources_re_import_halted = false;
    this.import_progress_state = null;
  }
  /**
   * Initializes the SmartSources instance by performing an initial scan of sources.
   * @async
   * @returns {Promise<void>}
   */
  async init() {
    await super.init();
    await this.init_items();
    this.register_env_event_listeners();
    this.register_source_watchers();
  }
  /**
   * Registers env.events listeners for source lifecycle events emitted by filesystem adapters.
   * @returns {void}
   */
  register_env_event_listeners() {
    this.unregister_env_event_listeners();
    if (!this.env?.events) return;
    const listeners = [
      ["sources:created", (event) => this.handle_source_created(event)],
      ["sources:modified", (event) => this.handle_source_modified(event)],
      ["sources:renamed", (event) => this.handle_source_renamed(event)],
      ["sources:deleted", (event) => this.handle_source_deleted(event)]
    ];
    this.env_event_unsubscribers = listeners.map(([event_key, handler]) => this.env.events.on(event_key, handler)).filter(Boolean);
  }
  /**
   * Unregisters env.events listeners that were previously attached by this collection.
   * @returns {void}
   */
  unregister_env_event_listeners() {
    if (!Array.isArray(this.env_event_unsubscribers)) return;
    while (this.env_event_unsubscribers.length) {
      const unsub = this.env_event_unsubscribers.pop();
      try {
        unsub?.();
      } catch (error) {
        console.warn("SmartSources: Failed to unregister env event listener", error);
      }
    }
  }
  /**
   * Determines whether the incoming event should be handled by this collection instance.
   * @param {Object} event
   * @returns {boolean}
   */
  should_handle_event(event = {}) {
    const { collection_key } = event;
    if (collection_key && collection_key !== this.collection_key) return false;
    return true;
  }
  /**
   * Normalizes event payload keys into a canonical source path.
   * @param {Object} event
   * @returns {string|undefined}
   */
  get_event_path(event = {}) {
    return event.item_key || event.path || event.new_path;
  }
  /**
   * Handles create events emitted by filesystem adapters.
   * @param {Object} event
   * @returns {void}
   */
  handle_source_created(event = {}) {
    if (!this.should_handle_event(event)) return;
    const key = this.get_event_path(event);
    if (!key) return;
    const source = this.init_file_path(key) || this.get(key);
    if (!source) {
      console.warn("SmartSources: Unable to initialize source on create event", event);
      return;
    }
    this.queue_source_re_import(source, { event_source: event.event_source });
  }
  /**
   * Handles modify events emitted by filesystem adapters.
   * @param {Object} event
   * @returns {void}
   */
  handle_source_modified(event = {}) {
    if (!this.should_handle_event(event)) return;
    const key = this.get_event_path(event);
    if (!key) return;
    if (this.fs.is_excluded(key)) return;
    let source = this.get(key);
    if (!source) source = this.init_file_path(key);
    if (!source) {
      console.warn("SmartSources: Unable to resolve source on modify event", { key, event });
      return;
    }
    this.queue_source_re_import(source, { event_source: event.event_source });
  }
  /**
   * Handles rename events emitted by filesystem adapters.
   * @param {Object} event
   * @returns {void}
   */
  handle_source_renamed(event = {}) {
    if (!this.should_handle_event(event)) return;
    const new_key = this.get_event_path(event);
    const old_key = event.old_path || event.from;
    if (!new_key && !old_key) return;
    if (old_key && this.items[old_key]) {
      const old_source = this.items[old_key];
      old_source?.delete?.();
      delete this.items[old_key];
      if (this.rename_debounce_timeout) clearTimeout(this.rename_debounce_timeout);
      this.rename_debounce_timeout = setTimeout(() => {
        this.process_save_queue();
        this.rename_debounce_timeout = null;
      }, 1e3);
    }
    if (!new_key) return;
    let source = this.get(new_key);
    if (!source) source = this.init_file_path(new_key);
    if (!source) {
      console.warn("SmartSources: Unable to initialize source on rename event", event);
      return;
    }
    this.queue_source_re_import(source, { event_source: event.event_source });
  }
  /**
   * Handles delete events emitted by filesystem adapters.
   * @param {Object} event
   * @returns {void}
   */
  handle_source_deleted(event = {}) {
    if (!this.should_handle_event(event)) return;
    const key = this.get_event_path(event);
    if (!key) return;
    delete this.items[key];
    if (this.sources_re_import_queue[key]) {
      delete this.sources_re_import_queue[key];
    }
  }
  /**
   * Requests filesystem adapters to register source watchers for this collection.
   * @returns {void}
   */
  register_source_watchers() {
    const adapter = this.fs?.adapter;
    if (!adapter || typeof adapter.register_source_watchers !== "function") return;
    if (this._source_watchers_registered) return;
    this._source_watchers_registered = adapter.register_source_watchers(this);
  }
  /**
   * Queues a SmartSource for re-import and schedules processing.
   * @param {import('./smart_source.js').SmartSource} source
   * @param {Object} [event_meta]
   * @returns {void}
   */
  queue_source_re_import(source, event_meta = {}) {
    if (!source?.key) return;
    if (this.sources_re_import_queue[source.key]) return;
    source.data.last_import = { at: 0, hash: null, mtime: 0, size: 0 };
    this.sources_re_import_queue[source.key] = { source, event_meta };
    this.debounce_re_import_queue();
  }
  /**
   * Debounces re-import processing to respect the configured wait time.
   * @returns {void}
   */
  debounce_re_import_queue() {
    this.sources_re_import_halted = true;
    if (this.sources_re_import_timeout) clearTimeout(this.sources_re_import_timeout);
    const queue_keys = Object.keys(this.sources_re_import_queue || {});
    if (!queue_keys.length) {
      this.sources_re_import_timeout = null;
      return;
    }
    const wait_seconds = typeof this.env?.settings?.re_import_wait_time === "number" ? this.env.settings.re_import_wait_time : 13;
    this.sources_re_import_timeout = setTimeout(
      () => this.run_re_import(),
      wait_seconds * 1e3
    );
  }
  /**
   * @returns {object|null}
   */
  get_import_progress_state() {
    return this.import_progress_state ? { ...this.import_progress_state } : null;
  }
  /**
   * @param {object|null} next_state
   * @returns {void}
   */
  set_import_progress_state(next_state = null) {
    this.import_progress_state = next_state ? {
      ...next_state,
      updated_at: Date.now()
    } : null;
  }
  /**
   * Processes the queued re-import tasks.
   * @returns {Promise<void>}
   */
  async run_re_import() {
    this.sources_re_import_halted = false;
    const queue_entries = Object.entries(this.sources_re_import_queue || {});
    if (!queue_entries.length) {
      if (this.sources_re_import_timeout) clearTimeout(this.sources_re_import_timeout);
      this.sources_re_import_timeout = null;
      this.set_import_progress_state(null);
      return;
    }
    this.set_import_progress_state({
      active: true,
      stage: "reimporting",
      progress: 0,
      total: queue_entries.length
    });
    this.emit_event("sources:reimport_started", {
      progress: 0,
      total: queue_entries.length,
      event_source: "run_re_import"
    });
    let completed_count = 0;
    for (let index = 0; index < queue_entries.length; index += 100) {
      const batch = queue_entries.slice(index, index + 100);
      await Promise.all(batch.map(([, { source }]) => source.import()));
      for (const [key, { source }] of batch) {
        if (!this._embed_queue) this._embed_queue = [];
        if (source.should_embed) this._embed_queue.push(source);
        if (this.block_collection?.settings?.embed_blocks) {
          for (const block of source.blocks || []) {
            if (block._queue_embed || block.should_embed && block.is_unembedded) {
              this._embed_queue.push(block);
              block._queue_embed = true;
            }
          }
        }
        delete this.sources_re_import_queue[key];
      }
      completed_count += batch.length;
      this.set_import_progress_state({
        active: true,
        stage: "reimporting",
        progress: completed_count,
        total: queue_entries.length
      });
      this.emit_event("sources:reimport_progress", {
        progress: completed_count,
        total: queue_entries.length,
        event_source: "run_re_import"
      });
      if (this.sources_re_import_halted) {
        this.debounce_re_import_queue();
        break;
      }
    }
    this.set_import_progress_state(null);
    if (this._embed_queue?.length) {
      const embed_start_at = Date.now();
      await this.process_embed_queue();
      console.log(`Processed embed queue in ${Date.now() - embed_start_at}ms`);
    }
    if (!this.sources_re_import_halted) {
      this.emit_event("sources:reimport_completed", {
        count: completed_count,
        total: queue_entries.length,
        event_source: "run_re_import"
      });
    }
    if (this.sources_re_import_timeout) clearTimeout(this.sources_re_import_timeout);
    this.sources_re_import_timeout = null;
  }
  /**
   * Initializes items by letting each adapter do any necessary file-based scanning.
   * @async
   * @returns {Promise<void>}
   */
  async init_items() {
    this.emit_event("source:initial_scan_started", {
      event_source: "init_items"
    });
    for (const AdapterClass of Object.values(this.source_adapters)) {
      if (typeof AdapterClass.init_items === "function") {
        await AdapterClass.init_items(this);
      }
    }
    this.emit_event("source:initial_scan_completed", {
      event_source: "init_items"
    });
  }
  /**
   * Creates (or returns existing) a SmartSource for a given file path, if the extension is recognized.
   * @param {string} file_path - The path to the file or pseudo-file
   * @returns {SmartSource|undefined} The newly created or existing SmartSource, or undefined if no recognized extension
   */
  init_file_path(file_path) {
    const ext = this.get_extension_for_path(file_path);
    if (!ext) {
      return;
    }
    if (this.fs.is_excluded(file_path)) {
      console.warn(`File ${file_path} is excluded from processing.`);
      return;
    }
    if (!this.fs.files[file_path]) {
      this.fs.include_file(file_path);
    }
    if (this.items[file_path]) return this.items[file_path];
    const item = new this.item_type(this.env, { path: file_path });
    this.items[file_path] = item;
    item.queue_import();
    item.queue_load();
    return item;
  }
  /**
   * Looks for an extension in descending order:
   * e.g. split "my.file.name.github" -> ["my","file","name","github"]
   * Try 'file.name.github', 'name.github', 'github'
   * Return the first that is in 'source_adapters'
   * @param {string} file_path
   * @returns {string|undefined} recognized extension, or undefined if none
   */
  get_extension_for_path(file_path) {
    if (!file_path) return void 0;
    const pcs = file_path.split(".");
    if (pcs.length < 2) return void 0;
    let last_ext;
    pcs.shift();
    while (pcs.length) {
      const supported_ext = pcs.join(".").toLowerCase();
      if (this.source_adapters[supported_ext]) {
        return supported_ext;
      }
      last_ext = pcs.shift();
    }
    return last_ext;
  }
  /**
   * Builds a map of links between sources.
   * @returns {Object} An object mapping link paths to source keys.
   */
  build_links_map() {
    const start_time = Date.now();
    this.links = {};
    for (const source of Object.values(this.items)) {
      for (const link of source.outlinks) {
        if (!this.links[link.key]) this.links[link.key] = {};
        this.links[link.key][source.key] = { ...link };
      }
    }
    const end_time = Date.now();
    console.log(`Time spent building links: ${end_time - start_time}ms`);
    return this.links;
  }
  /**
   * Creates a new source with the given key and content.
   * @async
   * @param {string} key - The key (path) of the new source.
   * @param {string} content - The content to write to the new source.
   * @returns {Promise<SmartSource>} The created SmartSource instance.
   */
  async create(key, content) {
    await this.fs.write(key, content);
    await this.fs.refresh();
    const source = await this.create_or_update({ path: key });
    await source.import();
    return source;
  }
  /**
   * Performs a lexical search for matching SmartSource content.
   * @async
   * @deprecated uses this.actions 2025-12-02
   * @param {Object} search_filter - The filter criteria for the search.
   * @param {string[]} search_filter.keywords - An array of keywords to search for.
   * @param {number} [search_filter.limit] - The maximum number of results to return.
   * @returns {Promise<Array<SmartSource>>} A promise that resolves to an array of matching SmartSource entities.
   */
  async search(search_filter = {}) {
    const {
      keywords,
      limit,
      ...filter_opts
    } = search_filter;
    if (!keywords) {
      console.warn("search_filter.keywords not set");
      return [];
    }
    this.search_results_ct = 0;
    const initial_results = this.filter(filter_opts);
    const search_results = [];
    for (let index = 0; index < initial_results.length; index += 10) {
      const batch = initial_results.slice(index, index + 10);
      const batch_results = await Promise.all(
        batch.map(async (item) => {
          try {
            const matches = await item.search(search_filter);
            if (matches) {
              this.search_results_ct++;
              return { item, score: matches };
            }
            return null;
          } catch (error) {
            console.error(`Error searching item ${item.id || "unknown"}:`, error);
            return null;
          }
        })
      );
      search_results.push(...batch_results.filter(Boolean));
    }
    return search_results.sort((a, b) => b.score - a.score).map((result) => result.item);
  }
  /**
   * Looks up entities based on the provided parameters.
   * @async
   * @deprecated uses this.actions 2025-12-02
   * @param {Object} [params={}] - Parameters for the lookup.
   * @param {Object} [params.filter] - Filter options.
   * @param {number} [params.k] - Deprecated. Use `params.filter.limit` instead.
   * @returns {Promise<Array<SmartSource>>} A promise that resolves to an array of matching SmartSource entities.
   */
  async lookup(params = {}) {
    const limit = params.filter?.limit || params.k || this.env.settings.lookup_k || 10;
    if (params.filter?.limit) delete params.filter.limit;
    if (params.collection) {
      const collection = this.env[params.collection];
      if (collection && collection.lookup) {
        delete params.collection;
        params.skip_blocks = true;
        const results2 = await collection.lookup(params);
        if (results2.error) {
          console.warn(results2.error);
          return [];
        }
        return results2.slice(0, limit);
      }
    }
    let results = await super.lookup(params);
    if (results.error) {
      console.warn(results.error);
      return [];
    }
    if (this.block_collection?.settings?.embed_blocks && !params.skip_blocks) {
      results = [
        ...results,
        ...await this.block_collection.lookup(params)
      ].sort(sort_by_score);
    }
    return results.slice(0, limit);
  }
  /**
   * Processes the load queue by loading items and optionally importing them.
   * @async
   * @returns {Promise<void>}
   */
  async process_load_queue() {
    await super.process_load_queue();
    if (this.collection_key === "smart_sources" && this.env.smart_blocks) {
      Object.values(this.env.smart_blocks.items).forEach((item) => item.init());
    }
    if (this.block_collection) {
      this.block_collection.loaded = Object.keys(this.block_collection.items).length;
    }
    if (!this.opts.prevent_import_on_load) {
      await this.process_source_import_queue(this.opts);
    }
    this.build_links_map();
    this.block_collection.cleanup_blocks();
  }
  /**
   * @method process_source_import_queue
   * @description
   * Imports items (SmartSources or SmartBlocks) that have been flagged for import.
   */
  async process_source_import_queue(opts = {}) {
    const { process_embed_queue = true, force = false } = opts;
    if (force) Object.values(this.items).forEach((item) => item._queue_import = true);
    const import_queue = Object.values(this.items).filter((item) => item._queue_import);
    console.log("import_queue " + import_queue.length);
    if (!import_queue.length) {
      this.set_import_progress_state(null);
      this.emit_event("sources:import_queue_empty", {
        event_source: "process_source_import_queue"
      });
      return;
    }
    const time_start = Date.now();
    this.set_import_progress_state({
      active: true,
      stage: "importing",
      progress: 0,
      total: import_queue.length
    });
    this.emit_event("sources:import_started", {
      progress: 0,
      total: import_queue.length,
      event_source: "process_source_import_queue"
    });
    for (let index = 0; index < import_queue.length; index += 100) {
      const batch = import_queue.slice(index, index + 100);
      await Promise.all(batch.map((item) => item.import()));
      const progress = Math.min(import_queue.length, index + batch.length);
      this.set_import_progress_state({
        active: true,
        stage: "importing",
        progress,
        total: import_queue.length
      });
      this.emit_event("sources:import_progress", {
        progress,
        total: import_queue.length,
        event_source: "process_source_import_queue"
      });
    }
    this.set_import_progress_state(null);
    this.build_links_map();
    if (process_embed_queue) await this.process_embed_queue();
    else console.log("skipping process_embed_queue");
    await this.process_save_queue();
    await this.block_collection?.process_save_queue();
    this.emit_event("sources:import_completed", {
      count: import_queue.length,
      time_in_seconds: (Date.now() - time_start) / 1e3,
      event_source: "process_source_import_queue"
    });
  }
  /**
   * Retrieves the source adapters based on the collection configuration.
   * @readonly
   * @returns {Object} An object mapping file extensions to adapter constructors.
   */
  get source_adapters() {
    if (!this._source_adapters) {
      const source_adapters = Object.entries(this.env.opts.collections?.[this.collection_key]?.source_adapters || {});
      const _source_adapters = source_adapters.reduce((acc, [key, Adapter]) => {
        if (Adapter.extensions) Adapter.extensions?.forEach((ext) => acc[ext] = Adapter);
        else if (typeof Adapter.detect_type === "function") acc[key] = Adapter;
        return acc;
      }, {});
      if (Object.keys(_source_adapters).length) {
        this._source_adapters = _source_adapters;
      }
    }
    return this._source_adapters;
  }
  /**
   * Retrieves the notices system from the environment.
   * @readonly
   * @returns {Object} The notices object.
   */
  get notices() {
    return this.env.smart_connections_plugin?.notices || this.env.main?.notices;
  }
  /**
   * Retrieves the currently active note.
   * @readonly
   * @returns {SmartSource|null} The current SmartSource instance or null if none.
   */
  get current_note() {
    return this.get(this.env.smart_connections_plugin.app.workspace.getActiveFile().path);
  }
  /**
   * Retrieves the file system instance, initializing it if necessary.
   * @readonly
   * @returns {SmartFS} The file system instance.
   */
  get fs() {
    if (!this._fs) {
      this._fs = new this.env.opts.modules.smart_fs.class(this.env, {
        adapter: this.env.opts.modules.smart_fs.adapter,
        fs_path: this.env.opts.env_path || "",
        exclude_patterns: this.excluded_patterns || []
      });
    }
    return this._fs;
  }
  /**
   * Retrieves the settings configuration by combining superclass settings and adapter-specific settings.
   * @readonly
   * @returns {Object} The settings configuration object.
   */
  get settings_config() {
    const _settings_config = {
      ...super.settings_config,
      ...this.process_settings_config(settings_config2),
      ...Object.entries(this.source_adapters).reduce((acc, [file_extension, adapter_constructor]) => {
        if (acc[adapter_constructor]) return acc;
        const item = this.items[Object.keys(this.items).find((key) => key.endsWith(file_extension))];
        const adapter_instance = new adapter_constructor(item || new this.item_type(this.env, {}));
        if (adapter_instance.settings_config) {
          acc[adapter_constructor.name] = {
            type: "html",
            value: `<h4>${adapter_constructor.name} adapter</h4>`
          };
          acc = { ...acc, ...adapter_instance.settings_config };
        }
        return acc;
      }, {})
    };
    return _settings_config;
  }
  /**
   * Retrieves the block collection associated with SmartSources.
   * @readonly
   * @returns {SmartBlocks} The block collection instance.
   */
  get block_collection() {
    return this.env.smart_blocks;
  }
  /**
   * Retrieves the embed queue containing items and their blocks to be embedded.
   * @readonly
   * @returns {Array<Object>} The embed queue.
   */
  get embed_queue() {
    if (!this._embed_queue.length) {
      try {
        const embed_blocks = this.block_collection.settings.embed_blocks;
        this._embed_queue = Object.values(this.items).reduce((acc, item) => {
          if (item._queue_embed || item.should_embed && item.is_unembedded) acc.push(item);
          if (embed_blocks) item.blocks.forEach((block) => {
            if (block._queue_embed || block.should_embed && block.is_unembedded) acc.push(block);
          });
          return acc;
        }, []);
      } catch (error) {
        console.error(`Error getting embed queue:`, error);
      }
    }
    return this._embed_queue;
  }
  /**
   * Clears all data by removing sources and blocks, reinitializing the file system, and reimporting items.
   * @async
   * @returns {Promise<void>}
   */
  async run_clear_all() {
    this.emit_event("sources:clear_started", {
      event_source: "run_clear_all"
    });
    await this.data_adapter.clear_all();
    this.clear();
    this.block_collection.clear();
    this._fs = null;
    await this.init_fs();
    await this.init_items();
    this._excluded_headings = null;
    Object.values(this.items).forEach((item) => {
      item.queue_import();
      item.queue_embed();
      item.loaded_at = Date.now() + 9999999999;
    });
    this.emit_event("sources:clear_completed", {
      event_source: "run_clear_all"
    });
    await this.process_source_import_queue();
  }
  async init_fs(opts = {}) {
    const { force_refresh = false } = opts;
    if (force_refresh) await this.env.fs.refresh();
    await this.fs.load_exclusions();
    this.fs.file_paths = this.fs.post_process(this.env.fs.file_paths);
    this.fs.files = this.fs.file_paths.reduce((acc, file_path) => {
      acc[file_path] = this.env.fs.files[file_path];
      return acc;
    }, {});
    this.fs.folder_paths = this.fs.post_process(this.env.fs.folder_paths);
    this.fs.folders = this.fs.folder_paths.reduce((acc, folder_path) => {
      acc[folder_path] = this.env.fs.folders[folder_path];
      return acc;
    }, {});
  }
  /**
   * Retrieves patterns for excluding files/folders from processing.
   * @readonly
   * @returns {Array<string>}
   */
  get excluded_patterns() {
    return [
      ...this.file_exclusions?.map((file) => `${file}**`) || [],
      ...(this.folder_exclusions || []).map((folder) => `${folder}**`),
      this.env.env_data_dir + "/**"
    ];
  }
  /**
   * Retrieves the file exclusion patterns from settings.
   * @readonly
   * @returns {Array<string>} An array of file exclusion patterns.
   */
  get file_exclusions() {
    const csv = this.env.settings?.smart_sources?.file_exclusions;
    return csv?.length ? csv.split(",").map((file) => file.trim()) : [];
  }
  /**
   * Retrieves the folder exclusion patterns from settings.
   * @readonly
   * @returns {Array<string>} An array of folder exclusion patterns.
   */
  get folder_exclusions() {
    const csv = this.env.settings?.smart_sources?.folder_exclusions;
    return csv?.length ? csv.split(",").map((folder) => {
      folder = folder.trim();
      if (folder === "") return false;
      if (folder === "/") return false;
      if (!folder.endsWith("/")) return folder + "/";
      return folder;
    }).filter(Boolean) : [];
  }
  /**
   * Retrieves the excluded headings from settings.
   * @readonly
   * @returns {Array<string>} An array of excluded headings.
   */
  get excluded_headings() {
    if (!this._excluded_headings) {
      const csv = this.env.settings?.smart_sources?.excluded_headings;
      this._excluded_headings = csv?.length ? csv.split(",").map((heading) => heading.trim()) : [];
    }
    return this._excluded_headings;
  }
  /**
   * Retrieves the count of included files that are not excluded.
   * @readonly
   * @returns {number} The number of included files.
   */
  get included_files() {
    const extensions = Object.keys(this.source_adapters);
    return this.fs.file_paths.filter((file_path) => extensions.some((ext) => file_path.endsWith(ext)) && !this.fs.is_excluded(file_path)).length;
  }
  get excluded_file_paths() {
    return this.env.fs.file_paths.filter((file_path) => this.fs.is_excluded(file_path));
  }
  /**
   * Retrieves the total number of files, regardless of exclusion.
   * @readonly
   * @returns {number} The total number of files.
   */
  get total_files() {
    return this.fs.file_paths.filter((file) => file.endsWith(".md") || file.endsWith(".canvas")).length;
  }
  /**
   * Unloads the collection and clears registered listeners and timers.
   * @returns {void}
   */
  unload() {
    this.unregister_env_event_listeners();
    if (this.sources_re_import_timeout) clearTimeout(this.sources_re_import_timeout);
    this.sources_re_import_timeout = null;
    this.sources_re_import_queue = {};
    this.set_import_progress_state(null);
    super.unload();
  }
  get data_dir() {
    return "multi";
  }
};
var settings_config2 = {};

// node_modules/obsidian-smart-env/node_modules/smart-collections/adapters/_adapter.js
var CollectionDataAdapter = class {
  /**
   * @constructor
   * @param {Object} collection - The collection instance that this adapter manages.
   */
  constructor(collection) {
    this.collection = collection;
    this.env = collection.env;
  }
  /**
   * The class to use for item adapters.
   * @type {typeof ItemDataAdapter}
   */
  ItemDataAdapter = ItemDataAdapter;
  /**
   * Optional factory method to create item adapters.
   * If `this.item_adapter_class` is not null, it uses that; otherwise can be overridden by subclasses.
   * @param {Object} item - The item to create an adapter for.
   * @returns {ItemDataAdapter}
   */
  create_item_adapter(item) {
    if (!this.ItemDataAdapter) {
      throw new Error("No item_adapter_class specified and create_item_adapter not overridden.");
    }
    return new this.ItemDataAdapter(item);
  }
  /**
   * Load a single item by its key using an `ItemDataAdapter`.
   * @async
   * @abstract
   * @param {string} key - The key of the item to load.
   * @returns {Promise<void>} Resolves when the item is loaded.
   */
  async load_item(key) {
    throw new Error("Not implemented");
  }
  /**
   * Save a single item by its key using its associated `ItemDataAdapter`.
   * @async
   * @abstract
   * @param {string} key - The key of the item to save.
   * @returns {Promise<void>} Resolves when the item is saved.
   */
  async save_item(key) {
    throw new Error("Not implemented");
  }
  /**
   * Delete a single item by its key. This may involve updating or removing its file,
   * as handled by the `ItemDataAdapter`.
   * @async
   * @abstract
   * @param {string} key - The key of the item to delete.
   * @returns {Promise<void>} Resolves when the item is deleted.
   */
  async delete_item(key) {
    throw new Error("Not implemented");
  }
  /**
   * Process any queued load operations. Typically orchestrates calling `load_item()`
   * on items that have been flagged for loading.
   * @async
   * @abstract
   * @returns {Promise<void>}
   */
  async process_load_queue() {
    throw new Error("Not implemented");
  }
  /**
   * Process any queued save operations. Typically orchestrates calling `save_item()`
   * on items that have been flagged for saving.
   * @async
   * @abstract
   * @returns {Promise<void>}
   */
  async process_save_queue() {
    throw new Error("Not implemented");
  }
  /**
   * Load the item's data from storage if it has been updated externally.
   * @async
   * @param {string} key - The key of the item to load.
   * @returns {Promise<void>} Resolves when the item is loaded.
   */
  async load_item_if_updated(item) {
    const adapter = this.create_item_adapter(item);
    await adapter.load_if_updated();
  }
  /**
   * Clear all data associated with this collection.
   * @async
   * @abstract
   * @returns {Promise<void>}
   */
  async clear_all() {
    throw new Error("Not implemented");
  }
};
var ItemDataAdapter = class {
  /**
   * @constructor
   * @param {Object} item - The collection item instance that this adapter manages.
   */
  constructor(item) {
    this.item = item;
  }
  /**
   * Load the item's data from storage. May involve reading a file and parsing
   * its contents, then updating `item.data`.
   * @async
   * @abstract
   * @returns {Promise<void>} Resolves when the item is fully loaded.
   */
  async load() {
    throw new Error("Not implemented");
  }
  /**
   * Save the item's data to storage. May involve writing to a file or appending
   * lines in an append-only format.
   * @async
   * @abstract
   * @param {string|null} [ajson=null] - An optional serialized representation of the item’s data.
   *                                     If not provided, the adapter should derive it from the item.
   * @returns {Promise<void>} Resolves when the item is saved.
   */
  async save(ajson = null) {
    throw new Error("Not implemented");
  }
  /**
   * Delete the item's data from storage. May involve removing a file or writing
   * a `null` entry in an append-only file to signify deletion.
   * @async
   * @abstract
   * @returns {Promise<void>} Resolves when the item’s data is deleted.
   */
  async delete() {
    throw new Error("Not implemented");
  }
  /**
   * Returns the file path or unique identifier used by this adapter to locate and store
   * the item's data. This may be a file name derived from the item's key.
   * @abstract
   * @returns {string} The path or identifier for the item's data.
   */
  get data_path() {
    throw new Error("Not implemented");
  }
  /**
   * @returns {CollectionDataAdapter} The collection data adapter that this item data adapter belongs to.
   */
  get collection_adapter() {
    return this.item.collection.data_adapter;
  }
  get env() {
    return this.item.env;
  }
  /**
   * Load the item's data from storage if it has been updated externally.
   * @async
   * @abstract
   * @returns {Promise<void>} Resolves when the item is loaded.
   */
  async load_if_updated() {
    throw new Error("Not implemented");
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-collections/adapters/_file.js
var FileCollectionDataAdapter = class extends CollectionDataAdapter {
  /**
   * The class to use for item adapters.
   * @type {typeof ItemDataAdapter}
   */
  ItemDataAdapter = FileItemDataAdapter;
  /**
   * @returns {Object} Filesystem interface derived from environment or collection settings.
   */
  get fs() {
    return this.collection.data_fs || this.collection.env.data_fs;
  }
  async clear_all() {
    await this.fs.remove_dir(this.collection.data_dir, true);
  }
};
var FileItemDataAdapter = class extends ItemDataAdapter {
  /**
   * @returns {Object} Filesystem interface derived from environment or collection settings.
   */
  get fs() {
    return this.item.collection.data_fs || this.item.collection.env.data_fs;
  }
  /**
   * Resolve the file path for the item's data.
   * @abstract
   * @returns {string} Path to the persisted item data.
   */
  get data_path() {
    throw new Error("Not implemented");
  }
  async load_if_updated() {
    const data_path = this.data_path;
    if (await this.fs.exists(data_path)) {
      const loaded_at = this.item.loaded_at || 0;
      const data_file_stat = await this.fs.stat(data_path);
      if (data_file_stat.mtime > loaded_at + 1 * 60 * 1e3) {
        console.log(`Smart Collections: Re-loading item ${this.item.key} because it has been updated on disk`);
        await this.load();
      }
    }
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-collections/adapters/ajson_multi_file.js
var class_to_collection_key = {
  "SmartSource": "smart_sources",
  "SmartNote": "smart_sources",
  // DEPRECATED
  "SmartBlock": "smart_blocks",
  "SmartDirectory": "smart_directories"
};
var AjsonMultiFileCollectionDataAdapter = class extends FileCollectionDataAdapter {
  /**
   * The class to use for item adapters.
   * @type {typeof ItemDataAdapter}
   */
  ItemDataAdapter = AjsonMultiFileItemDataAdapter;
  /**
   * Load a single item by its key.
   * @async
   * @param {string} key
   * @returns {Promise<void>}
   */
  async load_item(key) {
    const item = this.collection.get(key);
    if (!item) return;
    const adapter = this.create_item_adapter(item);
    await adapter.load();
  }
  /**
   * Save a single item by its key.
   * @async
   * @param {string} key
   * @returns {Promise<void>}
   */
  async save_item(key) {
    const item = this.collection.get(key);
    if (!item) return;
    const adapter = this.create_item_adapter(item);
    await adapter.save();
  }
  /**
   * Process any queued load operations.
   * @async
   * @returns {Promise<void>}
   */
  async process_load_queue() {
    this.collection.emit_event("collection:load_started");
    if (!await this.fs.exists(this.collection.data_dir)) {
      await this.fs.mkdir(this.collection.data_dir);
    }
    const load_queue = Object.values(this.collection.items).filter((item) => item._queue_load);
    const load_queue_length = load_queue.length;
    if (load_queue_length) {
      const now = Date.now();
      console.log(`Loading ${this.collection.collection_key}: ${load_queue_length} items from disk`);
      const batch_size = 100;
      for (let i = 0; i < load_queue.length; i += batch_size) {
        const batch = load_queue.slice(i, i + batch_size);
        await Promise.all(batch.map((item) => {
          const adapter = this.create_item_adapter(item);
          return adapter.load().catch((err) => {
            console.warn(`Error loading item ${item.key}`, err);
            item.queue_load();
          });
        }));
      }
      console.log(`Loaded ${this.collection.collection_key} from disk in ${Date.now() - now}ms`);
    }
    this.collection.loaded = load_queue_length;
    this.collection.emit_event("collection:load_completed", { loaded: load_queue_length });
  }
  /**
   * Process any queued save operations.
   * @async
   * @returns {Promise<void>}
   */
  async process_save_queue() {
    this.collection.emit_event("collection:save_started");
    const save_queue = Object.values(this.collection.items).filter((item) => item._queue_save);
    const save_queue_length = save_queue.length;
    console.log(`Saving ${this.collection.collection_key}: ${save_queue_length} items`);
    const time_start = Date.now();
    const batch_size = 50;
    for (let i = 0; i < save_queue.length; i += batch_size) {
      const batch = save_queue.slice(i, i + batch_size);
      await Promise.all(batch.map((item) => {
        const adapter = this.create_item_adapter(item);
        return adapter.save().catch((err) => {
          console.warn(`Error saving item ${item.key}`, err);
          item.queue_save();
        });
      }));
    }
    const deleted_items = Object.values(this.collection.items).filter((item) => item.deleted);
    if (deleted_items.length) {
      deleted_items.forEach((item) => {
        delete this.collection.items[item.key];
      });
    }
    console.log(`Saved ${this.collection.collection_key} in ${Date.now() - time_start}ms`);
    this.collection.emit_event("collection:save_completed", { saved: save_queue_length });
  }
  get_item_data_path(key) {
    return [
      this.collection.data_dir || "multi",
      this.fs?.sep || "/",
      this.get_data_file_name(key) + ".ajson"
    ].join("");
  }
  /**
   * Transforms the item key into a safe filename.
   * Replaces spaces, slashes, and dots with underscores.
   * @returns {string} safe file name
   */
  get_data_file_name(key) {
    return key.split("#")[0].replace(/[\s\/\.]/g, "_").replace(".md", "");
  }
  /**
   * Build a single AJSON line for the given item and data.
   * @param {Object} item 
   * @returns {string}
   */
  get_item_ajson(item) {
    const collection_key = item.collection_key;
    const key = item.key;
    const data_value = item.deleted ? "null" : JSON.stringify(item.data);
    return `${JSON.stringify(`${collection_key}:${key}`)}: ${data_value},`;
  }
};
var AjsonMultiFileItemDataAdapter = class extends FileItemDataAdapter {
  /**
   * Derives the `.ajson` file path from the collection's data_dir and item key.
   * @returns {string}
   */
  get data_path() {
    return this.collection_adapter.get_item_data_path(this.item.key);
  }
  /**
   * Load the item from its `.ajson` file.
   * @async
   * @returns {Promise<void>}
   */
  async load() {
    try {
      const raw_data = await this.fs.adapter.read(this.data_path, "utf-8", { no_cache: true });
      if (!raw_data) {
        this.item.queue_import();
        return;
      }
      const { rewrite, file_data } = this._parse(raw_data);
      if (rewrite) {
        if (file_data.length) await this.fs.write(this.data_path, file_data);
        else await this.fs.remove(this.data_path);
      }
      const last_import_mtime = this.item.data.last_import?.at || 0;
      if (last_import_mtime && this.item.init_file_mtime > last_import_mtime) {
        this.item.queue_import();
      }
    } catch (e) {
      this.item.queue_import();
    }
  }
  /**
   * Parse the entire AJSON content as a JSON object, handle legacy keys, and extract final state.
   * @private
   * @param {string} ajson 
   * @returns {boolean}
   */
  _parse(ajson) {
    try {
      let rewrite = false;
      if (!ajson.length) return false;
      ajson = ajson.trim();
      const original_line_count = ajson.split("\n").length;
      const json_str = "{" + ajson.slice(0, -1) + "}";
      const data = JSON.parse(json_str);
      const entries = Object.entries(data);
      for (let i = 0; i < entries.length; i++) {
        const [ajson_key, value] = entries[i];
        if (!value) {
          delete data[ajson_key];
          rewrite = true;
          continue;
        }
        const { collection_key, item_key, changed } = this._parse_ajson_key(ajson_key);
        if (changed) {
          rewrite = true;
          data[collection_key + ":" + item_key] = value;
          delete data[ajson_key];
        }
        const collection = this.env[collection_key];
        if (!collection) continue;
        const existing_item = collection.get(item_key);
        if (!value.key) value.key = item_key;
        if (existing_item) {
          existing_item.data = value;
          existing_item._queue_load = false;
          existing_item.loaded_at = Date.now();
        } else {
          const ItemClass = collection.item_type;
          const new_item = new ItemClass(this.env, value);
          new_item._queue_load = false;
          new_item.loaded_at = Date.now();
          collection.set(new_item);
        }
      }
      if (rewrite || original_line_count > entries.length) {
        rewrite = true;
      }
      return {
        rewrite,
        file_data: rewrite ? Object.entries(data).map(([key, value]) => `${JSON.stringify(key)}: ${JSON.stringify(value)},`).join("\n") : null
      };
    } catch (e) {
      if (ajson.split("\n").some((line) => !line.endsWith(","))) {
        console.warn("fixing trailing comma error");
        ajson = ajson.split("\n").map((line) => line.endsWith(",") ? line : line + ",").join("\n");
        return this._parse(ajson);
      }
      console.warn("Error parsing JSON:", e);
      return { rewrite: true, file_data: null };
    }
  }
  _parse_ajson_key(ajson_key) {
    let changed;
    let [collection_key, ...item_key] = ajson_key.split(":");
    if (class_to_collection_key[collection_key]) {
      collection_key = class_to_collection_key[collection_key];
      changed = true;
    }
    return {
      collection_key,
      item_key: item_key.join(":"),
      changed
    };
  }
  /**
   * Save the current state of the item by appending a new line to its `.ajson` file.
   * @async
   * @returns {Promise<void>}
   */
  async save(retries = 0) {
    try {
      const ajson_line = this.get_item_ajson();
      await this.fs.append(this.data_path, "\n" + ajson_line);
      this.item._queue_save = false;
    } catch (e) {
      if (e.code === "ENOENT" && retries < 1) {
        const dir = this.collection_adapter.collection.data_dir;
        if (!await this.fs.exists(dir)) {
          await this.fs.mkdir(dir);
        }
        return await this.save(retries + 1);
      }
      console.warn("Error saving item", this.data_path, this.item.key, e);
    }
  }
  /**
   * Build a single AJSON line for the given item and data.
   * @param {Object} item 
   * @returns {string}
   */
  get_item_ajson() {
    return this.collection_adapter.get_item_ajson(this.item);
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-sources/adapters/data/ajson_multi_file.js
var AjsonMultiFileSourcesDataAdapter = class extends AjsonMultiFileCollectionDataAdapter {
  ItemDataAdapter = AjsonMultiFileSourceDataAdapter;
};
var AjsonMultiFileSourceDataAdapter = class extends AjsonMultiFileItemDataAdapter {
};

// node_modules/obsidian-smart-env/node_modules/smart-sources/adapters/_adapter.js
var SourceContentAdapter = class {
  constructor(item) {
    this.item = item;
  }
  async import() {
    this.throw_not_implemented("import");
  }
  async create() {
    this.throw_not_implemented("create");
  }
  async update() {
    this.throw_not_implemented("update");
  }
  async read() {
    this.throw_not_implemented("read");
  }
  async remove() {
    this.throw_not_implemented("remove");
  }
  // HELPER METHODS
  get data() {
    return this.item.data;
  }
  // async create_hash(content) { return await create_hash(content); }
  create_hash(content) {
    return murmur_hash_32_alphanumeric(content);
  }
  get settings() {
    return this.item.env.settings.smart_sources[this.adapter_key];
  }
  get adapter_key() {
    return to_snake(this.constructor.name);
  }
  static get adapter_key() {
    return to_snake(this.name);
  }
  get fs() {
    return this.item.collection.fs;
  }
  get env() {
    return this.item.env;
  }
};
function to_snake(str) {
  return str[0].toLowerCase() + str.slice(1).replace(/([A-Z])/g, "_$1").toLowerCase();
}

// node_modules/obsidian-smart-env/node_modules/smart-blocks/parsers/markdown.js
function parse_markdown_blocks(markdown, opts = {}) {
  const { start_index = 1, line_keys = false } = opts;
  const lines = markdown.split("\n");
  const LIST_KEY_WORD_LEN = opts.list_key_word_len || 10;
  const result = {};
  const heading_stack = [];
  const heading_lines = {};
  const heading_counts = {};
  const sub_block_counts = {};
  const subheading_counts = {};
  const task_lines = [];
  const tasks = {};
  let current_list_item = null;
  let current_content_block = null;
  let in_frontmatter = false;
  let frontmatter_started = false;
  const root_heading_key = "#";
  let in_code_block = false;
  const codeblock_ranges = [];
  let codeblock_start = null;
  sub_block_counts[root_heading_key] = 0;
  for (let i = 0; i < lines.length; i++) {
    const line_number = i + start_index;
    const line = lines[i];
    const trimmed_line = line.trim();
    if (trimmed_line === "---") {
      if (!frontmatter_started && line_number === 1) {
        frontmatter_started = true;
        in_frontmatter = true;
        heading_lines["#---frontmatter---"] = [line_number, null];
        continue;
      } else if (in_frontmatter) {
        in_frontmatter = false;
        heading_lines["#---frontmatter---"][1] = line_number;
        continue;
      }
    }
    if (in_frontmatter) {
      continue;
    }
    if (!in_code_block && /^[-*+]\s+\[(?: |x|X)\]/.test(trimmed_line)) {
      task_lines.push(line_number);
      if (/^[-*+]\s+\[ \]/.test(trimmed_line)) {
        if (!tasks.incomplete) tasks.incomplete = { all: [], top: [] };
        tasks.incomplete.all.push(line_number);
      }
      if (/^[-*+]\s+\[ \]/.test(line)) {
        tasks.incomplete.top.push(line_number);
      }
    }
    if (trimmed_line.startsWith("```")) {
      in_code_block = !in_code_block;
      if (in_code_block && !codeblock_start) codeblock_start = line_number;
      else if (!in_code_block && codeblock_start) {
        codeblock_ranges.push([codeblock_start, line_number]);
        codeblock_start = null;
      }
      if (!current_content_block) {
        const parent_key = heading_stack.length > 0 ? heading_stack[heading_stack.length - 1].key : root_heading_key;
        if (parent_key === root_heading_key && !heading_lines[root_heading_key]) {
          heading_lines[root_heading_key] = [line_number, null];
        }
        if (parent_key === root_heading_key) {
          current_content_block = { key: root_heading_key, start_line: line_number };
          if (heading_lines[root_heading_key][1] === null || heading_lines[root_heading_key][1] < line_number) {
            heading_lines[root_heading_key][1] = null;
          }
        } else {
          if (sub_block_counts[parent_key] === void 0) {
            sub_block_counts[parent_key] = 0;
          }
          sub_block_counts[parent_key] += 1;
          const n = sub_block_counts[parent_key];
          const key = `${parent_key}#{${n}}`;
          heading_lines[key] = [line_number, null];
          current_content_block = { key, start_line: line_number };
        }
      }
      continue;
    }
    const heading_match = trimmed_line.match(/^(#{1,6})\s+(.+)$/);
    if (heading_match && !in_code_block) {
      const level = heading_match[1].length;
      let title = heading_match[2].trim();
      while (heading_stack.length > 0 && heading_stack[heading_stack.length - 1].level >= level) {
        const finished_heading = heading_stack.pop();
        if (heading_lines[finished_heading.key][1] === null) {
          heading_lines[finished_heading.key][1] = line_number - 1;
        }
      }
      if (heading_stack.length === 0 && heading_lines[root_heading_key] && heading_lines[root_heading_key][1] === null) {
        heading_lines[root_heading_key][1] = line_number - 1;
      }
      if (current_content_block) {
        if (heading_lines[current_content_block.key][1] === null) {
          heading_lines[current_content_block.key][1] = line_number - 1;
        }
        current_content_block = null;
      }
      if (current_list_item) {
        if (heading_lines[current_list_item.key][1] === null) {
          heading_lines[current_list_item.key][1] = line_number - 1;
        }
        current_list_item = null;
      }
      let parent_key = "";
      let parent_level = 0;
      if (heading_stack.length > 0) {
        parent_key = heading_stack[heading_stack.length - 1].key;
        parent_level = heading_stack[heading_stack.length - 1].level;
      } else {
        parent_key = "";
        parent_level = 0;
      }
      if (heading_stack.length === 0) {
        heading_counts[title] = (heading_counts[title] || 0) + 1;
        if (heading_counts[title] > 1) {
          title += `[${heading_counts[title]}]`;
        }
      } else {
        if (!subheading_counts[parent_key]) {
          subheading_counts[parent_key] = {};
        }
        subheading_counts[parent_key][title] = (subheading_counts[parent_key][title] || 0) + 1;
        const count = subheading_counts[parent_key][title];
        if (count > 1) {
          title += `#{${count}}`;
        }
      }
      const level_diff = level - parent_level;
      const hashes = "#".repeat(level_diff);
      const key = parent_key + hashes + title;
      heading_lines[key] = [line_number, null];
      sub_block_counts[key] = 0;
      heading_stack.push({ level, title, key });
      continue;
    }
    const list_match = line.match(/^(\s*)([-*]|\d+\.) (.+)$/);
    if (list_match && !in_code_block) {
      const indentation = list_match[1].length;
      if (indentation === 0) {
        if (current_list_item) {
          if (heading_lines[current_list_item.key][1] === null) {
            heading_lines[current_list_item.key][1] = line_number - 1;
          }
          current_list_item = null;
        }
        if (current_content_block && current_content_block.key !== root_heading_key) {
          if (heading_lines[current_content_block.key][1] === null) {
            heading_lines[current_content_block.key][1] = line_number - 1;
          }
          current_content_block = null;
        }
        let parent_key = heading_stack.length > 0 ? heading_stack[heading_stack.length - 1].key : root_heading_key;
        if (parent_key === root_heading_key && !heading_lines[root_heading_key]) {
          heading_lines[root_heading_key] = [line_number, null];
        }
        if (sub_block_counts[parent_key] === void 0) {
          sub_block_counts[parent_key] = 0;
        }
        sub_block_counts[parent_key] += 1;
        const n = sub_block_counts[parent_key];
        let key;
        if (line_keys) {
          const content_without_task = list_match[3].replace(/^\[(?: |x|X)\]\s*/, "");
          const words = get_longest_words_in_order(content_without_task, LIST_KEY_WORD_LEN);
          key = `${parent_key}#${words}`;
        } else {
          key = `${parent_key}#{${n}}`;
        }
        heading_lines[key] = [line_number, null];
        current_list_item = { key, start_line: line_number };
        continue;
      }
      if (current_list_item) {
        continue;
      }
    }
    if (trimmed_line === "") {
      continue;
    }
    if (!current_content_block) {
      if (current_list_item) {
        if (heading_lines[current_list_item.key][1] === null) {
          heading_lines[current_list_item.key][1] = line_number - 1;
        }
        current_list_item = null;
      }
      let parent_key = heading_stack.length > 0 ? heading_stack[heading_stack.length - 1].key : root_heading_key;
      if (parent_key === root_heading_key) {
        if (!heading_lines[root_heading_key]) {
          heading_lines[root_heading_key] = [line_number, null];
        }
        if (heading_lines[root_heading_key][1] === null || heading_lines[root_heading_key][1] < line_number) {
          heading_lines[root_heading_key][1] = null;
        }
        current_content_block = { key: root_heading_key, start_line: line_number };
      } else {
        if (sub_block_counts[parent_key] === void 0) {
          sub_block_counts[parent_key] = 0;
        }
        sub_block_counts[parent_key] += 1;
        const n = sub_block_counts[parent_key];
        const key = `${parent_key}#{${n}}`;
        heading_lines[key] = [line_number, null];
        current_content_block = { key, start_line: line_number };
      }
    }
  }
  const total_lines = lines.length;
  while (heading_stack.length > 0) {
    const finished_heading = heading_stack.pop();
    if (heading_lines[finished_heading.key][1] === null) {
      heading_lines[finished_heading.key][1] = total_lines + start_index - 1;
    }
  }
  if (current_list_item) {
    if (heading_lines[current_list_item.key][1] === null) {
      heading_lines[current_list_item.key][1] = total_lines + start_index - 1;
    }
    current_list_item = null;
  }
  if (current_content_block) {
    if (heading_lines[current_content_block.key][1] === null) {
      heading_lines[current_content_block.key][1] = total_lines + start_index - 1;
    }
    current_content_block = null;
  }
  if (heading_lines[root_heading_key] && heading_lines[root_heading_key][1] === null) {
    heading_lines[root_heading_key][1] = total_lines + start_index - 1;
  }
  for (const key in heading_lines) {
    result[key] = heading_lines[key];
  }
  return { blocks: result, task_lines, tasks, codeblock_ranges };
}
function get_longest_words_in_order(line, n = 3) {
  const words = line.split(/\s+/).sort((a, b) => b.length - a.length).slice(0, n);
  return words.sort((a, b) => line.indexOf(a) - line.indexOf(b)).join(" ");
}

// node_modules/obsidian-smart-env/node_modules/smart-sources/adapters/_file.js
var FileSourceContentAdapter = class extends SourceContentAdapter {
  static async init_items(collection) {
    if (collection.fs_items_initialized) return;
    collection._fs = null;
    await collection.fs.init();
    await collection.init_fs();
    for (const file of Object.values(collection.fs.files)) {
      const item = collection.init_file_path(file.path);
      if (item) item.init_file_mtime = file.stat.mtime;
    }
    collection.fs_items_initialized = Date.now();
  }
  /**
   * @name fs
   * @type {Object}
   * @readonly
   * @description
   * Access the file system interface used by this adapter. Typically derived
   * from `this.item.collection.fs`.
   */
  get fs() {
    return this.item.collection.fs;
  }
  /**
   * @name file_path
   * @type {string}
   * @readonly
   * @description
   * The file path on disk corresponding to the source. Used for read/write operations.
   */
  get file_path() {
    return this.item.file_path;
  }
  /**
   * @async
   * @method create
   * @param {string|null} [content=null] Initial content for the new file.
   * @description
   * Create a new file on disk. If content is not provided, attempts to use
   * `this.item.data.content` as fallback.
   */
  async create(content = null) {
    if (!content) content = this.item.data.content || "";
    await this.fs.write(this.file_path, content);
  }
  /**
   * @async
   * @method update
   * @param {string} content The full new content to write to the file.
   * @description
   * Overwrite the entire file content on disk.
   */
  async update(content) {
    await this.fs.write(this.file_path, content);
  }
  /**
   * @async
   * @method read
   * @returns {Promise<string>} The content of the file.
   * @description
   * Read the file content from disk. Updates `last_read` hash and timestamp on the entity’s data.
   * If file is large or special handling is needed, override this method.
   */
  async read() {
    const content = await this.fs.read(this.file_path);
    this.data.last_read = {
      hash: this.create_hash(content || ""),
      at: Date.now()
    };
    return content;
  }
  /**
   * @async
   * @method remove
   * @returns {Promise<void>}
   * @description
   * Delete the file from disk. After removal, the source item should also be deleted or updated accordingly.
   */
  async remove() {
    await this.fs.remove(this.file_path);
  }
  async move_to(move_to_ref) {
    if (!move_to_ref) {
      throw new Error("Invalid entity reference for move_to operation");
    }
    const move_content = await this.read();
    let has_existing = false;
    if (typeof move_to_ref === "string") {
      const existing = this.item.collection.get(move_to_ref);
      if (existing) {
        move_to_ref = existing;
        has_existing = true;
      }
    } else {
      has_existing = true;
    }
    if (has_existing) {
      await move_to_ref.append(move_content);
    } else {
      move_to_ref = await this.item.collection.create(move_to_ref, move_content);
    }
    if (this.item.key !== move_to_ref.key) {
      await this.remove();
      this.item.delete();
    } else {
      console.log(`did not delete ${this.item.key} because it was moved to ${move_to_ref.key}`);
    }
    return move_to_ref;
  }
  /**
   * Merge content into the source
   * @param {string} content - The content to merge into the source
   * @param {Object} opts - Options for the merge operation
   * @param {string} opts.mode - The mode to use for the merge operation. Defaults to 'append_blocks' (may also be 'replace_blocks')
   */
  async merge(content, opts = {}) {
    const { mode = "append_blocks" } = opts;
    const { blocks: blocks_obj, task_lines } = parse_markdown_blocks(content);
    if (typeof blocks_obj !== "object" || Array.isArray(blocks_obj)) {
      console.warn("merge error: Expected an object from parse_markdown_blocks, but received:", blocks_obj);
      throw new Error("merge error: parse_markdown_blocks did not return an object as expected.");
    }
    const { new_blocks, new_with_parent_blocks, changed_blocks, same_blocks } = await this.get_changes(blocks_obj, content);
    for (const block of new_blocks) {
      await this.append(block.content);
    }
    for (const block of new_with_parent_blocks) {
      const parent_block = this.item.block_collection.get(block.parent_key);
      await parent_block.append(block.content);
    }
    for (const block of changed_blocks) {
      const changed_block = this.item.block_collection.get(block.key);
      if (mode === "replace_blocks") {
        await changed_block.update(block.content);
      } else {
        await changed_block.append(block.content);
      }
    }
  }
  async get_changes(blocks_obj, content) {
    const new_blocks = [];
    const new_with_parent_blocks = [];
    const changed_blocks = [];
    const same_blocks = [];
    const existing_blocks = this.source.data.blocks || {};
    for (const [sub_key, line_range] of Object.entries(blocks_obj)) {
      const has_existing = !!existing_blocks[sub_key];
      const block_key = `${this.source.key}${sub_key}`;
      const block_content = get_line_range(content, line_range[0], line_range[1]);
      if (!has_existing) {
        new_blocks.push({
          key: block_key,
          state: "new",
          content: block_content
        });
        continue;
      }
      let has_parent;
      let headings = sub_key.split("#");
      let parent_key;
      while (!has_parent && headings.length > 0) {
        headings.pop();
        parent_key = headings.join("#");
        has_parent = !!existing_blocks[parent_key];
      }
      if (has_parent) {
        new_with_parent_blocks.push({
          key: block_key,
          parent_key: `${this.source.key}${parent_key}`,
          state: "new",
          content: block_content
        });
        continue;
      }
      const block = this.item.block_collection.get(block_key);
      const content_hash = await this.create_hash(block_content);
      if (content_hash !== block.last_read?.hash) {
        changed_blocks.push({
          key: block_key,
          state: "changed",
          content: block_content
        });
        continue;
      }
      same_blocks.push({
        key: block_key,
        state: "same",
        content: block_content
      });
    }
    return {
      new_blocks,
      new_with_parent_blocks,
      changed_blocks,
      same_blocks
    };
  }
  /**
   * Append new content to the source file, placing it at the end of the file.
   * @async
   * @param {string} content - The content to append.
   * @returns {Promise<void>}
   */
  async append(content) {
    const current_content = await this.read();
    const new_content = [
      current_content,
      "",
      content
    ].join("\n").trim();
    await this.update(new_content);
  }
  get size() {
    return this.item.file?.stat?.size || 0;
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-sources/utils/get_markdown_links.js
function get_markdown_links(content) {
  const result = [];
  const markdown_link_re = /\[([^\]]+?)\]\(([^)]+?)\)/g;
  const wikilink_re = /\[\[([^\|\]]+?)(?:\|([^\]]+?))?\]\]/g;
  const normalise_target = (raw) => {
    const trimmed = raw.trim();
    if (/^[a-zA-Z][\w+\-.]*:\/\//.test(trimmed)) return trimmed;
    try {
      return decodeURIComponent(trimmed);
    } catch (_) {
      return trimmed.replace(/%20/gi, " ");
    }
  };
  const is_embedded = (index) => {
    if (index <= 0) return false;
    return content[index - 1] === "!";
  };
  let m;
  while ((m = markdown_link_re.exec(content)) !== null) {
    const title = m[1];
    const target = normalise_target(m[2]);
    const line_no = content.slice(0, m.index).split("\n").length;
    const embedded = is_embedded(m.index);
    const record = { title, target, line: line_no };
    if (embedded) record.embedded = true;
    result.push(record);
  }
  while ((m = wikilink_re.exec(content)) !== null) {
    const target_raw = m[1];
    const title = m[2] || target_raw;
    const target = normalise_target(target_raw);
    const line_no = content.slice(0, m.index).split("\n").length;
    const embedded = is_embedded(m.index);
    const record = { title, target, line: line_no };
    if (embedded) record.embedded = true;
    result.push(record);
  }
  return result.sort(
    (a, b) => a.line - b.line || a.target.localeCompare(b.target)
  );
}

// node_modules/obsidian-smart-env/node_modules/smart-sources/utils/get_bases_cache_links.js
function get_bases_cache_links({ source, links = [], cache } = {}) {
  if (!source || !Array.isArray(links) || !links.length) return [];
  const cache_items = cache || source?.env?.bases_caches?.items;
  if (!cache_items) return [];
  const source_key = source?.key || source?.path;
  if (!source_key) return [];
  return links.flatMap((link) => {
    if (!link?.embedded) return [];
    if (typeof link.target !== "string" || !link.target.includes(".base")) return [];
    const cache_key = `${source_key}#${link.target}`;
    const markdown_table = get_bases_markdown_table(cache_items?.[cache_key]);
    if (!markdown_table) return [];
    return get_bases_table_links({ markdown_table, line_override: link.line });
  });
}
function get_bases_file_links({ source, cache } = {}) {
  if (!source || typeof source !== "object") return [];
  const cache_items = cache || source?.env?.bases_caches?.items;
  if (!cache_items) return [];
  const source_key = source?.key || source?.path;
  if (!source_key) return [];
  const markdown_table = get_bases_markdown_table(cache_items?.[source_key]);
  if (!markdown_table) return [];
  return get_bases_table_links({ markdown_table });
}
function get_bases_markdown_table(cache_item) {
  if (!cache_item) return "";
  if (typeof cache_item.markdown_table === "string") return cache_item.markdown_table;
  if (typeof cache_item.markdown_table === "function") return cache_item.markdown_table();
  if (typeof cache_item?.data?.markdown_table === "string") return cache_item.data.markdown_table;
  return "";
}
function get_bases_table_links({ markdown_table, line_override } = {}) {
  if (!markdown_table) return [];
  const table_links = get_markdown_links(markdown_table);
  if (!table_links.length) return [];
  return table_links.map((table_link) => ({
    ...table_link,
    line: typeof line_override === "number" ? line_override : table_link.line,
    bases_row: table_link.line - 2
    // Adjust for table header rows
  }));
}

// node_modules/obsidian-smart-env/node_modules/smart-sources/utils/parse_frontmatter.js
function parse_value(raw_value) {
  const trimmed = raw_value.trim();
  if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
    return trimmed.slice(1, -1);
  }
  const lower = trimmed.toLowerCase();
  if (lower === "true") return true;
  if (lower === "false") return false;
  if (!isNaN(trimmed) && trimmed !== "") {
    return Number(trimmed);
  }
  return trimmed;
}
function parse_yaml_block(yaml_block) {
  const lines = yaml_block.split(/\r?\n/);
  const data = {};
  let i = 0;
  while (i < lines.length) {
    const line = lines[i];
    i++;
    if (!line.trim() || line.trim().startsWith("#")) {
      continue;
    }
    const match = line.match(/^([^:]+)\s*:\s*(.*)$/);
    if (!match) {
      continue;
    }
    const key = match[1].trim();
    let value = match[2].trim();
    if (value === ">" || value === "|") {
      const multiline_lines = [];
      while (i < lines.length) {
        const next_line = lines[i];
        if (!/^\s+/.test(next_line) || next_line.trim().startsWith("#")) {
          break;
        }
        multiline_lines.push(next_line.replace(/^\s+/, ""));
        i++;
      }
      const joined = multiline_lines.join("\n");
      data[key] = parse_value(joined);
    } else if (value === "") {
      const arr = [];
      let array_consumed = false;
      while (i < lines.length) {
        const next_line = lines[i];
        if (!next_line.trim().startsWith("- ")) {
          break;
        }
        const item_value = next_line.trim().slice(2);
        arr.push(parse_value(item_value));
        i++;
        array_consumed = true;
      }
      if (array_consumed) {
        data[key] = arr;
      } else {
        data[key] = "";
      }
    } else {
      data[key] = parse_value(value);
    }
  }
  return data;
}
function parse_frontmatter(content) {
  if (!content.startsWith("---")) {
    return { frontmatter: {}, body: content };
  }
  const lines = content.split(/\r?\n/);
  let end_index = -1;
  for (let i = 1; i < lines.length; i++) {
    if (lines[i].trim() === "---") {
      end_index = i;
      break;
    }
  }
  if (end_index === -1) {
    return { frontmatter: {}, body: content };
  }
  const frontmatter_lines = lines.slice(1, end_index);
  const frontmatter_block = frontmatter_lines.join("\n");
  const frontmatter = parse_yaml_block(frontmatter_block);
  const body_lines = lines.slice(end_index + 1);
  const body = body_lines.join("\n");
  return { frontmatter, body };
}

// node_modules/obsidian-smart-env/node_modules/smart-sources/utils/get_markdown_tags.js
var get_markdown_tags = (content = "") => {
  const tag_re = /(?<!\w)#([\w/-]+)/g;
  const tags = /* @__PURE__ */ new Set();
  let match;
  while ((match = tag_re.exec(content)) !== null) {
    tags.add(`#${match[1]}`);
  }
  return [...tags];
};

// node_modules/obsidian-smart-env/node_modules/smart-sources/adapters/markdown_source.js
var MarkdownSourceContentAdapter = class extends FileSourceContentAdapter {
  static extensions = ["md", "txt"];
  /**
   * Import the source file content, parse blocks and links, and update `item.data`.
   * @async
   * @returns {Promise<void>}
   */
  async import() {
    if (!this.can_import) return;
    const is_outdated = this.outdated;
    const has_incomplete_block_coverage = this.has_incomplete_block_coverage();
    const repairing_block_coverage = !is_outdated && has_incomplete_block_coverage;
    if (!is_outdated && !has_incomplete_block_coverage) {
      this.item.blocks.forEach((block) => {
        if (!block.vec) block.queue_embed();
      });
      return;
    }
    const content = await this.read();
    if (!content) {
      return;
    }
    if (!this.item.vec) {
      this.item.data.last_import = null;
    }
    if (!has_incomplete_block_coverage && this.data.last_import?.hash === this.data.last_read?.hash) {
      if (this.data.blocks) return;
    }
    this.data.blocks = null;
    await this.parse_content(content);
    await this.item.parse_content(content);
    const { mtime, size } = this.item.file.stat;
    this.data.last_import = {
      mtime,
      size,
      at: Date.now(),
      hash: this.data.last_read.hash
    };
    this.item.loaded_at = Date.now();
    this.item.queue_save();
    if (this.item.should_embed && !repairing_block_coverage) this.item.queue_embed();
  }
  // // WIP: move block parsing here
  // async read() {
  //   const current_last_read_hash = this.data.last_read?.hash;
  //   const content = await super.read();
  //   if(!content) return console.warn(`MarkdownSourceContentAdapter: Skipping missing-file: ${this.file_path}`);
  //   if(current_last_read_hash === this.data.last_read?.hash) return content;
  //   const {blocks: blocks, task_lines} = parse_markdown_blocks(content);
  //   this.handle_excluded_headings(blocks);
  // }
  // Runs before configured content_parsers (for example, templates uses outlinks)
  async parse_content(content) {
    const outlinks = await this.get_links(content);
    this.data.outlinks = outlinks;
    const metadata = await this.get_metadata(content);
    this.data.metadata = metadata;
  }
  async get_links(content = null) {
    if (!content) content = await this.read();
    if (!content) return;
    const markdown_links = get_markdown_links(content);
    const bases_links = get_bases_cache_links({
      source: this.item,
      links: markdown_links
    });
    return [
      ...markdown_links,
      ...bases_links
    ];
  }
  async get_metadata(content = null) {
    if (!content) content = await this.read();
    if (!content) return;
    const { frontmatter, body } = parse_frontmatter(content);
    const tag_set = /* @__PURE__ */ new Set();
    let fm_tags = frontmatter.tags;
    if (typeof fm_tags === "string") {
      fm_tags = fm_tags.replace(/[\[\]]/g, "").split(",").map((t) => t.trim()).filter(Boolean);
    }
    if (Array.isArray(fm_tags)) {
      fm_tags.forEach((tag) => tag_set.add(tag.startsWith("#") ? tag : `#${tag}`));
    }
    get_markdown_tags(body).forEach((tag) => tag_set.add(tag));
    if (tag_set.size) frontmatter.tags = [...tag_set];
    return frontmatter;
  }
  has_incomplete_block_coverage() {
    if (!this.data.blocks || !this.item.block_collection) return false;
    return Object.entries(this.data.blocks).some(([sub_key, line_range]) => {
      const block = this.item.block_collection.get(this.item.key + sub_key);
      if (!block) return true;
      const block_lines = block.lines || [];
      return block_lines[0] !== line_range[0] || block_lines[1] !== line_range[1];
    });
  }
  // Erroneous reasons to skip import (logs to console)
  get can_import() {
    if (!this.item.file) {
      console.warn(`MarkdownSourceContentAdapter: Skipping missing-file: ${this.file_path}`);
      return false;
    }
    if (this.item.size > (this.settings?.max_import_size || 3e5)) {
      console.warn(`MarkdownSourceContentAdapter: Skipping large file: ${this.file_path}`);
      return false;
    }
    return true;
  }
  /**
   * @deprecated use outdated instead
   */
  get should_import() {
    return this.outdated;
  }
  get outdated() {
    try {
      if (!this.data.last_import) {
        return true;
      }
      if (this.data.last_read.at > this.data.last_import.at) {
        if (this.data.last_import?.hash !== this.data.last_read?.hash) return true;
      }
      if (this.data.last_import.mtime < this.item.mtime) {
        if (!this.data.last_import.size) return true;
        const size_diff = Math.abs(this.data.last_import.size - this.item.size);
        const size_diff_ratio = size_diff / (this.data.last_import.size || 1);
        if (size_diff_ratio > 0.01) return true;
      }
      return false;
    } catch (e) {
      console.warn(`MarkdownSourceContentAdapter: error getting should_import for ${this.file_path}: ${e}`);
      return true;
    }
  }
};

// node_modules/obsidian-smart-env/adapters/smart-sources/obsidian_markdown.js
var import_obsidian2 = require("obsidian");
function merge_tags(fm_tags, cache_tags = []) {
  const tag_set = /* @__PURE__ */ new Set();
  if (typeof fm_tags === "string") {
    fm_tags = fm_tags.replace(/[\[\]]/g, "").split(",").map((t) => t.trim()).filter(Boolean);
  }
  if (Array.isArray(fm_tags)) {
    fm_tags.filter((t) => typeof t === "string").forEach((tag) => tag_set.add(tag.startsWith("#") ? tag : `#${tag}`));
  }
  cache_tags.forEach(({ tag }) => tag_set.add(tag));
  return [...tag_set];
}
var ObsidianMarkdownSourceContentAdapter = class extends MarkdownSourceContentAdapter {
  /**
   * Returns metadata using Obsidian's metadataCache, merging frontmatter and tags.
   * @async
   * @returns {Promise<Object|undefined>}
   */
  async get_metadata() {
    const app2 = this.item.env.main.app;
    const cache = app2.metadataCache.getFileCache(this.item.file) || {};
    const tags = merge_tags(cache.frontmatter?.tags, cache.tags);
    if (cache.frontmatter) {
      if (tags.length) cache.frontmatter.tags = tags;
      return cache.frontmatter;
    }
    return tags.length ? { tags } : void 0;
  }
  /**
   * Reads the file content. If opts.render_output is true, attempts to use
   * Obsidian's MarkdownRenderer to render the file to HTML, then convert it
   * back to markdown via htmlToMarkdown.
   * @async
   * @param {Object} [opts={}] - Options for reading.
   * @param {boolean} [opts.render_output=false] - If true, render MD -> HTML -> MD.
   * @returns {Promise<string>} The file content (possibly rendered).
   */
  async read(opts = {}) {
    const content = await super.read(opts);
    if (!opts.render_output) {
      return content;
    }
    const app2 = this.item.env.main.app;
    if (!app2 || !import_obsidian2.MarkdownRenderer || !import_obsidian2.htmlToMarkdown) {
      console.warn("Obsidian environment not found; cannot render markdown.");
      return content;
    }
    const container = document.createElement("div");
    await import_obsidian2.MarkdownRenderer.render(app2, content, container, this.item.path, new import_obsidian2.Component());
    let last_html = container.innerHTML;
    const max_wait = 1e4;
    let wait_time = 0;
    let conseq_same = 0;
    let changed = true;
    while (conseq_same < 7) {
      await new Promise((resolve) => setTimeout(resolve, 100));
      changed = last_html !== container.innerHTML;
      last_html = container.innerHTML;
      if (!changed) conseq_same++;
      else conseq_same = 0;
      wait_time += 100;
      if (wait_time > max_wait) {
        console.warn("ObsidianMarkdownSourceContentAdapter: Timeout waiting for markdown to render.");
        break;
      }
    }
    const newMd = (0, import_obsidian2.htmlToMarkdown)(container);
    return newMd;
  }
};

// node_modules/obsidian-smart-env/adapters/smart-sources/bases.js
var BasesSourceContentAdapter = class extends FileSourceContentAdapter {
  static extensions = ["base"];
  async import() {
    if (!this.item?.file) return;
    const base_links = get_bases_file_links({ source: this.item });
    this.data.outlinks = base_links;
    this.data.blocks = this.data.blocks || {};
    this.data.metadata = this.data.metadata || {};
    const { mtime = 0, size = 0 } = this.item.file?.stat || {};
    this.data.last_import = {
      mtime,
      size,
      at: Date.now(),
      hash: this.data.last_read?.hash
    };
    this.item.loaded_at = Date.now();
    this.item.queue_save?.();
  }
};

// node_modules/obsidian-smart-env/adapters/smart-sources/rendered.js
var RenderedSourceContentAdapter = class extends FileSourceContentAdapter {
  static extensions = ["rendered"];
  async import() {
  }
};

// node_modules/obsidian-smart-env/adapters/smart-sources/canvas.js
function parse_canvas_json({ content } = {}) {
  if (!content) return null;
  try {
    return JSON.parse(content);
  } catch (error) {
    console.warn("CanvasSourceContentAdapter: invalid JSON content.", error);
    return null;
  }
}
function build_link_record({ target, title } = {}) {
  if (!target) return null;
  return {
    title: title || target,
    target,
    line: 1
  };
}
function get_canvas_node_links({ node } = {}) {
  if (!node || typeof node !== "object") return [];
  if (node.type === "text" && typeof node.text === "string") {
    return get_markdown_links(node.text);
  }
  if (node.type === "file" && typeof node.file === "string") {
    const subpath = typeof node.subpath === "string" ? node.subpath : "";
    const target = `${node.file}${subpath}`;
    const record = build_link_record({ target, title: node.file });
    return record ? [record] : [];
  }
  if (node.type === "link" && typeof node.url === "string") {
    const record = build_link_record({ target: node.url, title: node.url });
    return record ? [record] : [];
  }
  return [];
}
function get_canvas_links_from_nodes({ nodes = [] } = {}) {
  if (!Array.isArray(nodes)) return [];
  return nodes.reduce((links, node) => {
    links.push(...get_canvas_node_links({ node }));
    return links;
  }, []);
}
function get_canvas_links({ content } = {}) {
  const canvas_data = parse_canvas_json({ content });
  if (!canvas_data?.nodes) return [];
  return get_canvas_links_from_nodes({ nodes: canvas_data.nodes });
}
var CanvasSourceContentAdapter = class extends FileSourceContentAdapter {
  static extensions = ["canvas"];
  async import() {
    if (!this.item.file) {
      console.warn(`CanvasSourceContentAdapter: Skipping missing-file: ${this.file_path}`);
      return;
    }
    const content = await this.read();
    if (!content) return;
    if (this.data.last_import?.hash === this.data.last_read?.hash && Array.isArray(this.data.outlinks)) {
      return;
    }
    this.data.outlinks = get_canvas_links({ content });
    const file_stat = this.item.file?.stat;
    const size = file_stat?.size ?? content.length;
    const mtime = file_stat?.mtime ?? 0;
    this.data.last_import = {
      mtime,
      size,
      at: Date.now(),
      hash: this.data.last_read?.hash
    };
    this.item.loaded_at = Date.now();
    this.item.queue_save();
  }
};

// node_modules/obsidian-smart-env/adapters/smart-sources/excalidraw.js
var ExcalidrawSourceContentAdapter = class extends ObsidianMarkdownSourceContentAdapter {
  static extensions = ["excalidraw.md"];
  is_media = true;
  // Excalidraw files are treated as media for rendering
  async read(opts = {}) {
    const full_content = await super.read(opts);
    const BEGIN_LINE_MATCHER = "# Text Elements";
    const END_LINE_MATCHER = "# Drawing";
    const text_elements_start = full_content.indexOf(BEGIN_LINE_MATCHER);
    const drawing_lines_start = full_content.indexOf(END_LINE_MATCHER);
    if (text_elements_start === -1 || drawing_lines_start === -1) {
      console.warn("Excalidraw file does not contain expected sections. File: " + this.item.key);
      this.item.data.last_read.size = 0;
      return "";
    }
    const text_content = full_content.slice(text_elements_start + BEGIN_LINE_MATCHER.length, drawing_lines_start).trim();
    const stripped_refs = text_content.split("\n").map((line) => {
      if (line.trim() === "%%") return "";
      if (line.trim() === "#") return "";
      return line.replace(/\^[a-z0-9]+$/i, "").trim();
    }).filter(Boolean).join("\n");
    this.item.data.last_read.size = stripped_refs.length;
    return stripped_refs;
  }
  get size() {
    if (this.item.data?.last_read?.size) {
      return this.item.data.last_read.size;
    }
    return this.file?.stat?.size || 0;
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-model/smart_model.js
var SmartModel = class {
  scope_name = "smart_model";
  static defaults = {
    // override in sub-class if needed
  };
  /**
   * Create a SmartModel instance.
   * @param {Object} opts - Configuration options
   * @param {Object} opts.adapters - Map of adapter names to adapter classes
   * @param {Object} opts.settings - Model settings configuration
   * @param {string} [opts.model_key] - Optional model identifier to override settings
   * @throws {Error} If required options are missing
   */
  constructor(opts = {}) {
    this.opts = opts;
    this.validate_opts(opts);
    this.state = "unloaded";
    this._adapter = null;
    this.data = opts;
  }
  /**
   * Initialize the model by loading the configured adapter.
   * @async
   * @returns {Promise<void>}
   */
  async initialize() {
    this.load_adapter(this.adapter_name);
    await this.load();
  }
  /**
   * Validate required options.
   * @param {Object} opts - Configuration options
   */
  validate_opts(opts) {
    if (!opts.adapters) throw new Error("opts.adapters is required");
    if (!opts.settings) throw new Error("opts.settings is required");
  }
  /**
   * Get the current settings
   * @returns {Object} Current settings
   */
  get settings() {
    if (!this.opts.settings) this.opts.settings = {
      ...this.constructor.defaults
    };
    return this.opts.settings;
  }
  /**
   * Get the current adapter name
   * @returns {string} Current adapter name
   */
  get adapter_name() {
    let adapter_key = this.opts.adapter || this.settings.adapter || Object.keys(this.adapters)[0];
    if (!adapter_key || !this.adapters[adapter_key]) {
      console.warn(`Platform "${adapter_key}" not supported`);
      adapter_key = Object.keys(this.adapters)[0];
    }
    return adapter_key;
  }
  /**
   * Get available models.
   * @returns {Object} Map of model objects
   */
  get models() {
    return this.adapter.models;
  }
  /**
   * Get default model key.
   * @returns {string} Default model key
   */
  get default_model_key() {
    return this.adapter.constructor.defaults.default_model;
  }
  /**
   * Get the current model key
   * @returns {string} Current model key
   */
  get model_key() {
    return this.opts.model_key || this.settings.model_key || this.default_model_key;
  }
  /**
   * Load the current adapter and transition to loaded state.
   * @async
   * @returns {Promise<void>}
   */
  async load() {
    this.set_state("loading");
    try {
      if (!this.adapter?.is_loaded) {
        await this.invoke_adapter_method("load");
      }
    } catch (err) {
      this.set_state("unloaded");
      if (!this.reload_model_timeout) {
        this.reload_model_timeout = setTimeout(async () => {
          this.reload_model_timeout = null;
          await this.load();
          this.set_state("loaded");
          this.env?.events?.emit("model:loaded", { model_key: this.model_key });
        }, 6e4);
      }
      throw new Error(`Failed to load model: ${err.message}`);
    }
    this.set_state("loaded");
  }
  /**
   * Unload the current adapter and transition to unloaded state.
   * @async
   * @returns {Promise<void>}
   */
  async unload() {
    if (this.adapter?.is_loaded) {
      this.set_state("unloading");
      await this.invoke_adapter_method("unload");
      this.set_state("unloaded");
    }
  }
  /**
   * Set the model's state.
   * @param {('unloaded'|'loading'|'loaded'|'unloading')} new_state - The new state
   * @throws {Error} If the state is invalid
   */
  set_state(new_state) {
    const valid_states = ["unloaded", "loading", "loaded", "unloading"];
    if (!valid_states.includes(new_state)) {
      throw new Error(`Invalid state: ${new_state}`);
    }
    this.state = new_state;
  }
  get is_loading() {
    return this.state === "loading";
  }
  get is_loaded() {
    return this.state === "loaded";
  }
  get is_unloading() {
    return this.state === "unloading";
  }
  get is_unloaded() {
    return this.state === "unloaded";
  }
  // ADAPTERS
  /**
   * Get the map of available adapters
   * @returns {Object} Map of adapter names to adapter classes
   */
  get adapters() {
    return this.opts.adapters || {};
  }
  /**
   * Load a specific adapter by name.
   * @async
   * @param {string} adapter_name - Name of the adapter to load
   * @throws {Error} If adapter not found or loading fails
   * @returns {Promise<void>}
   */
  async load_adapter(adapter_name) {
    this.set_adapter(adapter_name);
    if (!this._adapter.loaded) {
      this.set_state("loading");
      try {
        await this.invoke_adapter_method("load");
        this.set_state("loaded");
      } catch (err) {
        this.set_state("unloaded");
        throw new Error(`Failed to load adapter: ${err.message}`);
      }
    }
  }
  /**
   * Set an adapter instance by name without loading it.
   * @param {string} adapter_name - Name of the adapter to set
   * @throws {Error} If adapter not found
   */
  set_adapter(adapter_name) {
    const AdapterClass = this.adapters[adapter_name];
    if (!AdapterClass) {
      throw new Error(`Adapter "${adapter_name}" not found.`);
    }
    if (this._adapter?.constructor.name.toLowerCase() === adapter_name.toLowerCase()) {
      return;
    }
    this._adapter = new AdapterClass(this);
  }
  /**
   * Get the current active adapter instance
   * @returns {Object} The active adapter instance
   * @throws {Error} If adapter not found
   */
  get adapter() {
    const adapter_name = this.adapter_name;
    if (!adapter_name) {
      throw new Error(`Adapter not set for model.`);
    }
    if (!this._adapter) {
      this.load_adapter(adapter_name);
    }
    return this._adapter;
  }
  /**
   * Ensure the adapter is ready to execute a method.
   * @param {string} method - Name of the method to check
   * @throws {Error} If adapter not loaded or method not implemented
   */
  ensure_adapter_ready(method) {
    if (!this.adapter) {
      throw new Error("No adapter loaded.");
    }
    if (typeof this.adapter[method] !== "function") {
      throw new Error(`Adapter does not implement method: ${method}`);
    }
  }
  /**
   * Invoke a method on the current adapter.
   * @async
   * @param {string} method - Name of the method to call
   * @param {...any} args - Arguments to pass to the method
   * @returns {Promise<any>} Result from the adapter method
   * @throws {Error} If adapter not ready or method fails
   */
  async invoke_adapter_method(method, ...args) {
    this.ensure_adapter_ready(method);
    return await this.adapter[method](...args);
  }
  /**
   * Get platforms as dropdown options.
   * @returns {Array<Object>} Array of {value, name} option objects
   */
  get_platforms_as_options() {
    return Object.entries(this.adapters).map(([key, AdapterClass]) => ({ value: key, name: AdapterClass.defaults.description || key }));
  }
  // SETTINGS
  /**
   * Get the settings configuration schema
   * @returns {Object} Settings configuration object
   */
  get settings_config() {
    return this.process_settings_config({
      adapter: {
        name: "Model Platform",
        type: "dropdown",
        description: "Select a model platform to use with Smart Model.",
        options_callback: "get_platforms_as_options",
        is_scope: true,
        // trigger re-render of settings when changed
        callback: "adapter_changed",
        default: "default"
      }
    });
  }
  /**
   * Process settings configuration with conditionals and prefixes.
   * @param {Object} _settings_config - Raw settings configuration
   * @param {string} [prefix] - Optional prefix for setting keys
   * @returns {Object} Processed settings configuration
   */
  process_settings_config(_settings_config, prefix = null) {
    return Object.entries(_settings_config).reduce((acc, [key, val]) => {
      const new_key = (prefix ? prefix + "." : "") + this.process_setting_key(key);
      acc[new_key] = val;
      return acc;
    }, {});
  }
  /**
   * Process an individual setting key.
   * Example: replace placeholders with actual adapter names.
   * @param {string} key - The setting key with placeholders.
   * @returns {string} Processed setting key.
   */
  process_setting_key(key) {
    return key.replace(/\[ADAPTER\]/g, this.adapter_name);
  }
  re_render_settings() {
    if (typeof this.opts.re_render_settings === "function") this.opts.re_render_settings();
    else console.warn("re_render_settings is not a function (must be passed in model opts)");
  }
  /**
   * Reload model.
   */
  reload_model() {
    if (typeof this.opts.reload_model === "function") this.opts.reload_model();
    else console.warn("reload_model is not a function (must be passed in model opts)");
  }
  adapter_changed() {
    this.reload_model();
    this.re_render_settings();
  }
  model_changed() {
    this.reload_model();
    this.re_render_settings();
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-embed-model/smart_embed_model.js
var SmartEmbedModel = class extends SmartModel {
  scope_name = "smart_embed_model";
  static defaults = {
    adapter: "transformers"
  };
  /**
   * Create a SmartEmbedModel instance
   * @param {Object} opts - Configuration options
   * @param {Object} [opts.adapters] - Map of available adapter implementations
   * @param {number} [opts.batch_size] - Default batch size for processing
   * @param {Object} [opts.settings] - User settings
   * @param {string} [opts.settings.api_key] - API key for remote models
   * @param {number} [opts.settings.min_chars] - Minimum text length to embed
   */
  constructor(opts = {}) {
    super(opts);
  }
  /**
   * Count tokens in an input string
   * @param {string} input - Text to tokenize
   * @returns {Promise<Object>} Token count result
   * @property {number} tokens - Number of tokens in input
   * 
   * @example
   * ```javascript
   * const result = await model.count_tokens("Hello world");
   * console.log(result.tokens); // 2
   * ```
   */
  async count_tokens(input) {
    return await this.invoke_adapter_method("count_tokens", input);
  }
  /**
   * Generate embeddings for a single input
   * @param {string|Object} input - Text or object with embed_input property
   * @returns {Promise<Object>} Embedding result
   * @property {number[]} vec - Embedding vector
   * @property {number} tokens - Token count
   * 
   * @example
   * ```javascript
   * const result = await model.embed("Hello world");
   * console.log(result.vec); // [0.1, 0.2, ...]
   * ```
   */
  async embed(input) {
    if (typeof input === "string") input = { embed_input: input };
    return (await this.embed_batch([input]))[0];
  }
  /**
   * Generate embeddings for multiple inputs in batch
   * @param {Array<string|Object>} inputs - Array of texts or objects with embed_input
   * @returns {Promise<Array<Object>>} Array of embedding results
   * @property {number[]} vec - Embedding vector for each input
   * @property {number} tokens - Token count for each input
   * 
   * @example
   * ```javascript
   * const results = await model.embed_batch([
   *   { embed_input: "First text" },
   *   { embed_input: "Second text" }
   * ]);
   * ```
   */
  async embed_batch(inputs) {
    return await this.invoke_adapter_method("embed_batch", inputs);
  }
  /**
   * Get the current batch size based on GPU settings
   * @returns {number} Current batch size for processing
   */
  get batch_size() {
    return this.adapter.batch_size || 1;
  }
  /**
   * Get settings configuration schema
   * @returns {Object} Settings configuration object
   */
  get settings_config() {
    const _settings_config = {
      adapter: {
        name: "Embedding model platform",
        type: "dropdown",
        description: "Select an embedding model platform. The default 'transformers' utilizes built-in local models.",
        options_callback: "get_platforms_as_options",
        callback: "adapter_changed",
        default: this.constructor.defaults.adapter
      },
      ...this.adapter.settings_config || {}
    };
    return this.process_settings_config(_settings_config);
  }
  process_setting_key(key) {
    return key.replace(/\[ADAPTER\]/g, this.adapter_name);
  }
  /**
   * Get available embedding model options
   * @returns {Array<Object>} Array of model options with value and name
   */
  get_embedding_model_options() {
    return Object.entries(this.models).map(([key, model]) => ({ value: key, name: key }));
  }
  // /**
  //  * Get embedding model options including 'None' option
  //  * @returns {Array<Object>} Array of model options with value and name
  //  */
  // get_block_embedding_model_options() {
  //   const options = this.get_embedding_model_options();
  //   options.unshift({ value: 'None', name: 'None' });
  //   return options;
  // }
};

// node_modules/obsidian-smart-env/node_modules/smart-model/adapters/_adapter.js
var SmartModelAdapter = class {
  /**
   * Create a SmartModelAdapter instance.
   * @param {SmartModel} model - The parent SmartModel instance
   */
  constructor(model) {
    this.model = model;
    this.state = "unloaded";
  }
  /**
   * Load the adapter.
   * @async
   * @returns {Promise<void>}
   */
  async load() {
    this.set_state("loaded");
  }
  /**
   * Unload the adapter.
   * @returns {void}
   */
  unload() {
    this.set_state("unloaded");
  }
  /**
   * Get all settings.
   * @returns {Object} All settings
   */
  get settings() {
    return this.model.settings;
  }
  /**
   * Get the current model key.
   * @returns {string} Current model identifier
   */
  get model_key() {
    return this.model.model_key;
  }
  /**
   * Get the models.
   * @returns {Object} Map of model objects
   */
  get models() {
    const models = this.model.data.provider_models;
    if (typeof models === "object" && Object.keys(models || {}).length > 0) return models;
    else {
      return {};
    }
  }
  /**
   * Get available models from the API.
   * @abstract
   * @param {boolean} [refresh=false] - Whether to refresh cached models
   * @returns {Promise<Object>} Map of model objects
   */
  async get_models(refresh = false) {
    throw new Error("get_models not implemented");
  }
  /**
   * Get available models as dropdown options synchronously.
   * @returns {Array<Object>} Array of model options.
   */
  get_models_as_options() {
    const models = this.models;
    if (!Object.keys(models || {}).length) {
      this.get_models(true);
      return [{ value: "", name: "No models currently available" }];
    }
    return Object.entries(models).map(([id, model]) => ({ value: id, name: model.name || id })).sort((a, b) => a.name.localeCompare(b.name));
  }
  /**
   * Set the adapter's state.
   * @deprecated should be handled in SmartModel (only handle once)
   * @param {('unloaded'|'loading'|'loaded'|'unloading')} new_state - The new state
   * @throws {Error} If the state is invalid
   */
  set_state(new_state) {
    const valid_states = ["unloaded", "loading", "loaded", "unloading"];
    if (!valid_states.includes(new_state)) {
      throw new Error(`Invalid state: ${new_state}`);
    }
    this.state = new_state;
  }
  // Replace individual state getters/setters with a unified state management
  get is_loading() {
    return this.state === "loading";
  }
  get is_loaded() {
    return this.state === "loaded";
  }
  get is_unloading() {
    return this.state === "unloading";
  }
  get is_unloaded() {
    return this.state === "unloaded";
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-embed-model/adapters/_adapter.js
var SmartEmbedAdapter = class extends SmartModelAdapter {
  /**
   * @override in sub-class with adapter-specific default configurations
   * @property {string} id - The adapter identifier
   * @property {string} description - Human-readable description
   * @property {string} type - Adapter type ("API")
   * @property {string} endpoint - API endpoint
   * @property {string} adapter - Adapter identifier
   * @property {string} default_model - Default model to use
   */
  static defaults = {};
  /**
   * Count tokens in input text
   * @abstract
   * @param {string} input - Text to tokenize
   * @returns {Promise<Object>} Token count result
   * @property {number} tokens - Number of tokens in input
   * @throws {Error} If not implemented by subclass
   */
  async count_tokens(input) {
    throw new Error("count_tokens method not implemented");
  }
  /**
   * Generate embeddings for single input
   * @abstract
   * @param {string|Object} input - Text to embed
   * @returns {Promise<Object>} Embedding result
   * @property {number[]} vec - Embedding vector
   * @property {number} tokens - Number of tokens in input
   * @throws {Error} If not implemented by subclass
   */
  async embed(input) {
    if (typeof input === "string") input = { embed_input: input };
    return (await this.embed_batch([input]))[0];
  }
  /**
   * Generate embeddings for multiple inputs
   * @abstract
   * @param {Array<string|Object>} inputs - Texts to embed
   * @returns {Promise<Array<Object>>} Array of embedding results
   * @property {number[]} vec - Embedding vector for each input
   * @property {number} tokens - Number of tokens in each input
   * @throws {Error} If not implemented by subclass
   */
  async embed_batch(inputs) {
    throw new Error("embed_batch method not implemented");
  }
  get settings_config() {
    return {
      "[ADAPTER].model_key": {
        name: "Embedding model",
        type: "dropdown",
        description: "Select an embedding model.",
        options_callback: "adapter.get_models_as_options",
        callback: "model_changed",
        default: this.constructor.defaults.default_model
      }
    };
  }
  get dims() {
    return this.model.data.dims;
  }
  get max_tokens() {
    return this.model.data.max_tokens;
  }
  get batch_size() {
    return this.model.data.batch_size || 1;
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-http-request/smart_http_request.js
var SmartHttpRequest = class {
  /**
   * @param {object} opts - Options for the SmartHttpRequest class
   * @param {SmartHttpRequestAdapter} opts.adapter - The adapter constructor to use for making HTTP requests
   * @param {Obsidian.requestUrl} opts.obsidian_request_adapter - For use with Obsidian adapter
   */
  constructor(opts = {}) {
    this.opts = opts;
    if (!opts.adapter) throw new Error("HttpRequestAdapter is required");
    this.adapter = new opts.adapter(this);
  }
  /**
   * Returns a well-formed response object
   * @param {object} request_params - Parameters for the HTTP request
   * @param {string} request_params.url - The URL to make the request to
   * @param {string} [request_params.method='GET'] - The HTTP method to use
   * @param {object} [request_params.headers] - Headers to include in the request
   * @param {*} [request_params.body] - The body of the request (for POST, PUT, etc.)
   * @returns {SmartHttpResponseAdapter} instance of the SmartHttpResponseAdapter class
   * @example
   * const response = await smart_http_request.request({
   *   url: 'https://api.example.com/data',
   *   method: 'GET',
   *   headers: { 'Content-Type': 'application/json' }
   * });
   * console.log(await response.json());
   */
  async request(request_params, throw_on_error = false) {
    return await this.adapter.request(request_params, throw_on_error);
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-http-request/adapters/_adapter.js
var SmartHttpRequestAdapter = class {
  constructor(main) {
    this.main = main;
  }
  /**
   * Execute an HTTP request using adapter-specific transport.
   * @abstract
   * @param {Object} request_params - Parameters for the outbound request.
   * @returns {Promise<SmartHttpResponseAdapter>} Adapter-specific response wrapper.
   */
  async request(request_params) {
    throw new Error("request not implemented");
  }
};
var SmartHttpResponseAdapter = class {
  constructor(response) {
    this.response = response;
  }
  /**
   * Retrieve response headers.
   * @abstract
   * @returns {Promise<Object>} Headers object for the response.
   */
  async headers() {
    throw new Error("headers not implemented");
  }
  /**
   * Parse the response body as JSON.
   * @abstract
   * @returns {Promise<*>} Parsed JSON payload.
   */
  async json() {
    throw new Error("json not implemented");
  }
  /**
   * Get the HTTP status code.
   * @abstract
   * @returns {Promise<number>} Response status code.
   */
  async status() {
    throw new Error("status not implemented");
  }
  /**
   * Read the raw text body.
   * @abstract
   * @returns {Promise<string>} Response body as text.
   */
  async text() {
    throw new Error("text not implemented");
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-http-request/adapters/obsidian.js
var SmartHttpObsidianRequestAdapter = class extends SmartHttpRequestAdapter {
  async request(request_params, throw_on_error = false) {
    let response;
    try {
      if (!this.main.opts.obsidian_request_url) {
        throw new Error("obsidian_request_url is required in SmartHttp constructor opts");
      }
      response = await this.main.opts.obsidian_request_url({ ...request_params, throw: throw_on_error });
      if (throw_on_error && response.status === 400) throw new Error("Obsidian request failed");
      return new SmartHttpObsidianResponseAdapter(response);
    } catch (error) {
      console.error("Error in SmartHttpObsidianRequestAdapter.request():");
      console.error(JSON.stringify(request_params, null, 2));
      console.error(response);
      console.error(error);
      return null;
    }
  }
};
var SmartHttpObsidianResponseAdapter = class extends SmartHttpResponseAdapter {
  async status() {
    return this.response.status;
  }
  async json() {
    return await this.response.json;
  }
  async text() {
    return await this.response.text;
  }
  async headers() {
    return this.response.headers;
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-http-request/adapters/fetch.js
var SmartHttpRequestFetchAdapter = class extends SmartHttpRequestAdapter {
  async request(request_params) {
    const { url, ...opts } = request_params;
    const resp = await fetch(url, opts);
    return new SmartHttpResponseFetchAdapter(resp);
  }
};
var SmartHttpResponseFetchAdapter = class extends SmartHttpResponseAdapter {
  async headers() {
    return this.response.headers;
  }
  async json() {
    if (!this._json) {
      this._json = await this.response.json();
    }
    return this._json;
  }
  async status() {
    return this.response.status;
  }
  async text() {
    if (!this._text) {
      this._text = await this.response.text();
    }
    return this._text;
  }
};

// node_modules/obsidian-smart-env/node_modules/js-tiktoken/dist/chunk-ZDNLBERF.js
var import_base64_js = __toESM(require_base64_js(), 1);
var __defProp2 = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
  __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
function bytePairMerge(piece, ranks) {
  let parts = Array.from(
    { length: piece.length },
    (_, i) => ({ start: i, end: i + 1 })
  );
  while (parts.length > 1) {
    let minRank = null;
    for (let i = 0; i < parts.length - 1; i++) {
      const slice = piece.slice(parts[i].start, parts[i + 1].end);
      const rank = ranks.get(slice.join(","));
      if (rank == null)
        continue;
      if (minRank == null || rank < minRank[0]) {
        minRank = [rank, i];
      }
    }
    if (minRank != null) {
      const i = minRank[1];
      parts[i] = { start: parts[i].start, end: parts[i + 1].end };
      parts.splice(i + 1, 1);
    } else {
      break;
    }
  }
  return parts;
}
function bytePairEncode(piece, ranks) {
  if (piece.length === 1)
    return [ranks.get(piece.join(","))];
  return bytePairMerge(piece, ranks).map((p) => ranks.get(piece.slice(p.start, p.end).join(","))).filter((x) => x != null);
}
function escapeRegex(str) {
  return str.replace(/[\\^$*+?.()|[\]{}]/g, "\\$&");
}
var _Tiktoken = class {
  /** @internal */
  specialTokens;
  /** @internal */
  inverseSpecialTokens;
  /** @internal */
  patStr;
  /** @internal */
  textEncoder = new TextEncoder();
  /** @internal */
  textDecoder = new TextDecoder("utf-8");
  /** @internal */
  rankMap = /* @__PURE__ */ new Map();
  /** @internal */
  textMap = /* @__PURE__ */ new Map();
  constructor(ranks, extendedSpecialTokens) {
    this.patStr = ranks.pat_str;
    const uncompressed = ranks.bpe_ranks.split("\n").filter(Boolean).reduce((memo, x) => {
      const [_, offsetStr, ...tokens] = x.split(" ");
      const offset = Number.parseInt(offsetStr, 10);
      tokens.forEach((token, i) => memo[token] = offset + i);
      return memo;
    }, {});
    for (const [token, rank] of Object.entries(uncompressed)) {
      const bytes = import_base64_js.default.toByteArray(token);
      this.rankMap.set(bytes.join(","), rank);
      this.textMap.set(rank, bytes);
    }
    this.specialTokens = { ...ranks.special_tokens, ...extendedSpecialTokens };
    this.inverseSpecialTokens = Object.entries(this.specialTokens).reduce((memo, [text, rank]) => {
      memo[rank] = this.textEncoder.encode(text);
      return memo;
    }, {});
  }
  encode(text, allowedSpecial = [], disallowedSpecial = "all") {
    const regexes = new RegExp(this.patStr, "ug");
    const specialRegex = _Tiktoken.specialTokenRegex(
      Object.keys(this.specialTokens)
    );
    const ret = [];
    const allowedSpecialSet = new Set(
      allowedSpecial === "all" ? Object.keys(this.specialTokens) : allowedSpecial
    );
    const disallowedSpecialSet = new Set(
      disallowedSpecial === "all" ? Object.keys(this.specialTokens).filter(
        (x) => !allowedSpecialSet.has(x)
      ) : disallowedSpecial
    );
    if (disallowedSpecialSet.size > 0) {
      const disallowedSpecialRegex = _Tiktoken.specialTokenRegex([
        ...disallowedSpecialSet
      ]);
      const specialMatch = text.match(disallowedSpecialRegex);
      if (specialMatch != null) {
        throw new Error(
          `The text contains a special token that is not allowed: ${specialMatch[0]}`
        );
      }
    }
    let start = 0;
    while (true) {
      let nextSpecial = null;
      let startFind = start;
      while (true) {
        specialRegex.lastIndex = startFind;
        nextSpecial = specialRegex.exec(text);
        if (nextSpecial == null || allowedSpecialSet.has(nextSpecial[0]))
          break;
        startFind = nextSpecial.index + 1;
      }
      const end = nextSpecial?.index ?? text.length;
      for (const match of text.substring(start, end).matchAll(regexes)) {
        const piece = this.textEncoder.encode(match[0]);
        const token2 = this.rankMap.get(piece.join(","));
        if (token2 != null) {
          ret.push(token2);
          continue;
        }
        ret.push(...bytePairEncode(piece, this.rankMap));
      }
      if (nextSpecial == null)
        break;
      let token = this.specialTokens[nextSpecial[0]];
      ret.push(token);
      start = nextSpecial.index + nextSpecial[0].length;
    }
    return ret;
  }
  decode(tokens) {
    const res = [];
    let length = 0;
    for (let i2 = 0; i2 < tokens.length; ++i2) {
      const token = tokens[i2];
      const bytes = this.textMap.get(token) ?? this.inverseSpecialTokens[token];
      if (bytes != null) {
        res.push(bytes);
        length += bytes.length;
      }
    }
    const mergedArray = new Uint8Array(length);
    let i = 0;
    for (const bytes of res) {
      mergedArray.set(bytes, i);
      i += bytes.length;
    }
    return this.textDecoder.decode(mergedArray);
  }
};
var Tiktoken = _Tiktoken;
__publicField(Tiktoken, "specialTokenRegex", (tokens) => {
  return new RegExp(tokens.map((i) => escapeRegex(i)).join("|"), "g");
});

// node_modules/obsidian-smart-env/node_modules/smart-embed-model/utils/fetch_cache.js
async function fetch_json_cached(url, cache_key = url) {
  const is_browser = typeof window !== "undefined" && typeof window.document !== "undefined";
  if (is_browser) {
    const cached_text = window.localStorage.getItem(cache_key);
    if (cached_text) return JSON.parse(cached_text);
    const remote2 = await do_fetch(url);
    window.localStorage.setItem(cache_key, JSON.stringify(remote2));
    return remote2;
  }
  const fs = await import("node:fs/promises");
  const path = await import("node:path");
  const os = await import("node:os");
  const cache_dir = path.join(os.homedir(), ".cache", "smart-embed-model");
  const cache_file = path.join(cache_dir, cache_key);
  try {
    const txt = await fs.readFile(cache_file, "utf8");
    return JSON.parse(txt);
  } catch {
  }
  const remote = await do_fetch(url);
  await fs.mkdir(cache_dir, { recursive: true });
  await fs.writeFile(cache_file, JSON.stringify(remote), "utf8");
  return remote;
}
async function do_fetch(url) {
  const resp = await fetch(url);
  if (!resp.ok) throw new Error(`failed to download ${url} \u2013 ${resp.status}`);
  return await resp.json();
}

// node_modules/obsidian-smart-env/node_modules/smart-utils/normalize_error.js
function is_json_compatible(value) {
  if (value === null) {
    return true;
  }
  const type = typeof value;
  if (type === "string" || type === "number" || type === "boolean") {
    return true;
  }
  if (Array.isArray(value)) {
    return value.every(is_json_compatible);
  }
  if (type === "object") {
    const obj = (
      /** @type {Record<string, unknown>} */
      value
    );
    return Object.values(obj).every(is_json_compatible);
  }
  return false;
}
function extract_json_details(source, exclude_keys) {
  const details = {};
  for (const [key, value] of Object.entries(source)) {
    if (exclude_keys.includes(key)) {
      continue;
    }
    if (!is_json_compatible(value)) {
      continue;
    }
    details[key] = value;
  }
  return details;
}
function is_empty_object(obj) {
  return Object.keys(obj).length === 0;
}
function merge_details(first, second) {
  if (is_empty_object(first)) {
    return second;
  }
  if (is_empty_object(second)) {
    return first;
  }
  return { ...first, ...second };
}
function get_message_from_object(value) {
  const raw = value.message;
  if (typeof raw === "string") {
    const trimmed = raw.trim();
    if (trimmed.length > 0) {
      return trimmed;
    }
  }
  return null;
}
function normalize_error(error, http_status = null) {
  if (Array.isArray(error) && error.length > 0) {
    return normalize_error(error[0], http_status);
  }
  if (error == null) {
    return { message: "Unknown error", details: null, http_status };
  }
  if (typeof error === "string") {
    return { message: error, details: null, http_status };
  }
  if (error instanceof Error) {
    const message = (error.message || "").trim() || "Unknown error";
    const extra_details = extract_json_details(
      /** @type {Record<string, unknown>} */
      error,
      ["message"]
    );
    return {
      message,
      details: is_empty_object(extra_details) ? null : extra_details,
      http_status
    };
  }
  if (typeof error === "object") {
    const obj = (
      /** @type {Record<string, unknown>} */
      error
    );
    if ("error" in obj && obj.error != null) {
      const nested_error = obj.error;
      if (typeof nested_error === "object") {
        const nested_obj = (
          /** @type {Record<string, unknown>} */
          nested_error
        );
        const nested_message = get_message_from_object(nested_obj);
        const nested_details = extract_json_details(nested_obj, ["message"]);
        const outer_details = extract_json_details(obj, ["message", "error"]);
        const combined_details = merge_details(outer_details, nested_details);
        const message = nested_message || get_message_from_object(obj) || "Unknown error";
        return {
          message,
          details: is_empty_object(combined_details) ? null : combined_details,
          http_status
        };
      }
      return normalize_error(nested_error);
    }
    const object_message = get_message_from_object(obj);
    if (object_message) {
      const details = extract_json_details(obj, ["message"]);
      return {
        message: object_message,
        details: is_empty_object(details) ? null : details,
        http_status
      };
    }
  }
  return { message: "Unknown error", details: null, http_status };
}

// node_modules/obsidian-smart-env/node_modules/smart-embed-model/adapters/_api.js
var CL100K_URL = "https://raw.githubusercontent.com/brianpetro/jsbrains/refs/heads/main/smart-embed-model/cl100k_base.json";
var SmartEmbedModelApiAdapter = class extends SmartEmbedAdapter {
  /**
   * Get the request adapter class.
   * @returns {SmartEmbedModelRequestAdapter} The request adapter class
   */
  get req_adapter() {
    return SmartEmbedModelRequestAdapter;
  }
  /**
   * Get the response adapter class.
   * @returns {SmartEmbedModelResponseAdapter} The response adapter class
   */
  get res_adapter() {
    return SmartEmbedModelResponseAdapter;
  }
  /** @returns {string} API endpoint URL */
  get endpoint() {
    return this.model.data.endpoint;
  }
  /**
   * Get HTTP request adapter instance
   * @returns {SmartHttpRequest} HTTP request handler
   */
  get http_adapter() {
    if (!this._http_adapter) {
      if (this.model.opts.http_adapter)
        this._http_adapter = this.model.opts.http_adapter;
      else
        this._http_adapter = new SmartHttpRequest({
          adapter: SmartHttpRequestFetchAdapter
        });
    }
    return this._http_adapter;
  }
  /**
   * Get API key for authentication
   * @returns {string} API key
   */
  get api_key() {
    return this.model.data.api_key;
  }
  /**
   * Count tokens in input text
   * @abstract
   * @param {string} input - Text to tokenize
   * @returns {Promise<Object>} Token count result
   * @throws {Error} If not implemented by subclass
   */
  async count_tokens(input) {
    throw new Error("count_tokens not implemented");
  }
  /**
   * Estimate token count for input text
   * Uses character-based estimation (3.7 chars per token)
   * @param {string|Object} input - Input to estimate tokens for
   * @returns {number} Estimated token count
   */
  estimate_tokens(input) {
    if (typeof input === "object") input = JSON.stringify(input);
    return Math.ceil(input.length / 3.7);
  }
  /**
   * Process a batch of inputs for embedding
   * @param {Array<Object>} inputs - Array of input objects
   * @returns {Promise<Array<Object>>} Processed inputs with embeddings
   * @throws {Error} If API key is not set
   */
  async embed_batch(inputs) {
    if (!this.api_key) throw new Error("API key not set");
    inputs = inputs.filter((item) => item.embed_input?.length > 0);
    if (inputs.length === 0) {
      console.log("Empty batch (or all items have empty embed_input)");
      return [];
    }
    const embed_inputs = await Promise.all(
      inputs.map((item) => this.prepare_embed_input(item.embed_input))
    );
    const _req = new this.req_adapter(this, embed_inputs);
    const request_params = _req.to_platform();
    const resp = await this.request(request_params);
    if (!resp) {
      console.error("No response received for embedding request.");
      return [];
    }
    if (resp.error) return [resp];
    const _res = new this.res_adapter(this, resp);
    const embeddings = _res.to_openai();
    if (!embeddings) {
      console.error("Failed to parse embeddings.");
      return [];
    }
    return inputs.map((item, i) => {
      item.vec = embeddings[i].vec;
      item.tokens = embeddings[i].tokens;
      return item;
    });
  }
  /**
   * Prepare input text for embedding
   * @abstract
   * @param {string} embed_input - Raw input text
   * @returns {Promise<string>} Processed input text
   * @throws {Error} If not implemented by subclass
   */
  async prepare_embed_input(embed_input) {
    throw new Error("prepare_embed_input not implemented");
  }
  /**
   * Prepare request headers
   * @returns {Object} Headers object with authorization
   */
  prepare_request_headers() {
    let headers = {
      "Content-Type": "application/json"
    };
    if (this.api_key) {
      headers["Authorization"] = `Bearer ${this.api_key}`;
    }
    return headers;
  }
  /**
   * Make API request with retry logic
   * @param {Object} req - Request configuration
   * @param {number} [retries=0] - Number of retries attempted
   * @returns {Promise<Object>} API response
   */
  async request(req, retries = 0) {
    try {
      req.throw = false;
      const resp = await this.http_adapter.request({
        url: this.endpoint,
        ...req
      });
      const resp_json = await this.get_resp_json(resp);
      if (resp_json.error) {
        return { error: normalize_error(resp_json, resp.status()) };
      }
      return resp_json;
    } catch (error) {
      console.warn("Request error:", error);
      return await this.handle_request_err(error, req, retries);
    }
  }
  /**
   * Handle API request errors with retry logic
   * @param {Error|Object} error - Error object
   * @param {Object} req - Original request
   * @param {number} retries - Number of retries attempted
   * @returns {Promise<Object|null>} Retry response or null
   */
  async handle_request_err(error, req, retries) {
    if (error.status === 429 && retries < 3) {
      const backoff = Math.pow(retries + 1, 2);
      console.log(`Retrying request (429) in ${backoff} seconds...`);
      await new Promise((r) => setTimeout(r, 1e3 * backoff));
      return await this.request(req, retries + 1);
    }
    console.error(error);
    return null;
  }
  /**
   * Parse response body as JSON
   * @param {Response} resp - Response object
   * @returns {Promise<Object>} Parsed JSON
   */
  async get_resp_json(resp) {
    return typeof resp.json === "function" ? await resp.json() : await resp.json;
  }
  /**
   * Validate API key by making test request
   * @returns {Promise<boolean>} True if API key is valid
   */
  async validate_api_key() {
    const resp = await this.embed_batch([{ embed_input: "test" }]);
    return Array.isArray(resp) && resp.length > 0 && resp[0].vec !== null;
  }
  /**
   * Trim input text to satisfy `max_tokens`.
   * @param {string} embed_input - Input text
   * @param {number} tokens_ct - Existing token count
   * @returns {Promise<string|null>} Trimmed text
   */
  async trim_input_to_max_tokens(embed_input, tokens_ct) {
    const reduce_ratio = (tokens_ct - this.max_tokens) / tokens_ct;
    const new_length = Math.floor(embed_input.length * (1 - reduce_ratio));
    let trimmed_input = embed_input.slice(0, new_length);
    const last_space_index = trimmed_input.lastIndexOf(" ");
    if (last_space_index > 0) trimmed_input = trimmed_input.slice(0, last_space_index);
    const prepared = await this.prepare_embed_input(trimmed_input);
    if (prepared === null) return null;
    return prepared;
  }
  async load_tiktoken() {
    const cl100k_base = await fetch_json_cached(CL100K_URL, "cl100k_base.json");
    this.tiktoken = new Tiktoken(cl100k_base);
  }
};
var SmartEmbedModelRequestAdapter = class {
  /**
   * @constructor
   * @param {SmartEmbedModelApiAdapter} adapter - The SmartEmbedModelApiAdapter instance
   * @param {Array<string>} embed_inputs - The array of input texts
   */
  constructor(adapter, embed_inputs) {
    this.adapter = adapter;
    this.embed_inputs = embed_inputs;
  }
  get model_id() {
    return this.adapter.model.data.model_key;
  }
  get model_dims() {
    return this.adapter.model.data.dims;
  }
  /**
   * Get request headers
   * @returns {Object} Headers object
   */
  get_headers() {
    return this.adapter.prepare_request_headers();
  }
  /**
   * Convert request to platform-specific format
   * @returns {Object} Platform-specific request parameters
   */
  to_platform() {
    return {
      method: "POST",
      headers: this.get_headers(),
      body: JSON.stringify(this.prepare_request_body())
    };
  }
  /**
   * Prepare request body for API call
   * @abstract
   * @returns {Object} Request body object
   * @throws {Error} If not implemented by subclass
   */
  prepare_request_body() {
    throw new Error("prepare_request_body not implemented");
  }
};
var SmartEmbedModelResponseAdapter = class {
  /**
   * @constructor
   * @param {SmartEmbedModelApiAdapter} adapter - The SmartEmbedModelApiAdapter instance
   * @param {Object} response - The response object
   */
  constructor(adapter, response) {
    this.adapter = adapter;
    this.response = response;
  }
  /**
   * Convert response to standard format
   * @returns {Array<Object>} Array of embedding results
   */
  to_openai() {
    return this.parse_response();
  }
  /**
   * Parse API response
   * @abstract
   * @returns {Array<Object>} Parsed embedding results
   * @throws {Error} If not implemented by subclass
   */
  parse_response() {
    throw new Error("parse_response not implemented");
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-embed-model/adapters/openai.js
var SmartEmbedOpenAIAdapter = class extends SmartEmbedModelApiAdapter {
  static defaults = {
    adapter: "openai",
    description: "OpenAI (API)",
    default_model: "text-embedding-3-small",
    endpoint: "https://api.openai.com/v1/embeddings"
  };
  /**
   * Count tokens in input text using OpenAI's tokenizer
   * @param {string} input - Text to tokenize
   * @returns {Promise<Object>} Token count result
   */
  async count_tokens(input) {
    if (!this.tiktoken) await this.load_tiktoken();
    return { tokens: this.tiktoken.encode(input).length };
  }
  /**
   * Prepare input text for embedding
   * Handles token limit truncation
   * @param {string} embed_input - Raw input text
   * @returns {Promise<string|null>} Processed input text
   */
  async prepare_embed_input(embed_input) {
    if (typeof embed_input !== "string") {
      throw new TypeError("embed_input must be a string");
    }
    if (embed_input.length === 0) {
      console.log("Warning: prepare_embed_input received an empty string");
      return null;
    }
    const { tokens } = await this.count_tokens(embed_input);
    if (tokens <= this.max_tokens) {
      return embed_input;
    }
    return await this.trim_input_to_max_tokens(embed_input, tokens);
  }
  /**
   * Trim input text to fit token limit
   * @private
   * @param {string} embed_input - Input text to trim
   * @param {number} tokens_ct - Current token count
   * @returns {Promise<string|null>} Trimmed input text
   */
  async trim_input_to_max_tokens(embed_input, tokens_ct) {
    const reduce_ratio = (tokens_ct - this.max_tokens) / tokens_ct;
    const new_length = Math.floor(embed_input.length * (1 - reduce_ratio));
    let trimmed_input = embed_input.slice(0, new_length);
    const last_space_index = trimmed_input.lastIndexOf(" ");
    if (last_space_index > 0) {
      trimmed_input = trimmed_input.slice(0, last_space_index);
    }
    const prepared_input = await this.prepare_embed_input(trimmed_input);
    if (prepared_input === null) {
      console.log(
        "Warning: prepare_embed_input resulted in an empty string after trimming"
      );
      return null;
    }
    return prepared_input;
  }
  /**
   * Get the request adapter class.
   * @returns {SmartEmbedOpenAIRequestAdapter} The request adapter class
   */
  get req_adapter() {
    return SmartEmbedOpenAIRequestAdapter;
  }
  /**
   * Get the response adapter class.
   * @returns {SmartEmbedOpenAIResponseAdapter} The response adapter class
   */
  get res_adapter() {
    return SmartEmbedOpenAIResponseAdapter;
  }
  /** @returns {number} Maximum tokens per input */
  get max_tokens() {
    return this.model.data.max_tokens || 8191;
  }
  /** @returns {Object} Settings configuration for OpenAI adapter */
  get settings_config() {
    return {
      ...super.settings_config,
      "[ADAPTER].api_key": {
        name: "OpenAI API key for embeddings",
        type: "password",
        description: "Required for OpenAI embedding models.",
        placeholder: "Enter OpenAI API key"
      }
    };
  }
  /**
   * Get available models (hardcoded list)
   * @returns {Promise<Object>} Map of model objects
   */
  get_models() {
    return Promise.resolve(this.models);
  }
  get models() {
    return {
      "text-embedding-3-small": {
        "id": "text-embedding-3-small",
        "batch_size": 50,
        "dims": 1536,
        "max_tokens": 8191,
        "name": "OpenAI Text-3 Small",
        "description": "API, 8,191 tokens, 1,536 dim",
        "endpoint": "https://api.openai.com/v1/embeddings",
        "adapter": "openai"
      },
      "text-embedding-3-large": {
        "id": "text-embedding-3-large",
        "batch_size": 50,
        "dims": 3072,
        "max_tokens": 8191,
        "name": "OpenAI Text-3 Large",
        "description": "API, 8,191 tokens, 3,072 dim",
        "endpoint": "https://api.openai.com/v1/embeddings",
        "adapter": "openai"
      },
      // "text-embedding-3-small-512": {
      //   "id": "text-embedding-3-small",
      //   "batch_size": 50,
      //   "dims": 512,
      //   "max_tokens": 8191,
      //   "name": "OpenAI Text-3 Small - 512",
      //   "description": "API, 8,191 tokens, 512 dim",
      //   "endpoint": "https://api.openai.com/v1/embeddings",
      //   "adapter": "openai"
      // },
      // "text-embedding-3-large-256": {
      //   "id": "text-embedding-3-large",
      //   "batch_size": 50,
      //   "dims": 256,
      //   "max_tokens": 8191,
      //   "name": "OpenAI Text-3 Large - 256",
      //   "description": "API, 8,191 tokens, 256 dim",
      //   "endpoint": "https://api.openai.com/v1/embeddings",
      //   "adapter": "openai"
      // },
      "text-embedding-ada-002": {
        "id": "text-embedding-ada-002",
        "batch_size": 50,
        "dims": 1536,
        "max_tokens": 8191,
        "name": "OpenAI Ada",
        "description": "API, 8,191 tokens, 1,536 dim",
        "endpoint": "https://api.openai.com/v1/embeddings",
        "adapter": "openai"
      }
    };
  }
};
var SmartEmbedOpenAIRequestAdapter = class extends SmartEmbedModelRequestAdapter {
  /**
   * Prepare request body for OpenAI API
   * @returns {Object} Request body for API
   */
  prepare_request_body() {
    const body = {
      model: this.model_id,
      input: this.embed_inputs
    };
    if (this.model_id.startsWith("text-embedding-3")) {
      body.dimensions = this.model_dims;
    }
    return body;
  }
};
var SmartEmbedOpenAIResponseAdapter = class extends SmartEmbedModelResponseAdapter {
  /**
   * Parse OpenAI API response
   * @returns {Array<Object>} Parsed embedding results
   */
  parse_response() {
    const resp = this.response;
    if (!resp || !resp.data || !resp.usage) {
      console.error("Invalid response format", resp);
      return [];
    }
    const avg_tokens = resp.usage.total_tokens / resp.data.length;
    return resp.data.map((item) => ({
      vec: item.embedding,
      tokens: avg_tokens
      // OpenAI doesn't provide tokens per item in batch requests
    }));
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-embed-model/adapters/_message.js
var SmartEmbedMessageAdapter = class extends SmartEmbedAdapter {
  /**
   * Create message adapter instance
   */
  constructor(model) {
    super(model);
    this.message_queue = {};
    this.message_id = 0;
    this.connector = null;
    this.message_prefix = `msg_${Math.random().toString(36).substr(2, 9)}_`;
  }
  /**
   * Send message and wait for response
   * @protected
   * @param {string} method - Method name to call
   * @param {Object} params - Method parameters
   * @returns {Promise<any>} Response data
   */
  async _send_message(method, params) {
    return new Promise((resolve, reject) => {
      const id = `${this.message_prefix}${this.message_id++}`;
      this.message_queue[id] = { resolve, reject };
      try {
        this._post_message({ method, params, id });
      } catch (error) {
        delete this.message_queue[id];
        reject(error instanceof Error ? error : new Error(String(error || "Unknown error")));
      }
    });
  }
  unload() {
    const unload_error = new Error("Message adapter unloaded");
    Object.values(this.message_queue).forEach((queue_entry) => {
      if (!queue_entry) return;
      if (typeof this.clear_message_timeout === "function") {
        this.clear_message_timeout(queue_entry);
      }
      queue_entry.reject(unload_error);
    });
    this.message_queue = {};
    super.unload();
  }
  /**
   * Handle response message from worker/iframe
   * @protected
   * @param {string} id - Message ID
   * @param {*} result - Response result
   * @param {Error} [error] - Response error
   */
  _handle_message_result(id, result, error) {
    if (!id.startsWith(this.message_prefix)) return;
    if (result?.model_loaded) {
      console.log("model loaded");
      this.state = "loaded";
      this.model.model_loaded = true;
      this.model.load_result = result;
    }
    if (this.message_queue[id]) {
      if (error) {
        this.message_queue[id].reject(new Error(error));
      } else {
        this.message_queue[id].resolve(result);
      }
      delete this.message_queue[id];
    }
  }
  /**
   * Count tokens in input text
   * @param {string} input - Text to tokenize
   * @returns {Promise<Object>} Token count result
   */
  async count_tokens(input) {
    return this._send_message("count_tokens", { input });
  }
  /**
   * Generate embeddings for multiple inputs
   * @param {Array<Object>} inputs - Array of input objects
   * @returns {Promise<Array<Object>>} Processed inputs with embeddings
   */
  async embed_batch(inputs) {
    inputs = inputs.filter((item) => item.embed_input?.length > 0);
    if (!inputs.length) return [];
    const embed_inputs = inputs.map((item) => ({ embed_input: item.embed_input }));
    const result = await this._send_message("embed_batch", { inputs: embed_inputs });
    return inputs.map((item, i) => {
      const item_result = result[i] || {};
      if ("vec" in item_result) item.vec = item_result.vec;
      if ("tokens" in item_result) item.tokens = item_result.tokens;
      if ("error" in item_result) item.error = item_result.error;
      else delete item.error;
      return item;
    });
  }
  /**
   * Post message to worker/iframe
   * @abstract
   * @protected
   * @param {Object} message_data - Message to send
   * @throws {Error} If not implemented by subclass
   */
  _post_message(message_data) {
    throw new Error("_post_message must be implemented by subclass");
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-embed-model/adapters/iframe.js
var SmartEmbedIframeAdapter = class extends SmartEmbedMessageAdapter {
  /**
   * Create iframe adapter instance
   */
  constructor(model) {
    super(model);
    this.iframe = null;
    this.origin = window.location.origin;
    this.iframe_id = `smart_embed_iframe`;
    this._bound_handle_message = this._handle_message.bind(this);
  }
  /**
   * Initialize iframe and load model
   * @returns {Promise<void>}
   */
  async load() {
    this.unload();
    const existing_iframe = document.getElementById(this.iframe_id);
    if (existing_iframe) {
      existing_iframe.onload = null;
      existing_iframe.remove();
    }
    this.iframe = document.createElement("iframe");
    this.iframe.style.display = "none";
    this.iframe.id = this.iframe_id;
    document.body.appendChild(this.iframe);
    window.addEventListener("message", this._bound_handle_message);
    this.iframe.srcdoc = `
          <html>
            <body>
              <script type="module">
                ${this.connector}
                // Set up a message listener in the iframe
                window.addEventListener('message', async (event) => {
                    if (event.origin !== '${this.origin}' || event.data.iframe_id !== '${this.iframe_id}') return console.log('message ignored (listener)', event);
                    // Process the message and send the response back
                    const response = await process_message(event.data);
                    window.parent.postMessage({ ...response, iframe_id: '${this.iframe_id}' }, '${this.origin}');
                });
              </script>
            </body>
          </html>
        `;
    await new Promise((resolve) => this.iframe.onload = resolve);
    const load_opts = {
      // ...this.model.opts,
      model_key: this.model.model_key,
      adapters: null,
      // cannot clone classes
      settings: null,
      batch_size: this.batch_size,
      use_gpu: this.use_gpu
    };
    await this._send_message("load", load_opts);
    return new Promise((resolve) => {
      const check_model_loaded = () => {
        if (this.model.model_loaded) {
          resolve();
        } else {
          setTimeout(check_model_loaded, 100);
        }
      };
      check_model_loaded();
    });
  }
  /**
   * Detect expected cancellation caused by tearing down the iframe adapter
   * while a background load is in flight.
   * @param {Error|*} error
   * @returns {boolean}
   */
  is_unload_error(error) {
    return error?.message === "Message adapter unloaded";
  }
  /**
   * Start loading in the background and suppress only expected unload
   * cancellation from fire-and-forget call sites.
   * @returns {Promise<void>}
   */
  load_background() {
    if (this._load_background_promise) {
      return this._load_background_promise;
    }
    this._load_background_promise = Promise.resolve(this.load()).catch((error) => {
      if (this.is_unload_error(error)) return;
      console.error(`[${this.constructor.name}] load failed`, error);
    }).finally(() => {
      this._load_background_promise = null;
    });
    return this._load_background_promise;
  }
  unload() {
    window.removeEventListener("message", this._bound_handle_message);
    const iframe = this.iframe || document.getElementById(this.iframe_id);
    if (iframe) {
      iframe.onload = null;
      iframe.remove();
    }
    this.iframe = null;
    if (this.model) {
      this.model.model_loaded = false;
      this.model.load_result = null;
    }
    super.unload();
  }
  /**
   * Post message to iframe
   * @protected
   * @param {Object} message_data - Message to send
   */
  _post_message(message_data) {
    if (!this.iframe?.contentWindow) {
      throw new Error("Iframe not loaded");
    }
    this.iframe.contentWindow.postMessage({ ...message_data, iframe_id: this.iframe_id }, this.origin);
  }
  /**
   * Handle message from iframe
   * @private
   * @param {MessageEvent} event - Message event
   */
  _handle_message(event) {
    if (event.origin !== this.origin || event.data?.iframe_id !== this.iframe_id) return;
    if (event.source !== this.iframe?.contentWindow) return;
    const { id, result, error } = event.data;
    this._handle_message_result(id, result, error);
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-embed-model/connectors/transformers_iframe.js
var transformers_connector = 'var __defProp = Object.defineProperty;\nvar __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;\nvar __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);\n\n// ../smart-model/adapters/_adapter.js\nvar SmartModelAdapter = class {\n  /**\n   * Create a SmartModelAdapter instance.\n   * @param {SmartModel} model - The parent SmartModel instance\n   */\n  constructor(model2) {\n    this.model = model2;\n    this.state = "unloaded";\n  }\n  /**\n   * Load the adapter.\n   * @async\n   * @returns {Promise<void>}\n   */\n  async load() {\n    this.set_state("loaded");\n  }\n  /**\n   * Unload the adapter.\n   * @returns {void}\n   */\n  unload() {\n    this.set_state("unloaded");\n  }\n  /**\n   * Get all settings.\n   * @returns {Object} All settings\n   */\n  get settings() {\n    return this.model.settings;\n  }\n  /**\n   * Get the current model key.\n   * @returns {string} Current model identifier\n   */\n  get model_key() {\n    return this.model.model_key;\n  }\n  /**\n   * Get the models.\n   * @returns {Object} Map of model objects\n   */\n  get models() {\n    const models = this.model.data.provider_models;\n    if (typeof models === "object" && Object.keys(models || {}).length > 0) return models;\n    else {\n      return {};\n    }\n  }\n  /**\n   * Get available models from the API.\n   * @abstract\n   * @param {boolean} [refresh=false] - Whether to refresh cached models\n   * @returns {Promise<Object>} Map of model objects\n   */\n  async get_models(refresh = false) {\n    throw new Error("get_models not implemented");\n  }\n  /**\n   * Get available models as dropdown options synchronously.\n   * @returns {Array<Object>} Array of model options.\n   */\n  get_models_as_options() {\n    const models = this.models;\n    if (!Object.keys(models || {}).length) {\n      this.get_models(true);\n      return [{ value: "", name: "No models currently available" }];\n    }\n    return Object.entries(models).map(([id, model2]) => ({ value: id, name: model2.name || id })).sort((a, b) => a.name.localeCompare(b.name));\n  }\n  /**\n   * Set the adapter\'s state.\n   * @deprecated should be handled in SmartModel (only handle once)\n   * @param {(\'unloaded\'|\'loading\'|\'loaded\'|\'unloading\')} new_state - The new state\n   * @throws {Error} If the state is invalid\n   */\n  set_state(new_state) {\n    const valid_states = ["unloaded", "loading", "loaded", "unloading"];\n    if (!valid_states.includes(new_state)) {\n      throw new Error(`Invalid state: ${new_state}`);\n    }\n    this.state = new_state;\n  }\n  // Replace individual state getters/setters with a unified state management\n  get is_loading() {\n    return this.state === "loading";\n  }\n  get is_loaded() {\n    return this.state === "loaded";\n  }\n  get is_unloading() {\n    return this.state === "unloading";\n  }\n  get is_unloaded() {\n    return this.state === "unloaded";\n  }\n};\n\n// adapters/_adapter.js\nvar SmartEmbedAdapter = class extends SmartModelAdapter {\n  /**\n   * Count tokens in input text\n   * @abstract\n   * @param {string} input - Text to tokenize\n   * @returns {Promise<Object>} Token count result\n   * @property {number} tokens - Number of tokens in input\n   * @throws {Error} If not implemented by subclass\n   */\n  async count_tokens(input) {\n    throw new Error("count_tokens method not implemented");\n  }\n  /**\n   * Generate embeddings for single input\n   * @abstract\n   * @param {string|Object} input - Text to embed\n   * @returns {Promise<Object>} Embedding result\n   * @property {number[]} vec - Embedding vector\n   * @property {number} tokens - Number of tokens in input\n   * @throws {Error} If not implemented by subclass\n   */\n  async embed(input) {\n    if (typeof input === "string") input = { embed_input: input };\n    return (await this.embed_batch([input]))[0];\n  }\n  /**\n   * Generate embeddings for multiple inputs\n   * @abstract\n   * @param {Array<string|Object>} inputs - Texts to embed\n   * @returns {Promise<Array<Object>>} Array of embedding results\n   * @property {number[]} vec - Embedding vector for each input\n   * @property {number} tokens - Number of tokens in each input\n   * @throws {Error} If not implemented by subclass\n   */\n  async embed_batch(inputs) {\n    throw new Error("embed_batch method not implemented");\n  }\n  get settings_config() {\n    return {\n      "[ADAPTER].model_key": {\n        name: "Embedding model",\n        type: "dropdown",\n        description: "Select an embedding model.",\n        options_callback: "adapter.get_models_as_options",\n        callback: "model_changed",\n        default: this.constructor.defaults.default_model\n      }\n    };\n  }\n  get dims() {\n    return this.model.data.dims;\n  }\n  get max_tokens() {\n    return this.model.data.max_tokens;\n  }\n  get batch_size() {\n    return this.model.data.batch_size || 1;\n  }\n};\n/**\n * @override in sub-class with adapter-specific default configurations\n * @property {string} id - The adapter identifier\n * @property {string} description - Human-readable description\n * @property {string} type - Adapter type ("API")\n * @property {string} endpoint - API endpoint\n * @property {string} adapter - Adapter identifier\n * @property {string} default_model - Default model to use\n */\n__publicField(SmartEmbedAdapter, "defaults", {});\n\n// adapters/transformers.js\nvar transformers_defaults = {\n  adapter: "transformers",\n  description: "Transformers (Local, built-in)",\n  default_model: "TaylorAI/bge-micro-v2",\n  models: transformers_models\n};\nvar DEVICE_CONFIGS = {\n  // // WebGPU: high quality first\n  webgpu_fp16: {\n    device: "webgpu",\n    dtype: "fp16",\n    quantized: false\n  },\n  webgpu_fp32: {\n    device: "webgpu",\n    dtype: "fp32",\n    quantized: false\n  },\n  // WebGPU: quantized tiers\n  webgpu_q8: {\n    device: "webgpu",\n    dtype: "q8",\n    quantized: true\n  },\n  webgpu_q4: {\n    device: "webgpu",\n    dtype: "q4",\n    quantized: true\n  },\n  // Optional, if you use it\n  webgpu_q4f16: {\n    device: "webgpu",\n    dtype: "q4f16",\n    quantized: true\n  },\n  webgpu_bnb4: {\n    device: "webgpu",\n    dtype: "bnb4",\n    quantized: true\n  },\n  // WASM: quantized CPU\n  wasm_q8: {\n    dtype: "q8",\n    quantized: true\n  },\n  wasm_q4: {\n    dtype: "q4",\n    quantized: true\n  },\n  // Final universal fallback: WASM CPU, dtype = auto\n  wasm_auto: {\n    // NOTE: leaving out device to avoid Linux issues with \'wasm\'\n    // transformers.js will pick CPU/WASM backend itself\n    quantized: false\n  }\n};\nvar is_webgpu_available = async () => {\n  if (!("gpu" in navigator)) return false;\n  const adapter = await navigator.gpu.requestAdapter();\n  if (!adapter) return false;\n  return true;\n};\nvar SmartEmbedTransformersAdapter = class extends SmartEmbedAdapter {\n  /**\n   * @param {import("../smart_embed_model.js").SmartEmbedModel} model\n   */\n  constructor(model2) {\n    super(model2);\n    this.pipeline = null;\n    this.tokenizer = null;\n    this.active_config_key = null;\n    this.has_gpu = false;\n  }\n  /**\n   * Load the underlying transformers pipeline with WebGPU \u2192 WASM fallback.\n   * @returns {Promise<void>}\n   */\n  async load() {\n    this.has_gpu = await is_webgpu_available();\n    try {\n      if (this.loading) {\n        console.warn("[Transformers v3] load already in progress, waiting...");\n        while (this.loading) {\n          await new Promise((resolve) => setTimeout(resolve, 100));\n        }\n      } else {\n        this.loading = true;\n        if (this.pipeline) {\n          this.loaded = true;\n          this.loading = false;\n          return;\n        }\n        await this.load_transformers_with_fallback();\n        this.loading = false;\n        this.loaded = true;\n        console.log(`[Transformers v3] model loaded using ${this.active_config_key}`, this);\n      }\n    } catch (e) {\n      this.loading = false;\n      this.loaded = false;\n      console.error("[Transformers v3] load failed", e);\n      throw e;\n    }\n  }\n  /**\n   * Unload the pipeline and free resources.\n   * @returns {Promise<void>}\n   */\n  async unload() {\n    try {\n      if (this.pipeline) {\n        if (typeof this.pipeline.destroy === "function") {\n          this.pipeline.destroy();\n        } else if (typeof this.pipeline.dispose === "function") {\n          this.pipeline.dispose();\n        }\n      }\n    } catch (err) {\n      console.warn("[Transformers v3] error while disposing pipeline", err);\n    }\n    this.pipeline = null;\n    this.tokenizer = null;\n    this.active_config_key = null;\n    this.loaded = false;\n  }\n  /**\n   * Available models \u2013 reuses the v1 transformers model catalog.\n   * @returns {Object}\n   */\n  get models() {\n    return transformers_models;\n  }\n  /**\n   * Maximum tokens per input.\n   * @returns {number}\n   */\n  get max_tokens() {\n    return this.model.data.max_tokens || 512;\n  }\n  /**\n   * Effective batch size.\n   * Prefers small deterministic batches when not explicitly configured.\n   * @returns {number}\n   */\n  get batch_size() {\n    const configured = this.model.data.batch_size;\n    if (configured && configured > 0) return configured;\n    return this.gpu_enabled ? 16 : 8;\n  }\n  get gpu_enabled() {\n    if (this.has_gpu) {\n      const explicit = typeof this.model.data.use_gpu === "boolean" ? this.model.data.use_gpu : null;\n      if (explicit === false) return false;\n      return true;\n    } else {\n      return false;\n    }\n  }\n  /**\n   * Initialize transformers pipeline with WebGPU \u2192 WASM fallback.\n   * @private\n   * @returns {Promise<void>}\n   */\n  async load_transformers_with_fallback() {\n    const { pipeline, env, AutoTokenizer } = await import("@huggingface/transformers");\n    env.allowLocalModels = false;\n    if (typeof env.useBrowserCache !== "undefined") {\n      env.useBrowserCache = true;\n    }\n    let last_error = null;\n    const CONFIG_LIST_ORDER = Object.keys(DEVICE_CONFIGS);\n    const try_create = async (config_key) => {\n      const pipe = await pipeline("feature-extraction", this.model_key, DEVICE_CONFIGS[config_key]);\n      return pipe;\n    };\n    for (const config of CONFIG_LIST_ORDER) {\n      if (this.pipeline) break;\n      if (config.includes("gpu") && !this.gpu_enabled) {\n        console.warn(`[Transformers v3: ${config}] skipping ${config} as GPU is disabled`);\n        continue;\n      }\n      try {\n        console.log(`[Transformers v3] trying to load pipeline on ${config}`);\n        this.pipeline = await try_create(config);\n        this.active_config_key = config;\n        break;\n      } catch (err) {\n        console.warn(`[Transformers v3: ${config}] failed to load pipeline on ${config}`, err);\n        last_error = err;\n      }\n    }\n    if (this.pipeline) {\n      console.log(`[Transformers v3: ${this.active_config_key}] pipeline initialized using ${this.active_config_key}`);\n    } else {\n      throw last_error || new Error("Failed to initialize transformers pipeline");\n    }\n    this.tokenizer = await AutoTokenizer.from_pretrained(this.model_key);\n  }\n  /**\n   * Count tokens in input text.\n   * @param {string} input\n   * @returns {Promise<{tokens:number}>}\n   */\n  async count_tokens(input) {\n    if (!this.tokenizer) {\n      await this.load();\n    }\n    const { input_ids } = await this.tokenizer(input);\n    return { tokens: input_ids.data.length };\n  }\n  /**\n   * Generate embeddings for multiple inputs.\n   * @param {Array<Object>} inputs\n   * @returns {Promise<Array<Object>>}\n   */\n  async embed_batch(inputs) {\n    if (!this.pipeline) {\n      await this.load();\n    }\n    const filtered_inputs = inputs.filter((item) => item.embed_input && item.embed_input.length > 0);\n    if (!filtered_inputs.length) return [];\n    const results = [];\n    for (let i = 0; i < filtered_inputs.length; i += this.batch_size) {\n      const batch = filtered_inputs.slice(i, i + this.batch_size);\n      const batch_results = await this._process_batch(batch);\n      results.push(...batch_results);\n    }\n    return results;\n  }\n  /**\n   * Process a single batch \u2013 with per-item retry on failure.\n   * @private\n   * @param {Array<Object>} batch_inputs\n   * @returns {Promise<Array<Object>>}\n   */\n  async _process_batch(batch_inputs) {\n    const prepared = await Promise.all(\n      batch_inputs.map((item) => this._prepare_input(item.embed_input))\n    );\n    const embed_inputs = prepared.map((p) => p.text);\n    const tokens = prepared.map((p) => p.tokens);\n    try {\n      const resp = await this.pipeline(embed_inputs, { pooling: "mean", normalize: true });\n      return batch_inputs.map((item, i) => {\n        const vec = Array.from(resp[i].data).map((val) => Math.round(val * 1e8) / 1e8);\n        item.vec = vec;\n        item.tokens = tokens[i];\n        return item;\n      });\n    } catch (err) {\n      console.error("[Transformers v3] batch embed failed \\u2013 retrying items individually", err);\n      return await this._retry_items_individually(batch_inputs);\n    }\n  }\n  /**\n   * Prepare a single input by truncating to max_tokens if necessary.\n   * @private\n   * @param {string} embed_input\n   * @returns {Promise<{text:string,tokens:number}>}\n   */\n  async _prepare_input(embed_input) {\n    let { tokens } = await this.count_tokens(embed_input);\n    if (tokens <= this.max_tokens) {\n      return { text: embed_input, tokens };\n    }\n    let truncated = embed_input;\n    while (tokens > this.max_tokens && truncated.length > 0) {\n      const pct = this.max_tokens / tokens;\n      const max_chars = Math.floor(truncated.length * pct * 0.9);\n      truncated = truncated.slice(0, max_chars);\n      const last_space = truncated.lastIndexOf(" ");\n      if (last_space > 0) {\n        truncated = truncated.slice(0, last_space);\n      }\n      tokens = (await this.count_tokens(truncated)).tokens;\n    }\n    return { text: truncated, tokens };\n  }\n  /**\n   * Retry each item individually after a batch failure.\n   * @private\n   * @param {Array<Object>} batch_inputs\n   * @returns {Promise<Array<Object>>}\n   */\n  async _retry_items_individually(batch_inputs) {\n    await this._reset_pipeline_after_error();\n    const results = [];\n    for (const item of batch_inputs) {\n      try {\n        const prepared = await this._prepare_input(item.embed_input);\n        const resp = await this.pipeline(prepared.text, { pooling: "mean", normalize: true });\n        const vec = Array.from(resp[0].data).map((val) => Math.round(val * 1e8) / 1e8);\n        results.push({\n          ...item,\n          vec,\n          tokens: prepared.tokens\n        });\n      } catch (single_err) {\n        console.error("[Transformers v3] single item embed failed \\u2013 skipping", single_err);\n        results.push({\n          ...item,\n          vec: [],\n          tokens: 0,\n          error: single_err.message\n        });\n      }\n    }\n    return results;\n  }\n  /**\n   * Reset pipeline after a failure \u2013 falling back to WASM if needed.\n   * @private\n   * @returns {Promise<void>}\n   */\n  async _reset_pipeline_after_error() {\n    try {\n      if (this.pipeline) {\n        if (typeof this.pipeline.destroy === "function") {\n          this.pipeline.destroy();\n        } else if (typeof this.pipeline.dispose === "function") {\n          this.pipeline.dispose();\n        }\n      }\n    } catch (err) {\n      console.warn("[Transformers v3] error while resetting pipeline", err);\n    }\n    this.pipeline = null;\n    await this.load_transformers_with_fallback();\n  }\n  /**\n   * V2 intentionally exposes only model selection in the settings UI.\n   * @returns {Object}\n   */\n  get settings_config() {\n    return super.settings_config;\n  }\n};\n__publicField(SmartEmbedTransformersAdapter, "defaults", transformers_defaults);\nvar transformers_models = {\n  "TaylorAI/bge-micro-v2": {\n    "id": "TaylorAI/bge-micro-v2",\n    "batch_size": 1,\n    "dims": 384,\n    "max_tokens": 512,\n    "name": "BGE-micro-v2",\n    "description": "Local, 512 tokens, 384 dim (recommended)",\n    "adapter": "transformers"\n  },\n  "Snowflake/snowflake-arctic-embed-xs": {\n    "id": "Snowflake/snowflake-arctic-embed-xs",\n    "batch_size": 1,\n    "dims": 384,\n    "max_tokens": 512,\n    "name": "Snowflake Arctic Embed XS",\n    "description": "Local, 512 tokens, 384 dim",\n    "adapter": "transformers"\n  },\n  "Snowflake/snowflake-arctic-embed-s": {\n    "id": "Snowflake/snowflake-arctic-embed-s",\n    "batch_size": 1,\n    "dims": 384,\n    "max_tokens": 512,\n    "name": "Snowflake Arctic Embed Small",\n    "description": "Local, 512 tokens, 384 dim",\n    "adapter": "transformers"\n  },\n  "Snowflake/snowflake-arctic-embed-m": {\n    "id": "Snowflake/snowflake-arctic-embed-m",\n    "batch_size": 1,\n    "dims": 768,\n    "max_tokens": 512,\n    "name": "Snowflake Arctic Embed Medium",\n    "description": "Local, 512 tokens, 768 dim",\n    "adapter": "transformers"\n  },\n  "TaylorAI/gte-tiny": {\n    "id": "TaylorAI/gte-tiny",\n    "batch_size": 1,\n    "dims": 384,\n    "max_tokens": 512,\n    "name": "GTE-tiny",\n    "description": "Local, 512 tokens, 384 dim",\n    "adapter": "transformers"\n  },\n  "onnx-community/embeddinggemma-300m-ONNX": {\n    "id": "onnx-community/embeddinggemma-300m-ONNX",\n    "batch_size": 1,\n    "dims": 768,\n    "max_tokens": 2048,\n    "name": "EmbeddingGemma-300M",\n    "description": "Local, 2,048 tokens, 768 dim",\n    "adapter": "transformers"\n  },\n  "Mihaiii/Ivysaur": {\n    "id": "Mihaiii/Ivysaur",\n    "batch_size": 1,\n    "dims": 384,\n    "max_tokens": 512,\n    "name": "Ivysaur",\n    "description": "Local, 512 tokens, 384 dim",\n    "adapter": "transformers"\n  },\n  "andersonbcdefg/bge-small-4096": {\n    "id": "andersonbcdefg/bge-small-4096",\n    "batch_size": 1,\n    "dims": 384,\n    "max_tokens": 4096,\n    "name": "BGE-small-4K",\n    "description": "Local, 4,096 tokens, 384 dim",\n    "adapter": "transformers"\n  },\n  // Too slow and persistent crashes\n  // "jinaai/jina-embeddings-v2-base-de": {\n  //   "id": "jinaai/jina-embeddings-v2-base-de",\n  //   "batch_size": 1,\n  //   "dims": 768,\n  //   "max_tokens": 4096,\n  //   "name": "jina-embeddings-v2-base-de",\n  //   "description": "Local, 4,096 tokens, 768 dim, German",\n  //   "adapter": "transformers"\n  // },\n  "Xenova/jina-embeddings-v2-base-zh": {\n    "id": "Xenova/jina-embeddings-v2-base-zh",\n    "batch_size": 1,\n    "dims": 768,\n    "max_tokens": 8192,\n    "name": "Jina-v2-base-zh-8K",\n    "description": "Local, 8,192 tokens, 768 dim, Chinese/English bilingual",\n    "adapter": "transformers"\n  },\n  "Xenova/jina-embeddings-v2-small-en": {\n    "id": "Xenova/jina-embeddings-v2-small-en",\n    "batch_size": 1,\n    "dims": 512,\n    "max_tokens": 8192,\n    "name": "Jina-v2-small-en",\n    "description": "Local, 8,192 tokens, 512 dim",\n    "adapter": "transformers"\n  },\n  "nomic-ai/nomic-embed-text-v1.5": {\n    "id": "nomic-ai/nomic-embed-text-v1.5",\n    "batch_size": 1,\n    "dims": 768,\n    "max_tokens": 2048,\n    "name": "Nomic-embed-text-v1.5",\n    "description": "Local, 8,192 tokens, 768 dim",\n    "adapter": "transformers"\n  },\n  "Xenova/bge-small-en-v1.5": {\n    "id": "Xenova/bge-small-en-v1.5",\n    "batch_size": 1,\n    "dims": 384,\n    "max_tokens": 512,\n    "name": "BGE-small",\n    "description": "Local, 512 tokens, 384 dim",\n    "adapter": "transformers"\n  },\n  "nomic-ai/nomic-embed-text-v1": {\n    "id": "nomic-ai/nomic-embed-text-v1",\n    "batch_size": 1,\n    "dims": 768,\n    "max_tokens": 2048,\n    "name": "Nomic-embed-text",\n    "description": "Local, 2,048 tokens, 768 dim",\n    "adapter": "transformers"\n  }\n};\n\n// build/transformers_iframe_script.js\nvar model = null;\nasync function process_message(data) {\n  const { method, params, id, iframe_id } = data;\n  try {\n    let result;\n    switch (method) {\n      case "init":\n        console.log("init");\n        break;\n      case "load":\n        const model_params = { data: params, ...params };\n        console.log("load", { model_params });\n        model = new SmartEmbedTransformersAdapter(model_params);\n        await model.load();\n        result = { model_loaded: true, model_config_key: model.active_config_key };\n        break;\n      case "embed_batch":\n        if (!model) throw new Error("Model not loaded");\n        result = await model.embed_batch(params.inputs);\n        break;\n      case "count_tokens":\n        if (!model) throw new Error("Model not loaded");\n        result = await model.count_tokens(params);\n        break;\n      default:\n        throw new Error(`Unknown method: ${method}`);\n    }\n    return { id, result, iframe_id };\n  } catch (error) {\n    console.error("Error processing message:", error);\n    return { id, error: error.message, iframe_id };\n  }\n}\nprocess_message({ method: "init" });\n';

// node_modules/obsidian-smart-env/node_modules/smart-embed-model/adapters/transformers.js
var transformers_defaults = {
  adapter: "transformers",
  description: "Transformers (Local, built-in)",
  default_model: "TaylorAI/bge-micro-v2",
  models: transformers_models
};
var transformers_models = {
  "TaylorAI/bge-micro-v2": {
    "id": "TaylorAI/bge-micro-v2",
    "batch_size": 1,
    "dims": 384,
    "max_tokens": 512,
    "name": "BGE-micro-v2",
    "description": "Local, 512 tokens, 384 dim (recommended)",
    "adapter": "transformers"
  },
  "Snowflake/snowflake-arctic-embed-xs": {
    "id": "Snowflake/snowflake-arctic-embed-xs",
    "batch_size": 1,
    "dims": 384,
    "max_tokens": 512,
    "name": "Snowflake Arctic Embed XS",
    "description": "Local, 512 tokens, 384 dim",
    "adapter": "transformers"
  },
  "Snowflake/snowflake-arctic-embed-s": {
    "id": "Snowflake/snowflake-arctic-embed-s",
    "batch_size": 1,
    "dims": 384,
    "max_tokens": 512,
    "name": "Snowflake Arctic Embed Small",
    "description": "Local, 512 tokens, 384 dim",
    "adapter": "transformers"
  },
  "Snowflake/snowflake-arctic-embed-m": {
    "id": "Snowflake/snowflake-arctic-embed-m",
    "batch_size": 1,
    "dims": 768,
    "max_tokens": 512,
    "name": "Snowflake Arctic Embed Medium",
    "description": "Local, 512 tokens, 768 dim",
    "adapter": "transformers"
  },
  "TaylorAI/gte-tiny": {
    "id": "TaylorAI/gte-tiny",
    "batch_size": 1,
    "dims": 384,
    "max_tokens": 512,
    "name": "GTE-tiny",
    "description": "Local, 512 tokens, 384 dim",
    "adapter": "transformers"
  },
  "onnx-community/embeddinggemma-300m-ONNX": {
    "id": "onnx-community/embeddinggemma-300m-ONNX",
    "batch_size": 1,
    "dims": 768,
    "max_tokens": 2048,
    "name": "EmbeddingGemma-300M",
    "description": "Local, 2,048 tokens, 768 dim",
    "adapter": "transformers"
  },
  "Mihaiii/Ivysaur": {
    "id": "Mihaiii/Ivysaur",
    "batch_size": 1,
    "dims": 384,
    "max_tokens": 512,
    "name": "Ivysaur",
    "description": "Local, 512 tokens, 384 dim",
    "adapter": "transformers"
  },
  "andersonbcdefg/bge-small-4096": {
    "id": "andersonbcdefg/bge-small-4096",
    "batch_size": 1,
    "dims": 384,
    "max_tokens": 4096,
    "name": "BGE-small-4K",
    "description": "Local, 4,096 tokens, 384 dim",
    "adapter": "transformers"
  },
  // Too slow and persistent crashes
  // "jinaai/jina-embeddings-v2-base-de": {
  //   "id": "jinaai/jina-embeddings-v2-base-de",
  //   "batch_size": 1,
  //   "dims": 768,
  //   "max_tokens": 4096,
  //   "name": "jina-embeddings-v2-base-de",
  //   "description": "Local, 4,096 tokens, 768 dim, German",
  //   "adapter": "transformers"
  // },
  "Xenova/jina-embeddings-v2-base-zh": {
    "id": "Xenova/jina-embeddings-v2-base-zh",
    "batch_size": 1,
    "dims": 768,
    "max_tokens": 8192,
    "name": "Jina-v2-base-zh-8K",
    "description": "Local, 8,192 tokens, 768 dim, Chinese/English bilingual",
    "adapter": "transformers"
  },
  "Xenova/jina-embeddings-v2-small-en": {
    "id": "Xenova/jina-embeddings-v2-small-en",
    "batch_size": 1,
    "dims": 512,
    "max_tokens": 8192,
    "name": "Jina-v2-small-en",
    "description": "Local, 8,192 tokens, 512 dim",
    "adapter": "transformers"
  },
  "nomic-ai/nomic-embed-text-v1.5": {
    "id": "nomic-ai/nomic-embed-text-v1.5",
    "batch_size": 1,
    "dims": 768,
    "max_tokens": 2048,
    "name": "Nomic-embed-text-v1.5",
    "description": "Local, 8,192 tokens, 768 dim",
    "adapter": "transformers"
  },
  "Xenova/bge-small-en-v1.5": {
    "id": "Xenova/bge-small-en-v1.5",
    "batch_size": 1,
    "dims": 384,
    "max_tokens": 512,
    "name": "BGE-small",
    "description": "Local, 512 tokens, 384 dim",
    "adapter": "transformers"
  },
  "nomic-ai/nomic-embed-text-v1": {
    "id": "nomic-ai/nomic-embed-text-v1",
    "batch_size": 1,
    "dims": 768,
    "max_tokens": 2048,
    "name": "Nomic-embed-text",
    "description": "Local, 2,048 tokens, 768 dim",
    "adapter": "transformers"
  }
};
var transformers_settings_config = {
  // "[ADAPTER].legacy_transformers": {
  //   name: 'Legacy transformers (no GPU)',
  //   type: "toggle",
  //   description: "Use legacy transformers (v2) instead of v3. This may resolve issues if the local embedding isn't working.",
  //   callback: 'embed_model_changed',
  //   default: true,
  // },
};
var settings_config3 = {
  // "legacy_transformers": {
  //   name: 'Legacy transformers (no GPU)',
  //   type: "toggle",
  //   description: "Use legacy transformers (v2) instead of v3. This may resolve issues if the local embedding isn't working.",
  //   // callback: 'embed_model_changed',
  //   // default: false,
  // },
};

// node_modules/obsidian-smart-env/node_modules/smart-embed-model/adapters/transformers_iframe.js
var SmartEmbedTransformersIframeAdapter = class extends SmartEmbedIframeAdapter {
  static defaults = transformers_defaults;
  /**
   * Create transformers iframe adapter instance
   */
  constructor(model) {
    super(model);
    this.connector = transformers_connector.replace("@huggingface/transformers", "https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.8.0");
    console.log("transformers iframe connector", this.model);
  }
  /** @returns {Object} Settings configuration for transformers adapter */
  get settings_config() {
    return {
      ...super.settings_config,
      ...transformers_settings_config
    };
  }
  /**
   * Get available models (hardcoded list)
   * @returns {Promise<Object>} Map of model objects
   */
  get_models() {
    return Promise.resolve(this.models);
  }
  get models() {
    return transformers_models;
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-embed-model/adapters/ollama.js
var SmartEmbedOllamaAdapter = class extends SmartEmbedModelApiAdapter {
  static defaults = {
    description: "Ollama (Local)",
    type: "API",
    host: "http://localhost:11434",
    endpoint: "/api/embed",
    models_endpoint: "/api/tags",
    api_key: "na",
    // Not required for local instance
    streaming: false,
    // Ollama's embed API does not support streaming
    max_tokens: 512,
    // Example default, adjust based on model capabilities
    signup_url: null,
    // Not applicable for local instance
    batch_size: 30,
    models: {}
  };
  /**
   * @override
   * always return 'something' to allow this adapter to be used without an API key since it's connecting to a local instance that doesn't require authentication
   * Problem/Reason( 2026-04-20): embed_batch method on parent class throws if this is falsy
   * should be better handed in future
   */
  get api_key() {
    return this.model.data.api_key || "something";
  }
  get host() {
    return this.model.data.host || this.constructor.defaults.host;
  }
  get endpoint() {
    return `${this.host}${this.constructor.defaults.endpoint}`;
  }
  get models_endpoint() {
    return `${this.host}${this.constructor.defaults.models_endpoint}`;
  }
  get model_show_endpoint() {
    return `${this.host}/api/show`;
  }
  async load() {
    await this.get_models();
    await super.load();
  }
  /**
   * Estimate token count for input text.
   * Ollama does not expose a tokenizer so we use a character based heuristic.
   * @param {string} input - Text to tokenize
   * @returns {Promise<Object>} Token count result
   */
  async count_tokens(input) {
    return { tokens: this.estimate_tokens(input) };
  }
  /**
   * Prepare input text and ensure it fits within `max_tokens`.
   * @param {string} embed_input - Raw input text
   * @returns {Promise<string|null>} Processed input text
   */
  async prepare_embed_input(embed_input) {
    if (typeof embed_input !== "string") throw new TypeError("embed_input must be a string");
    if (embed_input.length === 0) return null;
    const { tokens } = await this.count_tokens(embed_input);
    if (tokens <= this.max_tokens) return embed_input;
    return await this.trim_input_to_max_tokens(embed_input, tokens);
  }
  /**
   * Trim input text to satisfy `max_tokens`.
   * @private
   * @param {string} embed_input - Input text
   * @param {number} tokens_ct - Existing token count
   * @returns {Promise<string|null>} Trimmed text
   */
  async trim_input_to_max_tokens(embed_input, tokens_ct) {
    const reduce_ratio = (tokens_ct - this.max_tokens) / tokens_ct;
    const new_length = Math.floor(embed_input.length * (1 - reduce_ratio));
    let trimmed_input = embed_input.slice(0, new_length);
    const last_space_index = trimmed_input.lastIndexOf(" ");
    if (last_space_index > 0) trimmed_input = trimmed_input.slice(0, last_space_index);
    const prepared = await this.prepare_embed_input(trimmed_input);
    if (prepared === null) return null;
    return prepared;
  }
  /** @returns {number} Maximum tokens for an input */
  get max_tokens() {
    return this.model.data.max_tokens || this.constructor.defaults.max_tokens;
  }
  /**
   * Get the request adapter class.
   * @returns {SmartEmbedModelOllamaRequestAdapter} The request adapter class
   */
  get req_adapter() {
    return SmartEmbedModelOllamaRequestAdapter;
  }
  /**
   * Get the response adapter class.
   * @returns {SmartEmbedModelOllamaResponseAdapter} The response adapter class
   */
  get res_adapter() {
    return SmartEmbedModelOllamaResponseAdapter;
  }
  /**
   * Get available models from local Ollama instance.
   * @param {boolean} [refresh=false] - Whether to refresh cached models
   * @returns {Promise<Object>} Map of model objects
   */
  async get_models(refresh = false) {
    if (!this.model_data || refresh) {
      const list_resp = await this.http_adapter.request({
        url: this.models_endpoint,
        method: "GET"
      });
      if (list_resp.ok === false) {
        throw new Error(`Failed to fetch models list: ${list_resp.statusText}`);
      }
      const list_data = await list_resp.json();
      const models_raw = [];
      for (const m of filter_embedding_models(list_data.models || [])) {
        const detail_resp = await this.http_adapter.request({
          url: this.model_show_endpoint,
          method: "POST",
          body: JSON.stringify({ model: m.name })
        });
        models_raw.push({ ...await detail_resp.json(), name: m.name });
      }
      const model_data = this.parse_model_data(models_raw);
      this.model_data = model_data;
      if (typeof this.model.re_render_settings === "function") {
        this.model.re_render_settings();
      }
      return model_data;
    }
    return this.model_data;
  }
  /**
   * Get available models as dropdown options synchronously.
   * @returns {Array<Object>} Array of model options.
   */
  get_models_as_options() {
    const models = this.model_data;
    if (!Object.keys(models || {}).length) {
      this.get_models(true);
      return [{ value: "", name: "No models currently available" }];
    }
    return Object.values(models).map((model) => ({ value: model.id, name: model.name || model.id })).sort((a, b) => a.name.localeCompare(b.name));
  }
  /**
   * Parse model data from Ollama API response.
   * @param {Object} model_data - Raw model data from Ollama
   * @returns {Object} Map of model objects with capabilities and limits
   */
  parse_model_data(model_data) {
    if (!Array.isArray(model_data)) {
      this.model_data = {};
      console.error("Invalid model data format from Ollama:", model_data);
      return {};
    }
    if (model_data.length === 0) {
      this.model_data = { "no_models_available": {
        id: "no_models_available",
        name: "No models currently available"
      } };
      return this.model_data;
    }
    this.model_data = model_data.reduce((acc, model) => {
      const info = model.model_info || {};
      const ctx = Object.entries(info).find(([k]) => k.includes("context_length"))?.[1];
      const dims = Object.entries(info).find(([k]) => k.includes("embedding_length"))?.[1];
      acc[model.name] = {
        model_name: model.name,
        id: model.name,
        multimodal: false,
        max_tokens: ctx || this.max_tokens,
        dims,
        description: model.description || `Model: ${model.name}`
      };
      return acc;
    }, {});
    this._models = this.model_data;
    return this.model_data;
  }
  /**
   * Get the models.
   * @returns {Object} Map of model objects
   */
  get models() {
    if (typeof this._models === "object" && Object.keys(this._models || {}).length > 0) return this._models;
    else {
      return {};
    }
  }
  /**
   * Override settings config to remove API key setting since not needed for local instance.
   * @returns {Object} Settings configuration object
   */
  get settings_config() {
    const config = super.settings_config;
    delete config["[ADAPTER].api_key"];
    config["[ADAPTER].host"] = {
      name: "Ollama host",
      type: "text",
      description: "Enter the host for your Ollama instance",
      default: this.constructor.defaults.host
    };
    return config;
  }
};
var SmartEmbedModelOllamaRequestAdapter = class extends SmartEmbedModelRequestAdapter {
  /**
   * Convert request to Ollama's embed API format.
   * @returns {Object} Request parameters in Ollama's format
   */
  to_platform() {
    const ollama_body = {
      model: this.model_id,
      input: this.embed_inputs
    };
    return {
      url: this.adapter.endpoint,
      method: "POST",
      headers: this.get_headers(),
      body: JSON.stringify(ollama_body)
    };
  }
  /**
   * Prepare request headers for Ollama API.
   * @returns {Object} Headers object
   */
  get_headers() {
    return {
      "Content-Type": "application/json"
    };
  }
};
var SmartEmbedModelOllamaResponseAdapter = class extends SmartEmbedModelResponseAdapter {
  /**
   * Convert Ollama's response to a standardized OpenAI-like format.
   * @returns {Array<Object>} Array of embedding results
   */
  to_openai() {
    const resp = this.response;
    if (!resp || !resp.embeddings) {
      console.error("Invalid response format from Ollama:", resp);
      return [];
    }
    const tokens = Math.ceil(resp.prompt_eval_count / this.adapter.batch_size);
    const embeddings = resp.embeddings.map((vec) => ({
      vec,
      tokens
    }));
    return embeddings;
  }
  /**
   * Parse the response object.
   * @returns {Array<Object>} Parsed embedding results
   */
  parse_response() {
    return this.to_openai();
  }
};
var is_embedding_model = (mod) => {
  return ["embed", "embedding", "bge"].some((keyword) => mod.name.toLowerCase().includes(keyword));
};
var filter_embedding_models = (models) => {
  if (!Array.isArray(models)) {
    throw new TypeError("models must be an array");
  }
  return models.filter(is_embedding_model);
};

// node_modules/obsidian-smart-env/node_modules/smart-embed-model/adapters/gemini.js
var GeminiEmbedModelAdapter = class extends SmartEmbedModelApiAdapter {
  static defaults = {
    adapter: "gemini",
    description: "Google Gemini (API)",
    default_model: "gemini-embedding-001",
    endpoint: "https://generativelanguage.googleapis.com/v1beta/models/gemini-embedding-001:batchEmbedContents",
    dims: 768,
    max_tokens: 2048,
    batch_size: 50
  };
  /**
   * Count tokens in input text using tokenizer
   * @param {string} input - Text to tokenize
   * @returns {Promise<Object>} Token count result
   */
  async count_tokens(input) {
    if (!this.tiktoken) await this.load_tiktoken();
    return { tokens: this.tiktoken.encode(input).length };
  }
  /**
   * Prepare input text for embedding
   * Handles token limit truncation
   * @param {string} embed_input - Raw input text
   * @returns {Promise<string|null>} Processed input text
   */
  async prepare_embed_input(embed_input) {
    if (typeof embed_input !== "string") {
      throw new TypeError("embed_input must be a string");
    }
    if (embed_input.length === 0) {
      console.log("Warning: prepare_embed_input received an empty string");
      return null;
    }
    const { tokens } = await this.count_tokens(embed_input);
    if (tokens <= this.max_tokens) {
      return embed_input;
    }
    return await this.trim_input_to_max_tokens(embed_input, tokens);
  }
  /**
   * Trim input text to fit token limit
   * @private
   * @param {string} embed_input - Input text to trim
   * @param {number} tokens_ct - Current token count
   * @returns {Promise<string|null>} Trimmed input text
   */
  async trim_input_to_max_tokens(embed_input, tokens_ct) {
    const reduce_ratio = (tokens_ct - this.max_tokens) / tokens_ct;
    const new_length = Math.floor(embed_input.length * (1 - reduce_ratio));
    let trimmed_input = embed_input.slice(0, new_length);
    const last_space_index = trimmed_input.lastIndexOf(" ");
    if (last_space_index > 0) {
      trimmed_input = trimmed_input.slice(0, last_space_index);
    }
    const prepared_input = await this.prepare_embed_input(trimmed_input);
    if (prepared_input === null) {
      console.log(
        "Warning: prepare_embed_input resulted in an empty string after trimming"
      );
      return null;
    }
    return prepared_input;
  }
  /**
   * Get the request adapter class.
   * @returns {SmartEmbedGeminiRequestAdapter} The request adapter class
   */
  get req_adapter() {
    return SmartEmbedGeminiRequestAdapter;
  }
  /**
   * Get the response adapter class.
   * @returns {SmartEmbedGeminiResponseAdapter} The response adapter class
   */
  get res_adapter() {
    return SmartEmbedGeminiResponseAdapter;
  }
  /** @returns {Object} Settings configuration for Gemini adapter */
  get settings_config() {
    return {
      ...super.settings_config,
      "[ADAPTER].api_key": {
        name: "Google API Key for Gemini embeddings",
        type: "password",
        description: "Required for Gemini embedding models",
        placeholder: "Enter Google API Key"
      }
    };
  }
  /**
   * Get available models (hardcoded list)
   * @returns {Promise<Object>} Map of model objects
   */
  get_models() {
    return Promise.resolve(this.models);
  }
  get models() {
    return {
      "gemini-embedding-001": {
        "id": "gemini-embedding-001",
        "batch_size": 50,
        "dims": 768,
        "max_tokens": 2048,
        "name": "Gemini Embedding",
        "description": "API, 2,048 tokens, 768 dim",
        "endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-embedding-001:batchEmbedContents",
        "adapter": "gemini"
      }
    };
  }
  prepare_request_headers() {
    return {
      "Content-Type": "application/json",
      "x-goog-api-key": this.api_key
    };
  }
  backoff_wait_time = 5e3;
  // initial backoff wait time in ms
  backoff_factor = 1;
  // no usaqge stats from LM Studio so need to estimate tokens
  async embed_batch(inputs, retries = 0) {
    if (smart_env.smart_sources.entities_vector_adapter.is_queue_halted) {
      throw new Error("Embedding queue halted during backoff wait due to rate limit errors.");
    }
    const token_cts = inputs.map((item) => this.estimate_tokens(item.embed_input));
    const resp = await super.embed_batch(inputs);
    if (resp[0].error && resp[0].error.details && resp[0].error.details.code === 429) {
      console.warn("Rate limit error detected in Gemini embed_batch response.", resp);
      if (retries > 3) {
        console.error("Max retries reached for rate limit errors.");
        throw new Error("Max retries reached for rate limit errors.");
      }
      console.warn(resp[0].error.message);
      const retry_detail = resp[0].error.details?.details?.find((d) => d.retryDelay);
      if (retry_detail.retryDelay) {
        const wait_time_ms = parseInt(retry_detail.retryDelay) * 1e3 * 2;
        console.warn(`Using server-specified retry delay of ${wait_time_ms} ms`);
        await new Promise((resolve) => setTimeout(resolve, wait_time_ms));
        return await this.embed_batch(inputs, retries + 1);
      } else {
        this.backoff_factor += 1;
        console.warn(`Rate limit exceeded, backing off for ${this.backoff_wait_time * this.backoff_factor} ms`);
        await new Promise((resolve) => setTimeout(resolve, this.backoff_wait_time * this.backoff_factor));
        return await this.embed_batch(inputs, retries + 1);
      }
    } else if (resp[0].error) {
      console.error("Error in Gemini embed_batch response:", resp[0].error);
      throw new Error(`Gemini embed_batch error: ${resp[0].error.message}`);
    }
    resp.forEach((item, idx) => {
      item.tokens = token_cts[idx];
    });
    console.log("Gemini embed_batch response:", resp);
    return resp;
  }
};
var SmartEmbedGeminiRequestAdapter = class extends SmartEmbedModelRequestAdapter {
  get model_id() {
    let model_id = this.adapter.model.data.model_key;
    return `models/${model_id}`;
  }
  /**
   * Prepare request body for Gemini API
   * @returns {Object} Request body for API
   */
  prepare_request_body() {
    const requests = this.embed_inputs.map((input) => {
      const [title, ...content] = input.split("\n");
      const doc_content = content.join("\n").trim() || "";
      if (doc_content.length) {
        return {
          model: this.model_id,
          content: {
            parts: [{ text: doc_content }]
          },
          outputDimensionality: this.model_dims,
          taskType: "RETRIEVAL_DOCUMENT",
          title
        };
      } else {
        return {
          model: this.model_id,
          content: {
            parts: [{ text: title }]
          },
          outputDimensionality: this.model_dims,
          taskType: "RETRIEVAL_DOCUMENT"
        };
      }
    });
    return {
      requests
    };
  }
};
var SmartEmbedGeminiResponseAdapter = class extends SmartEmbedModelResponseAdapter {
  /**
   * Parse Gemini API response
   * @returns {Array<Object>} Parsed embedding results
   */
  parse_response() {
    const resp = this.response;
    console.log("Gemini response:", resp);
    if (!resp || !resp.embeddings || !resp.embeddings[0].values) {
      console.error("Invalid Gemini embedding response format", resp);
      return [];
    }
    return resp.embeddings.map((embedding, i) => {
      if (!embedding.values || embedding.values.length === 0) {
        console.warn(`No values for embedding at index ${i}`);
        return { vec: [], tokens: 0 };
      }
      return {
        vec: embedding.values,
        tokens: null
        // not provided
      };
    });
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-embed-model/adapters/lm_studio.js
function parse_lm_studio_models(list, adapter_key = "lm_studio") {
  if (list.object !== "list" || !Array.isArray(list.data)) {
    return { _: { id: "No models found." } };
  }
  console.log("LM Studio models", list);
  return list.data.filter((m) => m.id && m.type === "embeddings").reduce((acc, m) => {
    acc[m.id] = {
      id: m.id,
      model_name: m.id,
      max_tokens: m.loaded_context_length || 512,
      description: `LM Studio model: ${m.id}`,
      adapter: adapter_key
    };
    return acc;
  }, {});
}
var LmStudioEmbedModelAdapter = class extends SmartEmbedModelApiAdapter {
  static key = "lm_studio";
  static defaults = {
    description: "LM Studio",
    type: "API",
    host: "http://localhost:1234",
    // endpoint: "/v1/embeddings",
    endpoint: "/api/v0/embeddings",
    models_endpoint: "/api/v0/models",
    default_model: "",
    // user picks from dropdown
    streaming: false,
    api_key: "na",
    // not used
    batch_size: 10,
    max_tokens: 512
  };
  get req_adapter() {
    return LmStudioEmbedModelRequestAdapter;
  }
  get res_adapter() {
    return LmStudioEmbedModelResponseAdapter;
  }
  get host() {
    return this.model.data.host || this.constructor.defaults.host;
  }
  get endpoint() {
    return `${this.host}${this.constructor.defaults.endpoint}`;
  }
  get models_endpoint() {
    return `${this.host}${this.constructor.defaults.models_endpoint}`;
  }
  get settings_config() {
    const cfg = { ...super.settings_config };
    delete cfg["[ADAPTER].api_key"];
    cfg["[ADAPTER].refresh_models"] = {
      name: "Refresh Models",
      type: "button",
      description: "Refresh the list of available models.",
      callback: "adapter.refresh_models"
    };
    cfg["[ADAPTER].current_model"] = {
      type: "html",
      value: `<p>Embedding Model Max Tokens: ${this.max_tokens} (may be configured in LM Studio)</p>`
    };
    cfg["[ADAPTER].batch_size"] = {
      name: "Embedding Batch Size",
      type: "number",
      description: "Number of embeddings to process in parallel. Adjusting this may improve performance.",
      value: this.batch_size,
      default: this.constructor.defaults.batch_size
    };
    cfg["[ADAPTER].cors_note"] = {
      name: "CORS required",
      type: "html",
      // The renderer treats `value` as innerHTML.
      value: `<p>Before you can use LM Studio you must <strong>Enable CORS</strong> inside LM Studio \u2192 Developer \u2192 Settings</p>`
    };
    return cfg;
  }
  async get_models(refresh = false) {
    if (!refresh && this.model.data.provider_models) return this.model.data.provider_models;
    const resp = await this.http_adapter.request({
      url: this.models_endpoint,
      method: "GET"
    });
    const raw = await resp.json();
    const parsed = this.parse_model_data(raw);
    this.model.data.provider_models = parsed;
    this.model.re_render_settings();
    return parsed;
  }
  parse_model_data(list) {
    return parse_lm_studio_models(list, this.constructor.key);
  }
  async count_tokens(input) {
    return { tokens: this.estimate_tokens(input) };
  }
  /**
   * Prepare input text and ensure it fits within `max_tokens`.
   * @param {string} embed_input - Raw input text
   * @returns {Promise<string|null>} Processed input text
   */
  async prepare_embed_input(embed_input) {
    if (typeof embed_input !== "string") throw new TypeError("embed_input must be a string");
    if (embed_input.length === 0) return null;
    const { tokens } = await this.count_tokens(embed_input);
    if (tokens <= this.max_tokens) return embed_input;
    return await this.trim_input_to_max_tokens(embed_input, tokens);
  }
  /**
   * Refresh available models.
   */
  refresh_models() {
    console.log("refresh_models");
    this.get_models(true);
  }
  // no usaqge stats from LM Studio so need to estimate tokens
  async embed_batch(inputs) {
    const token_cts = inputs.map((item) => this.estimate_tokens(item.embed_input));
    const resp = await super.embed_batch(inputs);
    resp.forEach((item, idx) => {
      item.tokens = token_cts[idx];
    });
    return resp;
  }
};
var LmStudioEmbedModelRequestAdapter = class extends SmartEmbedModelRequestAdapter {
  /**
   * Prepare request body for LM Studio API
   * @returns {Object} Request body for API
   */
  prepare_request_body() {
    const body = {
      model: this.model_id,
      input: this.embed_inputs
    };
    return body;
  }
};
var LmStudioEmbedModelResponseAdapter = class extends SmartEmbedModelResponseAdapter {
  /**
   * Parse LM Studio API response
   * @returns {Array<Object>} Parsed embedding results
   */
  parse_response() {
    const resp = this.response;
    if (!resp || !resp.data) {
      console.error("Invalid response format", resp);
      return [];
    }
    return resp.data.map((item) => ({
      vec: item.embedding,
      tokens: null
      // LM Studio doesn't provide token usage
    }));
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-chat-model/smart_chat_model.js
var SmartChatModel = class extends SmartModel {
  scope_name = "smart_chat_model";
  static defaults = {
    adapter: "openai"
  };
  /**
   * Create a SmartChatModel instance.
   * @param {Object} opts - Configuration options
   * @param {string} opts.adapter - Adapter to use
   * @param {Object} opts.adapters - Map of adapter names to adapter classes
   * @param {Object} opts.settings - Model settings configuration
   */
  constructor(opts = {}) {
    super(opts);
  }
  /**
   * Get available models.
   * @returns {Object} Map of model objects
   */
  get models() {
    return this.adapter.models;
  }
  get can_stream() {
    return this.adapter.constructor.defaults.streaming;
  }
  /**
   * Complete a chat request.
   * @param {Object} req - Request parameters
   * @returns {Promise<Object>} Completion result
   */
  async complete(req) {
    const resp = await this.invoke_adapter_method("complete", req);
    if (resp.error) {
      throw normalize_error(resp.error);
    }
    return resp;
  }
  /**
   * Stream chat responses.
   * @param {Object} req - Request parameters
   * @param {Object} handlers - Event handlers for streaming
   * @param {Function} handlers.chunk - Handler for chunks: receives response object
   * @param {Function} handlers.error - Handler for errors: receives error object
   * @param {Function} handlers.done - Handler for completion: receives final response object
   * @returns {Promise<string>} Complete response text
   */
  async stream(req, handlers = {}) {
    return await this.invoke_adapter_method("stream", req, handlers);
  }
  /**
   * Stop active stream.
   */
  stop_stream() {
    this.invoke_adapter_method("stop_stream");
  }
  /**
   * Count tokens in input text.
   * @param {string|Object} input - Text to count tokens for
   * @returns {Promise<number>} Token count
   */
  async count_tokens(input) {
    return await this.invoke_adapter_method("count_tokens", input);
  }
  /**
   * Test if API key is valid.
   * @deprecated in favor of smart_model.test_model (should be safe to remove 2026-02-10)
   * @returns {Promise<boolean>} True if API key is valid
   */
  async test_api_key() {
    await this.invoke_adapter_method("test_api_key");
    this.re_render_settings();
  }
  /**
   * Get default model key.
   * @returns {string} Default model key
   */
  get default_model_key() {
    return this.adapter.constructor.defaults.default_model;
  }
  /**
   * Get current settings.
   * @returns {Object} Settings object
   */
  get settings() {
    return this.opts.settings;
  }
  /**
   * Get settings configuration.
   * @returns {Object} Settings configuration object
   */
  get settings_config() {
    const _settings_config = {
      adapter: {
        name: "Chat Model Platform",
        type: "dropdown",
        description: "Select a platform/provider for chat models.",
        options_callback: "get_platforms_as_options",
        is_scope: true,
        // trigger re-render of settings when changed
        callback: "adapter_changed"
      },
      // Merge adapter-specific settings
      ...this.adapter.settings_config || {}
    };
    return this.process_settings_config(_settings_config);
  }
  /**
   * Process setting key.
   * @param {string} key - Setting key
   * @returns {string} Processed key
   */
  process_setting_key(key) {
    return key.replace(/\[CHAT_ADAPTER\]/g, this.adapter_name);
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-chat-model/streamer.js
var SmartStreamer = class {
  constructor(url, options = {}) {
    const {
      method = "GET",
      headers = {},
      body = null,
      withCredentials = false
    } = options;
    this.url = url;
    this.method = method;
    this.headers = headers;
    this.body = body;
    this.withCredentials = withCredentials;
    this.listeners = {};
    this.readyState = this.CONNECTING;
    this.progress = 0;
    this.chunk = "";
    this.last_event_id = "";
    this.xhr = null;
    this.FIELD_SEPARATOR = ":";
    this.INITIALIZING = -1;
    this.CONNECTING = 0;
    this.OPEN = 1;
    this.CLOSED = 2;
    this.chunk_accumulator = "";
    this.chunk_splitting_regex = options.chunk_splitting_regex || /(\r\n|\n|\r)/g;
  }
  /**
   * Adds an event listener for the specified event type.
   *
   * @param {string} type - The type of the event.
   * @param {Function} listener - The listener function to be called when the event is triggered.
   */
  addEventListener(type, listener) {
    if (!this.listeners[type]) this.listeners[type] = [];
    if (!this.listeners[type].includes(listener)) this.listeners[type].push(listener);
  }
  /**
   * Removes an event listener from the SmartStreamer instance.
   *
   * @param {string} type - The type of event to remove the listener from.
   * @param {Function} listener - The listener function to remove.
   */
  removeEventListener(type, listener) {
    if (!this.listeners[type]) return;
    this.listeners[type] = this.listeners[type].filter((callback) => callback !== listener);
    if (this.listeners[type].length === 0) delete this.listeners[type];
  }
  /**
   * Dispatches an event to the appropriate event handlers.
   *
   * @param {Event} event - The event to be dispatched.
   * @returns {boolean} - Returns true if the event was successfully dispatched, false otherwise.
   */
  dispatchEvent(event) {
    if (!event) return true;
    event.source = this;
    const onHandler = "on" + event.type;
    if (Object.prototype.hasOwnProperty.call(this, onHandler)) {
      this[onHandler].call(this, event);
      if (event.defaultPrevented) return false;
    }
    if (this.listeners[event.type]) {
      this.listeners[event.type].forEach((callback) => {
        callback(event);
        return !event.defaultPrevented;
      });
    }
    return true;
  }
  /**
   * Initiates the streaming process.
   */
  stream() {
    this.#setReadyState(this.CONNECTING);
    this.xhr = new XMLHttpRequest();
    this.xhr.addEventListener("progress", this.#onStreamProgress.bind(this));
    this.xhr.addEventListener("load", this.#onStreamLoaded.bind(this));
    this.xhr.addEventListener("readystatechange", this.#checkStreamClosed.bind(this));
    this.xhr.addEventListener("error", this.#onStreamFailure.bind(this));
    this.xhr.addEventListener("abort", this.#onStreamAbort.bind(this));
    this.xhr.open(this.method, this.url);
    for (const header in this.headers) {
      this.xhr.setRequestHeader(header, this.headers[header]);
    }
    if (this.last_event_id) this.xhr.setRequestHeader("Last-Event-ID", this.last_event_id);
    this.xhr.withCredentials = this.withCredentials;
    this.xhr.send(this.body);
  }
  /**
   * Ends the streamer connection.
   * Aborts the current XHR request and sets the ready state to CLOSED.
   */
  end() {
    if (this.readyState === this.CLOSED) return;
    this.xhr.abort();
    this.xhr = null;
    this.#setReadyState(this.CLOSED);
  }
  // private methods
  #setReadyState(state) {
    const event = new CustomEvent("readyStateChange");
    event.readyState = state;
    this.readyState = state;
    this.dispatchEvent(event);
  }
  #onStreamFailure(e) {
    const event = new CustomEvent("error");
    try {
      const parsed = JSON.parse(e.currentTarget.response);
      if (typeof parsed === "object") {
        event.data = parsed;
      } else {
        event.data = e.currentTarget.response;
      }
    } catch {
      event.data = e.currentTarget.response;
    }
    this.dispatchEvent(event);
    this.end();
  }
  #onStreamAbort(e) {
    const event = new CustomEvent("abort");
    this.end();
  }
  #onStreamProgress(e) {
    if (!this.xhr) return;
    if (this.xhr.status !== 200) {
      this.#onStreamFailure(e);
      return;
    }
    if (this.readyState === this.CONNECTING) {
      this.dispatchEvent(new CustomEvent("open"));
      this.#setReadyState(this.OPEN);
    }
    const data = this.xhr.responseText.substring(this.progress);
    this.progress += data.length;
    const parts = data.split(this.chunk_splitting_regex);
    parts.forEach((part, index) => {
      if (part.trim().length === 0) {
        if (this.chunk) {
          this.dispatchEvent(this.#parseEventChunk(this.chunk.trim()));
          this.chunk = "";
        }
      } else {
        this.chunk += part;
        if (index === parts.length - 1 && this.xhr.readyState === XMLHttpRequest.DONE) {
          this.dispatchEvent(this.#parseEventChunk(this.chunk.trim()));
          this.chunk = "";
        }
      }
    });
  }
  #onStreamLoaded(e) {
    this.#onStreamProgress(e);
    this.dispatchEvent(this.#parseEventChunk(this.chunk));
    this.chunk = "";
  }
  #parseEventChunk(chunk) {
    if (!chunk) return console.log("no chunk");
    const event = new CustomEvent("message");
    event.data = chunk;
    event.last_event_id = this.last_event_id;
    return event;
  }
  #checkStreamClosed() {
    if (!this.xhr) return;
    if (this.xhr.readyState === XMLHttpRequest.DONE) this.#setReadyState(this.CLOSED);
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-chat-model/adapters/_adapter.js
var SmartChatModelAdapter = class extends SmartModelAdapter {
  /**
   * @override in sub-class with adapter-specific default configurations
   * @property {string} id - The adapter identifier
   * @property {string} description - Human-readable description
   * @property {string} type - Adapter type ("API")
   * @property {string} endpoint - API endpoint
   * @property {boolean} streaming - Whether streaming is supported
   * @property {string} adapter - Adapter identifier
   * @property {string} models_endpoint - Endpoint for retrieving models
   * @property {string} default_model - Default model to use
   * @property {string} signup_url - URL for API key signup
   */
  static defaults = {};
  /**
   * Create a SmartChatModelAdapter instance.
   * @param {SmartChatModel} model - The parent SmartChatModel instance
   */
  constructor(model) {
    super(model);
    this.smart_chat = model;
    this.main = model;
  }
  /**
   * Complete a chat request.
   * @abstract
   * @param {Object} req - Request parameters
   * @returns {Promise<Object>} Completion result
   */
  async complete(req) {
    throw new Error("complete not implemented");
  }
  /**
   * Count tokens in input text.
   * @abstract
   * @param {string|Object} input - Text to count tokens for
   * @returns {Promise<number>} Token count
   */
  async count_tokens(input) {
    throw new Error("count_tokens not implemented");
  }
  /**
   * Stream chat responses.
   * @abstract
   * @param {Object} req - Request parameters
   * @param {Object} handlers - Event handlers for streaming
   * @returns {Promise<string>} Complete response text
   */
  async stream(req, handlers = {}) {
    throw new Error("stream not implemented");
  }
  /**
   * Test if API key is valid.
   * @abstract
   * @deprecated in favor of smart_model.test_model (should be safe to remove 2026-02-10)
   * @returns {Promise<boolean>} True if API key is valid
   */
  async test_api_key() {
    throw new Error("test_api_key not implemented");
  }
  /**
   * Refresh available models.
   */
  refresh_models() {
    console.log("refresh_models");
    this.get_models(true);
  }
  /**
   * Get settings configuration.
   * @returns {Object} Settings configuration object
   */
  get settings_config() {
    return {
      "[CHAT_ADAPTER].model_key": {
        name: "Chat Model",
        type: "dropdown",
        description: "Select a chat model.",
        options_callback: "adapter.get_models_as_options",
        callback: "reload_model",
        default: this.constructor.defaults.default_model
      },
      "[CHAT_ADAPTER].refresh_models": {
        name: "Refresh Models",
        type: "button",
        description: "Refresh the list of available models.",
        callback: "adapter.refresh_models"
      }
    };
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-chat-model/adapters/_api.js
var MODEL_ADAPTER_CACHE = {};
var MODELS_DEV_CACHE = { data: null, fetched_at: 0 };
var SmartChatModelApiAdapter = class extends SmartChatModelAdapter {
  constructor(model) {
    super(model);
    this.model_data_loaded_at = 0;
  }
  /**
   * Get the request adapter class.
   * @returns {SmartChatModelRequestAdapter} The request adapter class
   */
  get req_adapter() {
    return SmartChatModelRequestAdapter;
  }
  /**
   * Get the response adapter class.
   * @returns {SmartChatModelResponseAdapter} The response adapter class
   */
  get res_adapter() {
    return SmartChatModelResponseAdapter;
  }
  /**
   * Get or initialize the HTTP adapter.
   * @returns {SmartHttpRequest} The HTTP adapter instance
   */
  get http_adapter() {
    if (!this._http_adapter) {
      if (this.model.http_adapter) this._http_adapter = this.model.http_adapter;
      else if (this.model.opts.http_adapter) this._http_adapter = this.model.opts.http_adapter;
      else this._http_adapter = new SmartHttpRequest({ adapter: SmartHttpRequestFetchAdapter });
    }
    return this._http_adapter;
  }
  /**
   * Get the settings configuration for the API adapter.
   * @deprecated migrating to module export
   * @returns {Object} Settings configuration object with API key and other settings
   */
  get settings_config() {
    return {
      ...super.settings_config,
      "[CHAT_ADAPTER].api_key": {
        name: "API Key",
        type: "password",
        description: "Enter your API key for the chat model platform.",
        callback: "test_api_key",
        is_scope: true
        // trigger re-render of settings when changed (reload models dropdown)
      }
    };
  }
  /**
   * Count tokens in the input text.
   * @abstract
   * @param {string|Object} input - Text or message object to count tokens for
   * @returns {Promise<number>} Number of tokens in the input
   */
  async count_tokens(input) {
    throw new Error("count_tokens not implemented");
  }
  /**
   * Get the parameters for requesting available models.
   * @returns {Object} Request parameters for models endpoint
   */
  get models_request_params() {
    return {
      url: this.models_endpoint,
      method: this.models_endpoint_method,
      headers: {
        "Authorization": `Bearer ${this.api_key}`
      }
    };
  }
  async get_enriched_model_data() {
    const provider_key = this.constructor.models_dev_key || this.constructor.key;
    await this.get_models_dev_index();
    const provider_data = MODELS_DEV_CACHE.data[provider_key] || {};
    const get_limit_i = (model) => model.limit?.context || 1e4;
    const get_limit_o = (model) => model.limit?.output || 1e4;
    const get_multimodal = (model) => model.modalities?.input?.includes("image") || false;
    if (Object.keys(this.model_data || {}).length > 0) {
      for (const [key, model] of Object.entries(this.model_data)) {
        const enriched = provider_data?.models?.[model.id];
        if (!enriched) continue;
        this.model_data[key].models_dev = enriched;
        this.model_data[key].name = enriched.name || model.name;
        this.model_data[key].max_input_tokens = get_limit_i(enriched);
        this.model_data[key].max_output_tokens = get_limit_o(enriched);
        this.model_data[key].multimodal = get_multimodal(enriched);
        this.model_data[key].cost = enriched.cost;
      }
    } else {
      for (const [key, model] of Object.entries(provider_data?.models || {})) {
        this.model_data[key] = {
          ...model,
          model_name: model.name,
          description: model.name,
          max_input_tokens: get_limit_i(model),
          max_output_tokens: get_limit_o(model),
          multimodal: get_multimodal(model)
        };
      }
    }
    return this.model_data;
  }
  valid_model_data() {
    return typeof this.model_data === "object" && Object.keys(this.model_data || {}).length > 0 && this.model_data_loaded_at && Date.now() - this.model_data_loaded_at < 1 * 60 * 60 * 1e3;
  }
  /**
   * Get available models from the API.
   * @param {boolean} [refresh=false] - Whether to refresh cached models
   * @returns {Promise<Object>} Map of model objects
   */
  async get_models(refresh = false) {
    if (!refresh && this.valid_model_data()) return this.model_data;
    if (this.api_key) {
      let response;
      try {
        response = await this.http_adapter.request(this.models_request_params);
        this.model_data = this.parse_model_data(await response.json());
      } catch (error) {
        console.error("Failed to fetch model data:", { error, response });
      }
    }
    this.model_data = await this.get_enriched_model_data();
    this.model_data_loaded_at = Date.now();
    if (this.model.data) {
      this.model.data.provider_models = this.model_data;
    }
    if (this.valid_model_data() && typeof this.model.re_render_settings === "function") setTimeout(() => {
      this.model.re_render_settings();
    }, 100);
    else console.warn("Invalid model data, not re-rendering settings");
    return this.model_data;
  }
  /**
   * Parses the raw model data from OpenAI API and transforms it into a more usable format.
   * @param {Object} model_data - The raw model data received from OpenAI API.
   * @abstract
   * @returns {Array<Object>} An array of parsed model objects with the following properties:
   *   @property {string} model_name - The name/ID of the model as returned by the API.
   *   @property {string} id - The id used to identify the model (usually same as model_name).
   *   @property {boolean} multimodal - Indicates if the model supports multimodal inputs.
   *   @property {number} [max_input_tokens] - The maximum number of input tokens the model can process.
   *   @property {string} [description] - A description of the model's context and output capabilities.
   */
  parse_model_data(model_data) {
    throw new Error("parse_model_data not implemented");
  }
  /**
   * Complete a chat request.
   * @param {Object} req - Request parameters
   * @returns {Promise<Object>} Completion response in OpenAI format
   */
  async complete(req) {
    const _req = new this.req_adapter(this, {
      ...req,
      stream: false
    });
    const request_params = await _req.to_platform();
    const http_resp = await this.http_adapter.request(request_params);
    if (!http_resp) return null;
    const _res = new this.res_adapter(this, await http_resp.json());
    try {
      const resp = _res.to_openai();
      return resp;
    } catch (error) {
      const normalized_error = normalize_error(error?.data || error);
      console.error("Error in SmartChatModelApiAdapter.complete():", { normalized_error, error });
      console.error(http_resp);
      return normalized_error;
    }
  }
  // STREAMING
  /**
  * Stream chat responses.
  * @param {Object} req - Request parameters
  * @param {Object} handlers - Event handlers for streaming
  * @param {Function} handlers.chunk - Handler for response objects
  * @param {Function} handlers.error - Handler for errors
  * @param {Function} handlers.done - Handler for completion
  * @returns {Promise<Object>} Complete response object
  */
  async stream(req, handlers = {}) {
    let request_params;
    try {
      const _req = new this.req_adapter(this, req);
      request_params = await _req.to_platform(true);
      if (this.streaming_chunk_splitting_regex) request_params.chunk_splitting_regex = this.streaming_chunk_splitting_regex;
    } catch (error) {
      const normalized_error = normalize_error(error?.data || error);
      console.error("Failed to start stream (request prep):", { error, normalized_error });
      if (typeof handlers?.error === "function") handlers.error(normalized_error);
      this.stop_stream();
      throw normalized_error;
    }
    return await new Promise((resolve, reject) => {
      try {
        this.active_stream = new SmartStreamer(this.endpoint_streaming, request_params);
        const resp_adapter = new this.res_adapter(this);
        this.active_stream.addEventListener("message", async (e) => {
          if (this.is_end_of_stream(e)) {
            await resp_adapter.handle_chunk(e.data);
            this.stop_stream();
            const final_resp = resp_adapter.to_openai();
            handlers.done && await handlers.done(final_resp);
            resolve(final_resp);
            return;
          }
          try {
            const raw = resp_adapter.handle_chunk(e.data);
            handlers.chunk && await handlers.chunk({ ...resp_adapter.to_openai(), raw });
          } catch (error) {
            const normalized_error = normalize_error({ ...e.data, ...error });
            console.error("Error processing stream chunk:", { e, error, normalized_error });
            handlers.error && handlers.error(normalized_error);
            this.stop_stream();
            reject(normalized_error);
          }
        });
        this.active_stream.addEventListener("error", (e) => {
          console.error("Stream error:", e);
          const normalized_error = normalize_error(e?.data || e);
          handlers.error && handlers.error(normalized_error);
          this.stop_stream();
          reject(normalized_error);
        });
        this.active_stream.stream();
      } catch (err) {
        console.error("Failed to start stream:", err);
        const normalized_error = normalize_error(err?.data || err);
        handlers.error && handlers.error(normalized_error);
        this.stop_stream();
        reject(normalized_error);
      }
    });
  }
  /**
   * Check if a stream event indicates end of stream.
   * @param {Event} event - Stream event
   * @returns {boolean} True if end of stream
   */
  is_end_of_stream(event) {
    return event.data === "data: [DONE]";
  }
  /**
   * Stop active stream.
   */
  stop_stream() {
    if (this.active_stream) {
      this.active_stream.end();
      this.active_stream = null;
    }
  }
  /**
   * Get the API key.
   * @returns {string} The API key.
   */
  get api_key() {
    return this.model.api_key || this.main.opts.api_key;
  }
  get models_endpoint() {
    return this.constructor.defaults.models_endpoint;
  }
  get models_endpoint_method() {
    return "POST";
  }
  /**
   * Get the endpoint URL.
   * @returns {string} The endpoint URL.
   */
  get endpoint() {
    return this.constructor.defaults.endpoint;
  }
  /**
   * Get the streaming endpoint URL.
   * @returns {string} The streaming endpoint URL.
   */
  get endpoint_streaming() {
    return this.constructor.defaults.endpoint_streaming || this.endpoint;
  }
  /**
   * Get the maximum output tokens.
   * @returns {number} The maximum output tokens.
   */
  get max_output_tokens() {
    return this.model.data.max_output_tokens || 3e3;
  }
  async get_models_dev_index(ttl_ms = 60 * 60 * 1e3) {
    const now = Date.now();
    if (MODELS_DEV_CACHE?.data && now - MODELS_DEV_CACHE?.fetched_at < ttl_ms) {
      return MODELS_DEV_CACHE.data;
    }
    try {
      const req = {
        url: "https://models.dev/api.json",
        method: "GET",
        headers: { "Content-Type": "application/json" }
      };
      const resp = await this.http_adapter.request(req);
      const data = await resp.json();
      MODELS_DEV_CACHE.data = data;
      MODELS_DEV_CACHE.fetched_at = now;
      console.log({ MODELS_DEV_CACHE });
      return data;
    } catch (err) {
      console.warn("models.dev fetch failed; continuing without enrichment", err);
      return MODELS_DEV_CACHE.data || [];
    }
  }
  /**
   * Get available models as dropdown options synchronously.
   * @returns {Array<Object>} Array of model options.
   */
  get_models_as_options() {
    if (Object.keys(this.model_data || {}).length) {
      return Object.entries(this.model_data).map(([id, model]) => ({ value: id, name: model.name || id })).sort((a, b) => a.name.localeCompare(b.name));
    }
    this.get_models(true);
    return [{ value: "", name: "No models currently available" }];
  }
  get model_data() {
    if (!MODEL_ADAPTER_CACHE[this.constructor.key]) MODEL_ADAPTER_CACHE[this.constructor.key] = {};
    return MODEL_ADAPTER_CACHE[this.constructor.key];
  }
  set model_data(data) {
    if (!MODEL_ADAPTER_CACHE[this.constructor.key]) MODEL_ADAPTER_CACHE[this.constructor.key] = {};
    MODEL_ADAPTER_CACHE[this.constructor.key] = data;
  }
};
var SmartChatModelRequestAdapter = class {
  /**
   * @constructor
   * @param {SmartChatModelAdapter} adapter - The SmartChatModelAdapter instance
   * @param {Object} req - The incoming request object
   */
  constructor(adapter, req = {}) {
    this.adapter = adapter;
    this._req = req;
  }
  /**
   * Get the messages array from the request
   * @returns {Array<Object>} Array of message objects
   */
  get messages() {
    return this._req.messages || [];
  }
  /**
   * Get the model identifier
   * @returns {string} Model ID
   */
  get model_id() {
    return this._req.model || this.adapter.model.model_key || this.adapter.model.data.id;
  }
  /**
   * Get the temperature setting
   * @returns {number} Temperature value
   */
  get temperature() {
    return this._req.temperature;
  }
  /**
   * Get the maximum tokens setting
   * @returns {number} Max tokens value
   */
  get max_tokens() {
    return this._req.max_tokens || this.adapter.max_output_tokens;
  }
  /**
   * Get the streaming flag
   * @returns {boolean} Whether to stream responses
   */
  get stream() {
    return this._req.stream;
  }
  /**
   * Get the tools array
   * @returns {Array<Object>|null} Array of tool objects or null
   */
  get tools() {
    return this._req.tools || null;
  }
  /**
   * Get the tool choice setting
   * @returns {string|Object|null} Tool choice configuration
   */
  get tool_choice() {
    return this._req.tool_choice || null;
  }
  get frequency_penalty() {
    return this._req.frequency_penalty;
  }
  get presence_penalty() {
    return this._req.presence_penalty;
  }
  get top_p() {
    return this._req.top_p;
  }
  /**
   * Get request headers
   * @returns {Object} Headers object
   */
  get_headers() {
    const headers = {
      "Content-Type": "application/json",
      ...this.adapter.constructor.defaults.headers || {}
    };
    const api_key_header = this.adapter.constructor.defaults.api_key_header;
    if (api_key_header !== "none") {
      if (api_key_header) {
        headers[api_key_header] = this.adapter.api_key;
      } else if (this.adapter.api_key) {
        headers["Authorization"] = `Bearer ${this.adapter.api_key}`;
      }
    }
    return headers;
  }
  /**
   * Convert request to platform-specific format
   * @returns {Object} Platform-specific request parameters
   */
  to_platform(streaming = false) {
    return this.to_openai(streaming);
  }
  /**
   * Convert request to OpenAI format
   * @returns {Object} Request parameters in OpenAI format
   */
  to_openai(streaming = false) {
    const body = {
      messages: this._transform_messages_to_openai(),
      model: this.model_id,
      // TODO max_completion_tokens
      temperature: this.temperature,
      stream: streaming,
      ...this.tools && { tools: this._transform_tools_to_openai() }
    };
    if (body.tools?.length > 0 && this.tool_choice && this.tool_choice !== "none") {
      body.tool_choice = this.tool_choice;
    }
    if (this.model_id?.startsWith("o1-")) {
      body.messages = body.messages.filter((m) => m.role !== "system");
      delete body.temperature;
    }
    if (typeof this._req.top_p === "number") body.top_p = this._req.top_p;
    if (typeof this._req.presence_penalty === "number") body.presence_penalty = this._req.presence_penalty;
    if (typeof this._req.frequency_penalty === "number") body.frequency_penalty = this._req.frequency_penalty;
    return {
      url: this.adapter.endpoint,
      method: "POST",
      headers: this.get_headers(),
      body: JSON.stringify(body)
    };
  }
  /**
   * Transform messages to OpenAI format
   * @returns {Array<Object>} Transformed messages array
   * @private
   */
  _transform_messages_to_openai() {
    return this.messages.map((message) => this._transform_single_message_to_openai(message));
  }
  /**
   * Transform a single message to OpenAI format
   * @param {Object} message - Message object to transform
   * @returns {Object} Transformed message object
   * @private
   */
  _transform_single_message_to_openai(message) {
    const transformed = {
      role: this._get_openai_role(message.role),
      content: this._get_openai_content(message)
    };
    if (message.name) transformed.name = message.name;
    if (message.tool_calls) transformed.tool_calls = this._transform_tool_calls_to_openai(message.tool_calls);
    if (message.image_url) transformed.image_url = message.image_url;
    if (message.tool_call_id) transformed.tool_call_id = message.tool_call_id;
    return transformed;
  }
  /**
   * Get the OpenAI role for a given role.
   * @param {string} role - The role to transform.
   * @returns {string} The transformed role.
   * @private
   */
  _get_openai_role(role) {
    return role;
  }
  /**
   * Get the OpenAI content for a given content.
   * @param {string} content - The content to transform.
   * @returns {string} The transformed content.
   * @private
   */
  _get_openai_content(message) {
    return message.content;
  }
  /**
   * Transform tool calls to OpenAI format.
   * @param {Array} tool_calls - Array of tool call objects.
   * @returns {Array} Transformed tool calls array.
   * @private
   */
  _transform_tool_calls_to_openai(tool_calls) {
    return tool_calls.map((tool_call) => ({
      id: tool_call.id,
      type: tool_call.type,
      function: {
        name: tool_call.function.name,
        arguments: tool_call.function.arguments
      }
    }));
  }
  /**
   * Transform tools to OpenAI format.
   * @returns {Array} Transformed tools array.
   * @private
   */
  _transform_tools_to_openai() {
    return this.tools.map((tool) => ({
      type: tool.type,
      function: {
        name: tool.function.name,
        description: tool.function.description,
        parameters: tool.function.parameters
      }
    }));
  }
};
var SmartChatModelResponseAdapter = class {
  // must be getter to prevent erroneous assignment
  static get platform_res() {
    return {
      id: "",
      object: "chat.completion",
      created: 0,
      model: "",
      choices: [],
      usage: {}
    };
  }
  /**
   * @constructor
   * @param {SmartChatModelAdapter} adapter - The SmartChatModelAdapter instance
   * @param {Object} res - The response object
   */
  constructor(adapter, res, status = null) {
    this.adapter = adapter;
    this._res = res || this.constructor.platform_res;
    this.status = status;
  }
  /**
   * Get response ID
   * @returns {string|null} Response ID
   */
  get id() {
    return this._res.id || null;
  }
  /**
   * Get response object type
   * @returns {string|null} Object type
   */
  get object() {
    return this._res.object || null;
  }
  /**
   * Get creation timestamp
   * @returns {number|null} Creation timestamp
   */
  get created() {
    return this._res.created || null;
  }
  /**
   * Get response choices
   * @returns {Array<Object>} Array of choice objects
   */
  get choices() {
    return this._res.choices || [];
  }
  /**
   * Get first tool call if present
   * @returns {Object|null} Tool call object
   */
  get tool_call() {
    return this.message.tool_calls?.[0] || null;
  }
  /**
   * Get tool name from first tool call
   * @returns {string|null} Tool name
   */
  get tool_name() {
    return this.tool_call?.tool_name || null;
  }
  /**
   * Get tool call parameters
   * @returns {Object|null} Tool parameters
   */
  get tool_call_content() {
    return this.tool_call?.parameters || null;
  }
  /**
   * Get token usage statistics
   * @returns {Object|null} Usage statistics
   */
  get usage() {
    return this._res.usage || null;
  }
  get error() {
    return this._res.error || null;
  }
  /**
   * Convert response to OpenAI format
   * @returns {Object} Response in OpenAI format
   */
  to_openai() {
    if (this.error) return { error: normalize_error(this.error, this.status) };
    const res = {
      id: this.id,
      object: this.object,
      created: this.created,
      choices: this._transform_choices_to_openai(),
      usage: this._transform_usage_to_openai(),
      raw: this._res
    };
    return res;
  }
  /**
   * Parse chunk adds delta to content as expected output format
   */
  handle_chunk(chunk) {
    if (chunk === "data: [DONE]") return;
    chunk = JSON.parse(chunk.split("data: ")[1] || "{}");
    if (Object.keys(chunk).length === 0) return;
    if (!this._res.choices[0]) {
      this._res.choices.push({
        message: {
          index: 0,
          role: "assistant",
          content: ""
        }
      });
    }
    if (!this._res.id) {
      this._res.id = chunk.id;
    }
    let raw;
    if (chunk.choices?.[0]?.delta?.content) {
      const content = chunk.choices[0].delta.content;
      raw = content;
      this._res.choices[0].message.content += content;
    }
    if (chunk.choices?.[0]?.delta?.tool_calls) {
      if (!this._res.choices[0].message.tool_calls) {
        this._res.choices[0].message.tool_calls = [{
          id: "",
          type: "function",
          function: {
            name: "",
            arguments: ""
          }
        }];
      }
      if (chunk.choices[0].delta.tool_calls[0].id) {
        this._res.choices[0].message.tool_calls[0].id += chunk.choices[0].delta.tool_calls[0].id;
      }
      if (chunk.choices[0].delta.tool_calls[0].function.name) {
        this._res.choices[0].message.tool_calls[0].function.name += chunk.choices[0].delta.tool_calls[0].function.name;
      }
      if (chunk.choices[0].delta.tool_calls[0].function.arguments) {
        this._res.choices[0].message.tool_calls[0].function.arguments += chunk.choices[0].delta.tool_calls[0].function.arguments;
      }
    }
    return raw;
  }
  /**
   * Transform choices to OpenAI format.
   * @returns {Array} Transformed choices array.
   * @private
   */
  _transform_choices_to_openai() {
    return this.choices.map((choice) => ({
      index: choice.index,
      message: this._transform_message_to_openai(choice.message),
      finish_reason: this._get_openai_finish_reason(choice.finish_reason)
    }));
  }
  /**
   * Transform a single message to OpenAI format.
   * @param {Object} message - The message object to transform.
   * @returns {Object} Transformed message object.
   * @private
   */
  _transform_message_to_openai(message = {}) {
    const transformed = {
      role: this._get_openai_role(message.role),
      content: this._get_openai_content(message)
    };
    if (message.name) transformed.name = message.name;
    if (message.tool_calls) transformed.tool_calls = this._transform_tool_calls_to_openai(message.tool_calls);
    if (message.image_url) transformed.image_url = message.image_url;
    return transformed;
  }
  /**
   * Get the OpenAI role for a given role.
   * @param {string} role - The role to transform.
   * @returns {string} The transformed role.
   * @private
   */
  _get_openai_role(role) {
    return role;
  }
  /**
   * Get the OpenAI content for a given content.
   * @param {string} content - The content to transform.
   * @returns {string} The transformed content.
   * @private
   */
  _get_openai_content(message) {
    return message.content;
  }
  /**
   * Get the OpenAI finish reason for a given finish reason.
   * @param {string} finish_reason - The finish reason to transform.
   * @returns {string} The transformed finish reason.
   * @private
   */
  _get_openai_finish_reason(finish_reason) {
    return finish_reason;
  }
  /**
   * Transform usage to OpenAI format.
   * @returns {Object} Transformed usage object.
   * @private
   */
  _transform_usage_to_openai() {
    return this.usage;
  }
  /**
   * Transform tool calls to OpenAI format.
   * @param {Array} tool_calls - Array of tool call objects.
   * @returns {Array} Transformed tool calls array.
   * @private
   */
  _transform_tool_calls_to_openai(tool_calls) {
    return tool_calls.map((tool_call) => ({
      id: tool_call.id,
      type: tool_call.type,
      function: {
        name: tool_call.function.name,
        arguments: tool_call.function.arguments
      }
    }));
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-chat-model/adapters/anthropic.js
var SmartChatModelAnthropicAdapter = class extends SmartChatModelApiAdapter {
  static key = "anthropic";
  static defaults = {
    description: "Anthropic Claude",
    type: "API",
    endpoint: "https://api.anthropic.com/v1/messages",
    streaming: true,
    api_key_header: "x-api-key",
    headers: {
      "anthropic-version": "2023-06-01",
      "anthropic-beta": "tools-2024-04-04",
      "anthropic-dangerous-direct-browser-access": true
    },
    adapter: "Anthropic",
    models_endpoint: false,
    default_model: "claude-opus-4-1-20250805",
    signup_url: "https://console.anthropic.com/login?returnTo=%2Fsettings%2Fkeys"
  };
  /**
   * Get request adapter class
   * @returns {typeof SmartChatModelAnthropicRequestAdapter} Request adapter class
   */
  get req_adapter() {
    return SmartChatModelAnthropicRequestAdapter;
  }
  /**
   * Get response adapter class
   * @returns {typeof SmartChatModelAnthropicResponseAdapter} Response adapter class
   */
  res_adapter = SmartChatModelAnthropicResponseAdapter;
  /**
   * Get available models (hardcoded list) and enrich via models.dev
   * @returns {Promise<Object>} Map of model objects
   */
  async get_models() {
    try {
      this.model_data = await this.get_enriched_model_data();
      this.model_data_loaded_at = Date.now();
      this.model.data.provider_models = this.model_data;
      setTimeout(() => {
        this.model.re_render_settings();
      }, 100);
      return this.model_data;
    } catch {
      return this.anthropic_models;
    }
  }
  is_end_of_stream(event) {
    const data = String(event?.data || "").trim();
    if (!data.startsWith("data: ")) return false;
    try {
      return data.includes("message_stop");
    } catch {
      return false;
    }
  }
  /**
   * Get hardcoded list of available models
   * @deprecated use get_enriched_model_data() instead (remove after no-incidents)
   * @returns {Object} Map of model objects with capabilities and limits
   */
  get anthropic_models() {
    return {
      // ── Claude 4 family ──────────────────────────────────────────────────────
      "claude-opus-4-1-20250805": {
        name: "Claude Opus 4.1 (2025-08-05)",
        id: "claude-opus-4-1-20250805",
        model_name: "claude-opus-4-1-20250805",
        description: "Anthropic Claude Opus 4.1 snapshot (2025-08-05)",
        max_input_tokens: 2e5,
        max_output_tokens: 32e3,
        multimodal: true
      },
      "claude-opus-4-20250514": {
        name: "Claude Opus 4 (2025-05-14)",
        id: "claude-opus-4-20250514",
        model_name: "claude-opus-4-20250514",
        description: "Anthropic Claude Opus 4 snapshot (2025-05-14)",
        max_input_tokens: 2e5,
        max_output_tokens: 32e3,
        multimodal: true
      },
      "claude-sonnet-4-20250514": {
        name: "Claude Sonnet 4 (2025-05-14)",
        id: "claude-sonnet-4-20250514",
        model_name: "claude-sonnet-4-20250514",
        description: "Anthropic Claude Sonnet 4 snapshot (2025-05-14)",
        max_input_tokens: 2e5,
        max_output_tokens: 64e3,
        multimodal: true
      },
      // ── Claude 3.7 family ───────────────────────────────────────────────────
      "claude-3-7-sonnet-latest": {
        name: "Claude 3.7 Sonnet (latest)",
        id: "claude-3-7-sonnet-latest",
        model_name: "claude-3-7-sonnet-latest",
        description: "Anthropic Claude 3.7 Sonnet (rolling-latest)",
        max_input_tokens: 2e5,
        max_output_tokens: 64e3,
        multimodal: true
      },
      "claude-3-7-sonnet-20250219": {
        name: "Claude 3.7 Sonnet (2025-02-19)",
        id: "claude-3-7-sonnet-20250219",
        model_name: "claude-3-7-sonnet-20250219",
        description: "Anthropic Claude 3.7 Sonnet snapshot (2025-02-19)",
        max_input_tokens: 2e5,
        max_output_tokens: 64e3,
        multimodal: true
      },
      // ── Claude 3.5 family ───────────────────────────────────────────────────
      "claude-3-5-sonnet-latest": {
        name: "Claude 3.5 Sonnet (latest)",
        id: "claude-3-5-sonnet-latest",
        model_name: "claude-3-5-sonnet-latest",
        description: "Anthropic Claude 3.5 Sonnet (rolling-latest)",
        max_input_tokens: 2e5,
        max_output_tokens: 8192,
        multimodal: true
      },
      "claude-3-5-sonnet-20241022": {
        name: "Claude 3.5 Sonnet (2024-10-22)",
        id: "claude-3-5-sonnet-20241022",
        model_name: "claude-3-5-sonnet-20241022",
        description: "Anthropic Claude 3.5 Sonnet snapshot (2024-10-22)",
        max_input_tokens: 2e5,
        max_output_tokens: 8192,
        multimodal: true
      },
      "claude-3-5-haiku-latest": {
        name: "Claude 3.5 Haiku (latest)",
        id: "claude-3-5-haiku-latest",
        model_name: "claude-3-5-haiku-latest",
        description: "Anthropic Claude 3.5 Haiku (rolling-latest)",
        max_input_tokens: 2e5,
        max_output_tokens: 8192
      },
      "claude-3-5-haiku-20241022": {
        name: "Claude 3.5 Haiku (2024-10-22)",
        id: "claude-3-5-haiku-20241022",
        model_name: "claude-3-5-haiku-20241022",
        description: "Anthropic Claude 3.5 Haiku snapshot (2024-10-22)",
        max_input_tokens: 2e5,
        max_output_tokens: 8192
      },
      // ── Claude 3 family ─────────────────────────────────────────────────────
      "claude-3-opus-latest": {
        name: "Claude 3 Opus (latest)",
        id: "claude-3-opus-latest",
        model_name: "claude-3-opus-latest",
        description: "Anthropic Claude 3 Opus (rolling-latest)",
        max_input_tokens: 2e5,
        max_output_tokens: 4096,
        multimodal: true
      },
      "claude-3-opus-20240229": {
        name: "Claude 3 Opus (2024-02-29)",
        id: "claude-3-opus-20240229",
        model_name: "claude-3-opus-20240229",
        description: "Anthropic Claude 3 Opus snapshot (2024-02-29)",
        max_input_tokens: 2e5,
        max_output_tokens: 4096,
        multimodal: true
      },
      "claude-3-sonnet-20240229": {
        name: "Claude 3 Sonnet (2024-02-29)",
        id: "claude-3-sonnet-20240229",
        model_name: "claude-3-sonnet-20240229",
        description: "Anthropic Claude 3 Sonnet snapshot (2024-02-29)",
        max_input_tokens: 2e5,
        max_output_tokens: 4096,
        multimodal: true
      },
      "claude-3-haiku-20240307": {
        name: "Claude 3 Haiku (2024-03-07)",
        id: "claude-3-haiku-20240307",
        model_name: "claude-3-haiku-20240307",
        description: "Anthropic Claude 3 Haiku snapshot (2024-03-07)",
        max_input_tokens: 2e5,
        max_output_tokens: 4096,
        multimodal: true
      }
    };
  }
};
var SmartChatModelAnthropicRequestAdapter = class extends SmartChatModelRequestAdapter {
  /**
   * Convert request to Anthropic format
   * @returns {Object} Request parameters in Anthropic format
   */
  to_platform(streaming = false) {
    return this.to_anthropic(streaming);
  }
  /**
   * Convert request to Anthropic format
   * @returns {Object} Request parameters in Anthropic format
   */
  to_anthropic(streaming = false) {
    this.anthropic_body = {
      model: this.model_id,
      max_tokens: this.max_tokens,
      temperature: this.temperature,
      stream: streaming
    };
    this.anthropic_body.messages = this._transform_messages_to_anthropic();
    if (this.tools) {
      this.anthropic_body.tools = this._transform_tools_to_anthropic();
    }
    if (this.tool_choice) {
      this.anthropic_body.tool_choice = this.tool_choice === "auto" ? { type: "auto" } : { type: "tool", name: this.tool_choice.function.name };
    }
    return {
      url: this.adapter.endpoint,
      method: "POST",
      headers: this.get_headers(),
      body: JSON.stringify(this.anthropic_body)
    };
  }
  /**
   * Transform messages to Anthropic format
   * @returns {Array<Object>} Messages in Anthropic format
   * @private
   */
  _transform_messages_to_anthropic() {
    let anthropic_messages = [];
    for (const message of this.messages) {
      if (message.role === "system") {
        if (!this.anthropic_body.system) this.anthropic_body.system = "";
        else this.anthropic_body.system += "\n\n";
        this.anthropic_body.system += Array.isArray(message.content) ? message.content.map((part) => part.text).join("\n") : message.content;
      } else if (message.role === "tool") {
        const msg = {
          role: "user",
          content: [
            {
              type: "tool_result",
              tool_use_id: message.tool_call_id,
              content: message.content
            }
          ]
        };
        anthropic_messages.push(msg);
      } else {
        const msg = {
          role: this._get_anthropic_role(message.role),
          content: this._get_anthropic_content(message.content)
        };
        if (message.tool_calls?.length > 0) msg.content = this._transform_tool_calls_to_content(message.tool_calls);
        anthropic_messages.push(msg);
      }
    }
    return anthropic_messages;
  }
  /**
   * Transform tool calls to Anthropic format
   * @param {Array<Object>} tool_calls - Tool calls
   * @returns {Array<Object>} Tool calls in Anthropic format
   * @private
   */
  _transform_tool_calls_to_content(tool_calls) {
    return tool_calls.map((tool_call) => ({
      type: "tool_use",
      id: tool_call.id,
      name: tool_call.function.name,
      input: JSON.parse(tool_call.function.arguments)
    }));
  }
  /**
   * Transform role to Anthropic format
   * @param {string} role - Original role
   * @returns {string} Role in Anthropic format
   * @private
   */
  _get_anthropic_role(role) {
    const role_map = {
      function: "assistant",
      // Anthropic doesn't have a function role, so we'll treat it as assistant
      tool: "user"
    };
    return role_map[role] || role;
  }
  /**
   * Transform content to Anthropic format
   * @param {string|Array} content - Original content
   * @returns {string|Array} Content in Anthropic format
   * @private
   */
  _get_anthropic_content(content) {
    if (Array.isArray(content)) {
      return content.map((item) => {
        if (item.type === "text") return { type: "text", text: item.text };
        if (item.type === "image_url") {
          return {
            type: "image",
            source: {
              type: "base64",
              media_type: item.image_url.url.split(";")[0].split(":")[1],
              data: item.image_url.url.split(",")[1]
            }
          };
        }
        if (item.type === "file" && item.file?.filename?.toLowerCase().endsWith(".pdf")) {
          if (item.file?.file_data) {
            return {
              type: "document",
              source: {
                type: "base64",
                media_type: "application/pdf",
                data: item.file.file_data.split(",")[1]
              }
            };
          }
        }
        return item;
      });
    }
    return content;
  }
  /**
     * Transform tools to Anthropic format
     * @returns {Array<Object>} Tools in Anthropic format
     * @private
     */
  _transform_tools_to_anthropic() {
    if (!this.tools) return void 0;
    return this.tools.map((tool) => ({
      name: tool.function.name,
      description: tool.function.description,
      input_schema: tool.function.parameters
    }));
  }
};
var SmartChatModelAnthropicResponseAdapter = class extends SmartChatModelResponseAdapter {
  static get platform_res() {
    return {
      content: [],
      id: "",
      model: "",
      role: "assistant",
      stop_reason: null,
      stop_sequence: null,
      type: "message",
      usage: {
        input_tokens: 0,
        output_tokens: 0
      }
    };
  }
  /**
   * Convert response to OpenAI format
   * @returns {Object} Response in OpenAI format
   */
  to_openai() {
    if (this.error) return { error: normalize_error(this.error, this.status) };
    return {
      id: this._res.id,
      object: "chat.completion",
      created: Date.now(),
      choices: [
        {
          index: 0,
          message: this._transform_message_to_openai(),
          finish_reason: this._get_openai_finish_reason(this._res.stop_reason)
        }
      ],
      usage: this._transform_usage_to_openai()
    };
  }
  /**
   * Transform message to OpenAI format
   * @returns {Object} Message in OpenAI format
   * @private
   */
  _transform_message_to_openai() {
    const message = {
      role: "assistant",
      content: "",
      tool_calls: []
    };
    if (Array.isArray(this._res.content)) {
      for (const content of this._res.content) {
        if (content.type === "text") {
          message.content += (message.content ? "\n\n" : "") + content.text;
        } else if (content.type === "tool_use") {
          message.tool_calls.push({
            id: content.id,
            type: "function",
            function: {
              name: content.name,
              arguments: JSON.stringify(content.input)
            }
          });
        }
      }
    } else {
      message.content = this._res.content;
    }
    if (message.tool_calls.length === 0) {
      delete message.tool_calls;
    }
    return message;
  }
  /**
   * Transform finish reason to OpenAI format
   * @param {string} stop_reason - Original finish reason
   * @returns {string} Finish reason in OpenAI format
   * @private
   */
  _get_openai_finish_reason(stop_reason) {
    const reason_map = {
      "end_turn": "stop",
      "max_tokens": "length",
      "tool_use": "function_call"
    };
    return reason_map[stop_reason] || stop_reason;
  }
  /**
   * Transform usage statistics to OpenAI format
   * @returns {Object} Usage statistics in OpenAI format
   * @private
   */
  _transform_usage_to_openai() {
    if (!this._res.usage) {
      return {
        prompt_tokens: 0,
        completion_tokens: 0,
        total_tokens: 0
      };
    }
    return {
      prompt_tokens: this._res.usage.input_tokens || 0,
      completion_tokens: this._res.usage.output_tokens || 0,
      total_tokens: (this._res.usage.input_tokens || 0) + (this._res.usage.output_tokens || 0)
    };
  }
  handle_chunk(chunk) {
    if (!chunk.startsWith("data: ")) return;
    chunk = JSON.parse(chunk.slice(6));
    if (!this._res.content.length) {
      this._res.content = [
        {
          type: "text",
          text: ""
        }
      ];
    }
    if (chunk.message?.id) {
      this._res.id = chunk.message.id;
    }
    if (chunk.message?.model) {
      this._res.model = chunk.message.model;
    }
    if (chunk.message?.role) {
      this._res.role = chunk.message.role;
    }
    let raw;
    if (chunk.delta?.type === "text_delta") {
      const content = chunk.delta?.text;
      raw = content;
      this._res.content[0].text += content;
    }
    if (chunk.delta?.stop_reason) {
      this._res.stop_reason = chunk.delta.stop_reason;
    }
    if (chunk.usage) {
      this._res.usage = {
        ...this._res.usage,
        ...chunk.usage
      };
    }
    return raw;
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-chat-model/adapters/openai.js
var EXCLUDED_PREFIXES = [
  "text-",
  "davinci",
  "babbage",
  "ada",
  "curie",
  "dall-e",
  "whisper",
  "omni",
  "tts",
  "gpt-4o-mini-tts",
  "computer-use",
  "codex",
  "gpt-4o-transcribe",
  "gpt-4o-mini-transcribe",
  "gpt-4o-mini-realtime",
  "gpt-4o-realtime",
  "o4-mini-deep-research",
  "o3-deep-research",
  "gpt-image"
];
var SmartChatModelOpenaiAdapter = class extends SmartChatModelApiAdapter {
  static key = "openai";
  static defaults = {
    description: "OpenAI",
    type: "API",
    endpoint: "https://api.openai.com/v1/chat/completions",
    streaming: true,
    models_endpoint: "https://api.openai.com/v1/models",
    default_model: "gpt-5-nano",
    signup_url: "https://platform.openai.com/api-keys"
  };
  res_adapter = SmartChatModelOpenaiResponseAdapter;
  /**
   * Parse model data from OpenAI API response.
   * Filters for GPT models and adds context window information.
   * @param {Object} model_data - Raw model data from OpenAI
   * @returns {Object} Map of model objects with capabilities and limits
   */
  parse_model_data(model_data) {
    return model_data.data.filter((model) => !EXCLUDED_PREFIXES.some((m) => model.id.startsWith(m)) && !model.id.includes("-instruct")).reduce((acc, model) => {
      const out = {
        model_name: model.id,
        id: model.id,
        multimodal: true,
        max_input_tokens: get_max_input_tokens(model.id)
      };
      acc[model.id] = out;
      return acc;
    }, {});
  }
  /**
   * Override the HTTP method for fetching models.
   */
  models_endpoint_method = "GET";
  /**
   * Test the API key by attempting to fetch models.
   * @deprecated in favor of smart_model.test_model (should be safe to remove 2026-02-10)
   * @returns {Promise<boolean>} True if API key is valid
   */
  async test_api_key() {
    const models = await this.get_models();
    return models.length > 0;
  }
  /**
   * Get settings configuration for OpenAI adapter.
   * Adds image resolution setting for multimodal models.
   * @returns {Object} Settings configuration object
   */
  get settings_config() {
    const config = super.settings_config;
    config["[CHAT_ADAPTER].open_ai_note"] = {
      name: "Note about using OpenAI",
      type: "html",
      value: "<b>OpenAI models:</b> Some models require extra verification steps in your OpenAI account for them to appear in the model list."
    };
    return config;
  }
};
function get_max_input_tokens(model_id) {
  if (model_id.startsWith("gpt-4.1")) {
    return 1e6;
  }
  if (model_id.startsWith("o")) {
    return 2e5;
  }
  if (model_id.startsWith("gpt-5")) {
    return 4e5;
  }
  if (model_id.startsWith("gpt-4o") || model_id.startsWith("gpt-4.5") || model_id.startsWith("gpt-4-turbo")) {
    return 128e3;
  }
  if (model_id.startsWith("gpt-4")) {
    return 8192;
  }
  if (model_id.startsWith("gpt-3")) {
    return 16385;
  }
  return 8e3;
}
var SmartChatModelOpenaiResponseAdapter = class extends SmartChatModelResponseAdapter {
};

// node_modules/obsidian-smart-env/node_modules/smart-chat-model/adapters/azure.js
var SmartChatModelAzureAdapter = class extends SmartChatModelOpenaiAdapter {
  static key = "azure";
  static defaults = {
    description: "Azure OpenAI",
    type: "API",
    adapter: "AzureOpenAI",
    streaming: true,
    api_key_header: "api-key",
    azure_resource_name: "",
    azure_deployment_name: "",
    azure_api_version: "2024-10-01-preview",
    default_model: "gpt-35-turbo",
    signup_url: "https://learn.microsoft.com/azure/cognitive-services/openai/quickstart?tabs=command-line",
    models_endpoint: "https://{azure_resource_name}.openai.azure.com/openai/deployments?api-version={azure_api_version}"
  };
  /**
   * Override the settings configuration to include Azure-specific fields.
   */
  get settings_config() {
    return {
      ...super.settings_config,
      "[CHAT_ADAPTER].azure_resource_name": {
        name: "Azure Resource Name",
        type: "text",
        description: "The name of your Azure OpenAI resource (e.g. 'my-azure-openai').",
        default: ""
      },
      "[CHAT_ADAPTER].azure_deployment_name": {
        name: "Azure Deployment Name",
        type: "text",
        description: "The name of your specific model deployment (e.g. 'gpt35-deployment').",
        default: ""
      },
      "[CHAT_ADAPTER].azure_api_version": {
        name: "Azure API Version",
        type: "text",
        description: "The API version for Azure OpenAI (e.g. '2024-10-01-preview').",
        default: "2024-10-01-preview"
      }
    };
  }
  /**
   * Build the endpoint dynamically based on Azure settings.
   * Example:
   *  https://<RESOURCE>.openai.azure.com/openai/deployments/<DEPLOYMENT>/chat/completions?api-version=2023-05-15
   */
  get endpoint() {
    const { azure_resource_name, azure_deployment_name, azure_api_version } = this.model.data;
    return `https://${azure_resource_name}.openai.azure.com/openai/deployments/${azure_deployment_name}/chat/completions?api-version=${azure_api_version}`;
  }
  /**
   * For streaming, we can reuse the same endpoint. 
   * The request body includes `stream: true` which the base class uses.
   */
  get endpoint_streaming() {
    return this.endpoint;
  }
  /**
   * The models endpoint for retrieving a list of your deployments.
   * E.g.:
   *   https://<RESOURCE>.openai.azure.com/openai/deployments?api-version=2023-05-15
   */
  get models_endpoint() {
    const { azure_resource_name, azure_api_version } = this.model.data;
    return `https://${azure_resource_name}.openai.azure.com/openai/deployments?api-version=${azure_api_version}`;
  }
  /**
   * Azure returns a list of deployments in the shape:
   * {
   *   "object": "list",
   *   "data": [
   *     {
   *       "id": "mydeployment",
   *       "model": "gpt-35-turbo",
   *       "status": "succeeded",
   *       "createdAt": ...
   *       "updatedAt": ...
   *       ...
   *     },
   *     ...
   *   ]
   * }
   * We'll parse them into a dictionary keyed by deployment ID.
   */
  parse_model_data(model_data) {
    if (model_data.object !== "list" || !Array.isArray(model_data.data)) {
      return { "_": { id: "No deployments found." } };
    }
    const parsed = {};
    for (const d of model_data.data) {
      parsed[d.id] = {
        model_name: d.id,
        id: d.id,
        raw: d,
        // You can add more details if you want:
        description: `Model: ${d.model}, Status: ${d.status}`,
        // Hard to guess tokens; omit or guess:
        max_input_tokens: 4e3
      };
    }
    return parsed;
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-chat-model/adapters/google.js
var SmartChatModelGoogleAdapter = class extends SmartChatModelApiAdapter {
  static key = "google";
  static defaults = {
    description: "Google (Gemini)",
    type: "API",
    api_key_header: "none",
    endpoint: "https://generativelanguage.googleapis.com/v1beta/models/MODEL_NAME:generateContent",
    endpoint_streaming: "https://generativelanguage.googleapis.com/v1beta/models/MODEL_NAME:streamGenerateContent",
    streaming: true,
    adapter: "Gemini",
    models_endpoint: "https://generativelanguage.googleapis.com/v1beta/models",
    default_model: "gemini-1.5-pro",
    signup_url: "https://ai.google.dev/"
  };
  streaming_chunk_splitting_regex = /(\r\n|\n|\r){2}/g;
  // handle Google's BS (split on double newlines only)
  /**
   * Get request adapter class
   */
  req_adapter = SmartChatModelGeminiRequestAdapter;
  /**
   * Get response adapter class
   */
  res_adapter = SmartChatModelGeminiResponseAdapter;
  /**
   * Uses Gemini's dedicated token counting endpoint
   */
  async count_tokens(input) {
    const req = {
      url: `https://generativelanguage.googleapis.com/v1beta/models/${this.model_key}:countTokens?key=${this.api_key}`,
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(this.prepare_token_count_body(input))
    };
    const resp = await this.http_adapter.request(req);
    return resp.json.totalTokens;
  }
  /**
   * Formats input for token counting based on type
   * @private
   */
  prepare_token_count_body(input) {
    if (typeof input === "string") {
      return { contents: [{ parts: [{ text: input }] }] };
    } else if (Array.isArray(input)) {
      return { contents: input.map((msg) => this.transform_message_for_token_count(msg)) };
    } else if (typeof input === "object") {
      return { contents: [this.transform_message_for_token_count(input)] };
    }
    throw new Error("Invalid input for count_tokens");
  }
  /**
   * Transforms message for token counting, handling text and images
   * @private
   */
  transform_message_for_token_count(message) {
    return {
      role: message.role === "assistant" ? "model" : message.role,
      parts: Array.isArray(message.content) ? message.content.map((part) => {
        if (part.type === "text") return { text: part.text };
        if (part.type === "image_url") return {
          inline_data: {
            mime_type: part.image_url.url.split(";")[0].split(":")[1],
            data: part.image_url.url.split(",")[1]
          }
        };
        return part;
      }) : [{ text: message.content }]
    };
  }
  /**
   * Builds endpoint URLs with model and API key
   */
  get endpoint() {
    return `https://generativelanguage.googleapis.com/v1beta/models/${this.model_key}:generateContent?key=${this.api_key}`;
  }
  get endpoint_streaming() {
    return `https://generativelanguage.googleapis.com/v1beta/models/${this.model_key}:streamGenerateContent?key=${this.api_key}`;
  }
  // /**
  //  * Extracts text from Gemini's streaming format
  //  */
  // get_text_chunk_from_stream(event) {
  //   const data = JSON.parse(event.data);
  //   return data.candidates[0]?.content?.parts[0]?.text || '';
  // }
  /**
   * Get models endpoint URL with API key
   * @returns {string} Complete models endpoint URL
   */
  get models_endpoint() {
    return `${this.constructor.defaults.models_endpoint}?key=${this.api_key}`;
  }
  /**
   * Get HTTP method for models endpoint
   * @returns {string} HTTP method ("GET")
   */
  get models_endpoint_method() {
    return "GET";
  }
  get models_request_params() {
    return {
      url: this.models_endpoint,
      method: this.models_endpoint_method
    };
  }
  /**
   * Parse model data from Gemini API response
   * @param {Object} model_data - Raw model data from API
   * @returns {Object} Map of model objects with capabilities and limits
   */
  parse_model_data(model_data) {
    return model_data.models.filter((model) => model.name.startsWith("models/gemini")).reduce((acc, model) => {
      const out = {
        model_name: model.name.split("/").pop(),
        id: model.name.split("/").pop(),
        max_input_tokens: model.inputTokenLimit,
        max_output_tokens: model.maxOutputTokens,
        description: model.description,
        multimodal: model.name.includes("vision") || model.description.includes("multimodal"),
        raw: model
      };
      acc[model.name.split("/").pop()] = out;
      return acc;
    }, {});
  }
  is_end_of_stream(event) {
    return event.data.includes('"finishReason"');
    return false;
  }
};
var SmartChatModelGeminiRequestAdapter = class extends SmartChatModelRequestAdapter {
  to_platform(streaming = false) {
    return this.to_gemini(streaming);
  }
  to_gemini(streaming = false) {
    const gemini_body = {
      contents: this._transform_messages_to_gemini(),
      generationConfig: {
        temperature: this.temperature,
        maxOutputTokens: this.max_tokens,
        topK: this._req.topK || 1,
        topP: this._req.topP || 1,
        stopSequences: this._req.stop || []
      },
      safetySettings: [
        {
          category: "HARM_CATEGORY_HARASSMENT",
          threshold: "BLOCK_NONE"
        },
        {
          category: "HARM_CATEGORY_HATE_SPEECH",
          threshold: "BLOCK_NONE"
        },
        {
          category: "HARM_CATEGORY_SEXUALLY_EXPLICIT",
          threshold: "BLOCK_NONE"
        },
        {
          category: "HARM_CATEGORY_DANGEROUS_CONTENT",
          threshold: "BLOCK_NONE"
        }
      ]
    };
    if (this.tools) gemini_body.tools = this._transform_tools_to_gemini();
    if (gemini_body.tools && this.tool_choice !== "none") gemini_body.tool_config = this._transform_tool_choice_to_gemini();
    return {
      url: streaming ? this.adapter.endpoint_streaming : this.adapter.endpoint,
      method: "POST",
      headers: this.get_headers(),
      body: JSON.stringify(gemini_body)
    };
  }
  _transform_messages_to_gemini() {
    let gemini_messages = [];
    let system_message = "";
    for (const message of this.messages) {
      if (message.role === "system") {
        system_message += message.content + "\n";
      } else {
        gemini_messages.push({
          role: this._get_gemini_role(message.role),
          parts: this._transform_content_to_gemini(message.content)
        });
      }
    }
    if (system_message) {
      gemini_messages.unshift({
        role: "user",
        parts: [{ text: system_message.trim() }]
      });
    }
    return gemini_messages;
  }
  _get_gemini_role(role) {
    const role_map = {
      user: "user",
      assistant: "model",
      function: "model"
      // Gemini doesn't have a function role, so we'll treat it as model
    };
    return role_map[role] || role;
  }
  _transform_content_to_gemini(content) {
    if (Array.isArray(content)) {
      return content.map((part) => {
        if (part.type === "text") return { text: part.text };
        if (part.type === "image_url") {
          let mime_type = part.image_url.url.split(";")[0].split(":")[1];
          if (mime_type === "image/jpg") mime_type = "image/jpeg";
          return {
            inline_data: {
              mime_type,
              data: part.image_url.url.split(",")[1]
            }
          };
        }
        if (part.type === "file" && part.file?.filename?.toLowerCase().endsWith(".pdf")) {
          if (part.file?.file_data) {
            return {
              inline_data: {
                mime_type: "application/pdf",
                data: part.file.file_data.split(",")[1]
              }
            };
          }
        }
        return part;
      });
    }
    return [{ text: content }];
  }
  _transform_tools_to_gemini() {
    return [{
      function_declarations: this.tools.map((tool) => ({
        name: tool.function.name,
        description: tool.function.description,
        parameters: tool.function.parameters
      }))
    }];
  }
  _transform_tool_choice_to_gemini() {
    return {
      function_calling_config: {
        mode: "ANY",
        allowed_function_names: this.tools.map((tool) => tool.function.name)
      }
    };
  }
};
var SmartChatModelGeminiResponseAdapter = class extends SmartChatModelResponseAdapter {
  static get platform_res() {
    return {
      candidates: [{
        content: {
          parts: [
            {
              text: ""
            }
          ],
          role: ""
        },
        finishReason: ""
      }],
      promptFeedback: {},
      usageMetadata: {}
    };
  }
  to_openai() {
    if (this.error) return { error: normalize_error(this.error, this.status) };
    const first_candidate = this._res.candidates[0];
    if (!this._res.id) this._res.id = "gemini-" + Date.now().toString();
    return {
      id: this._res.id,
      object: "chat.completion",
      created: Date.now(),
      model: this.adapter.model_key,
      choices: [{
        index: 0,
        message: first_candidate?.content ? this._transform_message_to_openai(first_candidate.content) : "",
        finish_reason: this._get_openai_finish_reason(first_candidate.finishReason)
      }],
      usage: this._transform_usage_to_openai()
    };
  }
  _transform_message_to_openai(content) {
    const message = {
      role: "assistant",
      content: content.parts.filter((part) => part.text).map((part) => part.text).join("")
    };
    const function_call = content.parts.find((part) => part.functionCall);
    if (function_call) {
      message.tool_calls = [{
        type: "function",
        function: {
          name: function_call.functionCall.name,
          arguments: JSON.stringify(function_call.functionCall.args)
        }
      }];
    }
    return message;
  }
  _get_openai_finish_reason(finish_reason) {
    const reason_map = {
      "STOP": "stop",
      "MAX_TOKENS": "length",
      "SAFETY": "content_filter",
      "RECITATION": "content_filter",
      "OTHER": "null"
    };
    return reason_map[finish_reason] || finish_reason.toLowerCase();
  }
  _transform_usage_to_openai() {
    if (!this._res.usageMetadata) {
      return {
        prompt_tokens: null,
        completion_tokens: null,
        total_tokens: null
      };
    }
    return {
      prompt_tokens: this._res.usageMetadata.promptTokenCount || null,
      completion_tokens: this._res.usageMetadata.candidatesTokenCount || null,
      total_tokens: this._res.usageMetadata.totalTokenCount || null
    };
  }
  handle_chunk(chunk) {
    let chunk_trimmed = chunk.trim();
    if (["[", ","].includes(chunk_trimmed[0])) chunk_trimmed = chunk_trimmed.slice(1);
    if (["]", ","].includes(chunk_trimmed[chunk_trimmed.length - 1])) chunk_trimmed = chunk_trimmed.slice(0, -1);
    const data = JSON.parse(chunk_trimmed);
    let raw;
    if (data.candidates?.[0]?.content?.parts?.[0]?.text?.length) {
      const content = data.candidates[0].content.parts[0].text;
      raw = content;
      this._res.candidates[0].content.parts[0].text += content;
    }
    if (data.candidates?.[0]?.content?.role?.length) {
      this._res.candidates[0].content.role = data.candidates[0].content.role;
    }
    if (data.candidates?.[0]?.finishReason?.length) {
      this._res.candidates[0].finishReason += data.candidates[0].finishReason;
    }
    if (data.promptFeedback) {
      this._res.promptFeedback = {
        ...this._res.promptFeedback || {},
        ...data.promptFeedback
      };
    }
    if (data.usageMetadata) {
      this._res.usageMetadata = {
        ...this._res.usageMetadata || {},
        ...data.usageMetadata
      };
    }
    if (data.candidates?.[0]?.content?.parts?.[0]?.functionCall) {
      if (!this._res.candidates[0].content.parts[0].functionCall) {
        this._res.candidates[0].content.parts[0].functionCall = {
          name: "",
          args: {}
        };
      }
      this._res.candidates[0].content.parts[0].functionCall.name += data.candidates[0].content.parts[0].functionCall.name;
      if (data.candidates[0].content.parts[0].functionCall.args) {
        Object.entries(data.candidates[0].content.parts[0].functionCall.args).forEach(([key, value]) => {
          if (!this._res.candidates[0].content.parts[0].functionCall.args[key]) {
            this._res.candidates[0].content.parts[0].functionCall.args[key] = "";
          }
          this._res.candidates[0].content.parts[0].functionCall.args[key] += value;
        });
      }
    }
    return raw;
  }
};
var SmartChatModelGeminiAdapter = class extends SmartChatModelGoogleAdapter {
  static key = "gemini";
  static defaults = {
    description: "Gemini (SWITCH TO **GOOGLE** ADAPTER)",
    type: "API",
    api_key_header: "none",
    endpoint: "https://generativelanguage.googleapis.com/v1beta/models/MODEL_NAME:generateContent",
    endpoint_streaming: "https://generativelanguage.googleapis.com/v1beta/models/MODEL_NAME:streamGenerateContent",
    streaming: true,
    adapter: "Gemini",
    models_endpoint: "https://generativelanguage.googleapis.com/v1beta/models",
    default_model: "gemini-1.5-pro",
    signup_url: "https://ai.google.dev/"
  };
};

// node_modules/obsidian-smart-env/node_modules/smart-chat-model/adapters/open_router.js
var SmartChatModelOpenRouterAdapter = class extends SmartChatModelApiAdapter {
  static key = "open_router";
  static models_dev_key = "openrouter";
  static defaults = {
    description: "Open Router",
    type: "API",
    endpoint: "https://openrouter.ai/api/v1/chat/completions",
    streaming: true,
    adapter: "OpenRouter",
    models_endpoint: "https://openrouter.ai/api/v1/models",
    default_model: "mistralai/mistral-7b-instruct:free",
    signup_url: "https://accounts.openrouter.ai/sign-up?redirect_url=https%3A%2F%2Fopenrouter.ai%2Fkeys"
  };
  /**
   * Get request adapter class
   * @returns {typeof SmartChatModelOpenRouterRequestAdapter} Request adapter class
   */
  get req_adapter() {
    return SmartChatModelOpenRouterRequestAdapter;
  }
  /**
   * Get response adapter class
   * @returns {typeof SmartChatModelOpenRouterResponseAdapter} Response adapter class
   */
  get res_adapter() {
    return SmartChatModelOpenRouterResponseAdapter;
  }
  /**
   * Count tokens in input text (rough estimate)
   * @param {string|Object} input - Text to count tokens for
   * @returns {Promise<number>} Estimated token count
   */
  async count_tokens(input) {
    const text = typeof input === "string" ? input : JSON.stringify(input);
    return Math.ceil(text.length / 4);
  }
  get models_request_params() {
    return {
      url: this.models_endpoint,
      method: "GET"
    };
  }
  /**
   * Parse model data from OpenRouter API response
   * @param {Object} model_data - Raw model data
   * @returns {Object} Map of model objects with capabilities and limits
   */
  parse_model_data(model_data) {
    if (model_data.data) {
      model_data = model_data.data;
    }
    if (model_data.error) throw new Error(model_data.error);
    return model_data.reduce((acc, model) => {
      acc[model.id] = {
        model_name: model.id,
        id: model.id,
        max_input_tokens: model.context_length,
        name: model.name,
        description: model.name,
        long_desc: model.description,
        multimodal: model.architecture.modality === "multimodal",
        raw: model
      };
      return acc;
    }, {});
  }
};
var SmartChatModelOpenRouterRequestAdapter = class extends SmartChatModelRequestAdapter {
  to_platform(stream = false) {
    const req = this.to_openai(stream);
    return req;
  }
  _get_openai_content(message) {
    if (message.role === "user") {
      if (Array.isArray(message.content) && message.content.every((part) => part.type === "text")) {
        return message.content.map((part) => part.text).join("\n");
      }
    }
    return message.content;
  }
};
var SmartChatModelOpenRouterResponseAdapter = class extends SmartChatModelResponseAdapter {
  static get platform_res() {
    return {
      id: "",
      object: "chat.completion",
      created: 0,
      model: "",
      choices: [],
      usage: {}
    };
  }
  to_platform() {
    return this.to_openai();
  }
  get object() {
    return "chat.completion";
  }
  get error() {
    if (!this._res.error) return null;
    const error = this._res.error;
    if (!error.message) error.message = "";
    if (this._res.error.metadata?.raw) {
      if (typeof this._res.error.metadata.raw === "string") {
        error.message += `

${this._res.error.metadata.raw}`;
      } else {
        error.message += `

${JSON.stringify(this._res.error.metadata.raw, null, 2)}`;
      }
    }
    if (error.message.startsWith("No cookie auth")) {
      error.suggested_action = "Ensure your Open Router API key is set correctly.";
    }
    return error;
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-chat-model/adapters/lm_studio.js
var SmartChatModelLmStudioAdapter = class extends SmartChatModelApiAdapter {
  static key = "lm_studio";
  /** @type {import('./_adapter.js').SmartChatModelAdapter['constructor']['defaults']} */
  static defaults = {
    description: "LM Studio (OpenAI\u2011compatible)",
    type: "API",
    endpoint: "http://localhost:1234/v1/chat/completions",
    streaming: true,
    adapter: "LM_Studio_OpenAI_Compat",
    models_endpoint: "http://localhost:1234/v1/models",
    default_model: "",
    signup_url: "https://lmstudio.ai/docs/api/openai-api",
    api_key: "no api key required"
  };
  /* ------------------------------------------------------------------ *
   *  Request / Response classes
   * ------------------------------------------------------------------ */
  get req_adapter() {
    return SmartChatModelLmStudioRequestAdapter;
  }
  get res_adapter() {
    return SmartChatModelLmStudioResponseAdapter;
  }
  /* ------------------------------------------------------------------ *
   *  Settings
   * ------------------------------------------------------------------ */
  /**
   * Extend the base settings with a read‑only HTML block that reminds the
   * user to enable CORS inside LM Studio. The Smart View renderer treats
   * `type: "html"` as a static fragment, so no extra runtime logic is needed.
   */
  get settings_config() {
    const config = super.settings_config;
    delete config["[CHAT_ADAPTER].api_key"];
    return {
      ...config,
      "[CHAT_ADAPTER].cors_instructions": {
        /* visible only when this adapter is selected */
        name: "CORS required",
        type: "html",
        value: `<p>Before you can use LM Studio you must <strong>Enable CORS</strong> inside LM Studio \u2192 Developer \u2192 Settings</p>`
      }
    };
  }
  /* ------------------------------------------------------------------ *
   *  Model list helpers
   * ------------------------------------------------------------------ */
  /**
   * LM Studio returns an OpenAI‑style list; normalise to the project shape.
   */
  parse_model_data(model_data) {
    if (model_data.object !== "list" || !Array.isArray(model_data.data)) {
      return { _: { id: "No models found." } };
    }
    const out = {};
    for (const m of model_data.data) {
      out[m.id] = {
        id: m.id,
        model_name: m.id,
        description: `LM Studio model: ${m.id}`,
        multimodal: false
      };
    }
    return out;
  }
  get models_endpoint_method() {
    return "get";
  }
  /**
   * Count tokens in input text (no dedicated endpoint)
   * Rough estimate: 1 token ~ 4 chars
   * @param {string|Object} input
   * @returns {Promise<number>}
   */
  async count_tokens(input) {
    const text = typeof input === "string" ? input : JSON.stringify(input);
    return Math.ceil(text.length / 4);
  }
  /**
   * Test API key - LM Studio doesn't require API key. Always true.
   * @deprecated in favor of smart_model.test_model (should be safe to remove 2026-02-10)
   * @returns {Promise<boolean>}
   */
  async test_api_key() {
    return true;
  }
  get api_key() {
    return "no api key required";
  }
};
var SmartChatModelLmStudioRequestAdapter = class extends SmartChatModelRequestAdapter {
  to_platform(streaming = false) {
    const req = this.to_openai(streaming);
    const body = JSON.parse(req.body);
    if (this.tool_choice?.function?.name) {
      const last_msg = body.messages[body.messages.length - 1];
      if (typeof last_msg.content === "string") {
        last_msg.content = [
          { type: "text", text: last_msg.content }
        ];
      }
      last_msg.content.push({
        type: "text",
        text: `Use the "${this.tool_choice.function.name}" tool.`
      });
      body.tool_choice = "required";
    } else if (body.tool_choice && typeof body.tool_choice === "object") {
      body.tool_choice = "auto";
    }
    req.body = JSON.stringify(body);
    return req;
  }
};
var SmartChatModelLmStudioResponseAdapter = class extends SmartChatModelResponseAdapter {
};

// node_modules/obsidian-smart-env/node_modules/smart-chat-model/adapters/ollama.js
var SmartChatModelOllamaAdapter = class extends SmartChatModelApiAdapter {
  static key = "ollama";
  static defaults = {
    description: "Ollama (Local)",
    type: "API",
    // models_endpoint: "http://localhost:11434/api/tags",
    // endpoint: "http://localhost:11434/api/chat",
    api_key: "na",
    host: "http://localhost:11434",
    endpoint: "/api/chat",
    models_endpoint: "/api/tags",
    // streaming: false, // TODO: Implement streaming
    streaming: true
  };
  req_adapter = SmartChatModelOllamaRequestAdapter;
  res_adapter = SmartChatModelOllamaResponseAdapter;
  get host() {
    return this.model.data.host || this.constructor.defaults.host;
  }
  get endpoint() {
    return `${this.host}${this.constructor.defaults.endpoint}`;
  }
  get models_endpoint() {
    return `${this.host}${this.constructor.defaults.models_endpoint}`;
  }
  get model_show_endpoint() {
    return `${this.host}/api/show`;
  }
  get models_endpoint_method() {
    return "GET";
  }
  /**
   * Get available models from local Ollama instance
   * @param {boolean} [refresh=false] - Whether to refresh cached models
   * @returns {Promise<Object>} Map of model objects
   */
  async get_models(refresh = false) {
    if (!refresh && typeof this.model_data === "object" && Object.keys(this.model_data || {}).length > 0 && this.model_data_loaded_at && time_now - this.model_data_loaded_at < 1 * 60 * 60 * 1e3) return this.model_data;
    try {
      const list_resp = await this.http_adapter.request(this.models_request_params);
      const list_data = await list_resp.json();
      const models_raw_data = [];
      for (const model of list_data.models) {
        const model_details_resp = await this.http_adapter.request({
          url: this.model_show_endpoint,
          method: "POST",
          body: JSON.stringify({ model: model.name })
        });
        const model_details_data = await model_details_resp.json();
        models_raw_data.push({ ...model_details_data, name: model.name });
      }
      this.model_data = this.parse_model_data(models_raw_data);
      await this.get_enriched_model_data();
      this.model.data.provider_models = this.model_data;
      if (typeof this.model.re_render_settings === "function") {
        this.model.re_render_settings();
      }
      this.model_data_loaded_at = Date.now();
      return this.model_data;
    } catch (error) {
      console.error("Failed to fetch model data:", error);
      return { "_": { id: `Failed to fetch models from ${this.model.adapter_name}` } };
    }
  }
  /**
   * Parse model data from Ollama API response
   * @param {Object[]} model_data - Raw model data from Ollama
   * @returns {Object} Map of model objects with capabilities and limits
   */
  parse_model_data(model_data) {
    if (!Array.isArray(model_data)) {
      this.model_data = {};
      console.error("Invalid model data format from Ollama:", model_data);
      return {};
    }
    if (model_data.length === 0) {
      this.model_data = { "no_models_available": {
        id: "no_models_available",
        name: "No models currently available"
      } };
      return this.model_data;
    }
    return model_data.reduce((acc, model) => {
      if (model.name.includes("embed")) return acc;
      const context_entry = Object.entries(model.model_info || {}).find((m) => m[0].includes(".context_length"));
      const out = {
        model_name: model.name,
        id: model.name,
        multimodal: false,
        max_input_tokens: context_entry?.[1] || 4096
      };
      acc[model.name] = out;
      return acc;
    }, {});
  }
  /**
   * Override settings config to remove API key setting since not needed for local instance
   * @returns {Object} Settings configuration object
   */
  get settings_config() {
    const config = super.settings_config;
    delete config["[CHAT_ADAPTER].api_key"];
    config["[CHAT_ADAPTER].host"] = {
      name: "Ollama host",
      type: "text",
      description: "Enter the host for your Ollama instance",
      default: this.constructor.defaults.host
    };
    return config;
  }
  is_end_of_stream(event) {
    return event.data.includes('"done_reason"');
  }
};
var SmartChatModelOllamaRequestAdapter = class extends SmartChatModelRequestAdapter {
  /**
   * Convert request to Ollama format
   * @returns {Object} Request parameters in Ollama format
   */
  to_platform(streaming = false) {
    const ollama_body = {
      model: this.model_id,
      messages: this._transform_messages_to_ollama(),
      options: this._transform_parameters_to_ollama(),
      stream: streaming || this.stream
      // format: 'json', // only used for tool calls since returns JSON in content body
    };
    if (this.tools) {
      ollama_body.tools = this._transform_functions_to_tools();
      if (this.tool_choice?.function?.name) {
        ollama_body.messages[ollama_body.messages.length - 1].content += `

Use the "${this.tool_choice.function.name}" tool.`;
        ollama_body.format = "json";
      }
    }
    return {
      url: this.adapter.endpoint,
      method: "POST",
      body: JSON.stringify(ollama_body)
    };
  }
  /**
   * Transform messages to Ollama format
   * @returns {Array} Messages in Ollama format
   * @private
   */
  _transform_messages_to_ollama() {
    return this.messages.map((message) => {
      const ollama_message = {
        role: message.role,
        content: this._transform_content_to_ollama(message.content)
      };
      const images = this._extract_images_from_content(message.content);
      if (images.length > 0) {
        ollama_message.images = images.map((img) => img.replace(/^data:image\/[^;]+;base64,/, ""));
      }
      return ollama_message;
    });
  }
  /**
   * Transform content to Ollama format
   * @param {string|Array} content - Message content
   * @returns {string} Content in Ollama format
   * @private
   */
  _transform_content_to_ollama(content) {
    if (Array.isArray(content)) {
      return content.filter((item) => item.type === "text").map((item) => item.text).join("\n");
    }
    return content;
  }
  /**
   * Extract images from content
   * @param {string|Array} content - Message content
   * @returns {Array} Array of image URLs
   * @private
   */
  _extract_images_from_content(content) {
    if (!Array.isArray(content)) return [];
    return content.filter((item) => item.type === "image_url").map((item) => item.image_url.url);
  }
  /**
   * Transform functions to tools format
   * @returns {Array} Tools array in Ollama format
   * @private
   */
  _transform_functions_to_tools() {
    return this.tools;
  }
  /**
   * Transform parameters to Ollama options format
   * @returns {Object} Options in Ollama format
   * @private
   */
  _transform_parameters_to_ollama() {
    const options = {};
    if (this.max_tokens) options.num_predict = this.max_tokens;
    if (this.temperature) options.temperature = this.temperature;
    if (this.top_p) options.top_p = this.top_p;
    if (this.frequency_penalty) options.frequency_penalty = this.frequency_penalty;
    if (this.presence_penalty) options.presence_penalty = this.presence_penalty;
    return options;
  }
};
var SmartChatModelOllamaResponseAdapter = class extends SmartChatModelResponseAdapter {
  static get platform_res() {
    return {
      model: "",
      created_at: null,
      message: {
        role: "",
        content: ""
      },
      total_duration: 0,
      load_duration: 0,
      prompt_eval_count: 0,
      prompt_eval_duration: 0,
      eval_count: 0,
      eval_duration: 0
    };
  }
  /**
   * Convert response to OpenAI format
   * @returns {Object} Response in OpenAI format
   */
  to_openai() {
    if (this.error) return { error: normalize_error(this.error, this.status) };
    return {
      id: this._res.created_at,
      object: "chat.completion",
      created: Date.now(),
      model: this._res.model,
      choices: [
        {
          index: 0,
          message: this._transform_message_to_openai(),
          finish_reason: this._res.done_reason
        }
      ],
      usage: this._transform_usage_to_openai()
    };
  }
  /**
   * Transform message to OpenAI format
   * @returns {Object} Message in OpenAI format
   * @private
   */
  _transform_message_to_openai() {
    return {
      role: this._res.message.role,
      content: this._res.message.content,
      tool_calls: this._res.message.tool_calls
    };
  }
  /**
   * Transform usage statistics to OpenAI format
   * @returns {Object} Usage statistics in OpenAI format
   * @private
   */
  _transform_usage_to_openai() {
    return {
      prompt_tokens: this._res.prompt_eval_count || 0,
      completion_tokens: this._res.eval_count || 0,
      total_tokens: (this._res.prompt_eval_count || 0) + (this._res.eval_count || 0)
    };
  }
  /**
   * Parse chunk adds delta to content as expected output format
   */
  handle_chunk(chunk) {
    chunk = JSON.parse(chunk || "{}");
    if (chunk.created_at && !this._res.created_at) {
      this._res.created_at = chunk.created_at;
    }
    let raw;
    if (chunk.message?.content) {
      const content = chunk.message.content;
      raw = content;
      this._res.message.content += content;
    }
    if (chunk.message?.role) {
      this._res.message.role = chunk.message.role;
    }
    if (chunk.model) {
      this._res.model = chunk.model;
    }
    if (chunk.message?.tool_calls) {
      if (!this._res.message.tool_calls) {
        this._res.message.tool_calls = [{
          id: "",
          type: "function",
          function: {
            name: "",
            arguments: ""
          }
        }];
      }
      if (chunk.message.tool_calls[0].id) {
        this._res.message.tool_calls[0].id += chunk.message.tool_calls[0].id;
      }
      if (chunk.message.tool_calls[0].function.name) {
        this._res.message.tool_calls[0].function.name += chunk.message.tool_calls[0].function.name;
      }
      if (chunk.message.tool_calls[0].function.arguments) {
        if (typeof chunk.message.tool_calls[0].function.arguments === "string") {
          this._res.message.tool_calls[0].function.arguments += chunk.message.tool_calls[0].function.arguments;
        } else {
          this._res.message.tool_calls[0].function.arguments = chunk.message.tool_calls[0].function.arguments;
        }
      }
    }
    return raw;
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-chat-model/adapters/_custom.js
var adapters_map = {
  "openai": {
    req: SmartChatModelRequestAdapter,
    res: SmartChatModelResponseAdapter
  },
  "anthropic": {
    req: SmartChatModelAnthropicRequestAdapter,
    res: SmartChatModelAnthropicResponseAdapter
  },
  "gemini": {
    req: SmartChatModelGeminiRequestAdapter,
    res: SmartChatModelGeminiResponseAdapter
  },
  "lm_studio": {
    req: SmartChatModelLmStudioRequestAdapter,
    res: SmartChatModelLmStudioResponseAdapter
  },
  "ollama": {
    req: SmartChatModelOllamaRequestAdapter,
    res: SmartChatModelOllamaResponseAdapter
  }
};
var SmartChatModelCustomAdapter = class extends SmartChatModelApiAdapter {
  static key = "custom";
  static defaults = {
    description: "Custom API (Local or Remote, OpenAI format)",
    type: "API",
    /**
     * new default property: 'api_adapter' indicates which
     * request/response adapter set to use internally
     */
    api_adapter: "openai"
  };
  /**
   * Provide dynamic request/response classes
   */
  /**
   * @override
   * @returns {typeof SmartChatModelRequestAdapter}
   */
  get req_adapter() {
    const adapter_name = this.model.data.api_adapter || "openai";
    const map_entry = adapters_map[adapter_name];
    return map_entry && map_entry.req ? map_entry.req : SmartChatModelRequestAdapter;
  }
  /**
   * @override
   * @returns {typeof SmartChatModelResponseAdapter}
   */
  get res_adapter() {
    const adapter_name = this.model.data.api_adapter || "openai";
    const map_entry = adapters_map[adapter_name];
    return map_entry && map_entry.res ? map_entry.res : SmartChatModelResponseAdapter;
  }
  /**
   * Synthesize a custom endpoint from the config fields.
   * All fields are optional; fallback to a minimal default.
   * @returns {string}
   */
  get endpoint() {
    const protocol = this.model.data.protocol || "http";
    const hostname = this.model.data.hostname || "localhost";
    const port = this.model.data.port ? `:${this.model.data.port}` : "";
    let path = this.model.data.path || "";
    if (path && !path.startsWith("/")) path = `/${path}`;
    return `${protocol}://${hostname}${port}${path}`;
  }
  get_adapters_as_options() {
    return Object.keys(adapters_map).map((adapter_name) => ({ value: adapter_name, name: adapter_name }));
  }
  /**
   * Provide custom settings for configuring
   * the user-defined fields plus the new 'api_adapter'.
   * @override
   * @returns {Object} settings configuration
   */
  get settings_config() {
    return {
      /**
       * Select which specialized request/response adapter
       * you'd like to use for your custom endpoint.
       */
      "[CHAT_ADAPTER].api_adapter": {
        name: "API Adapter",
        type: "dropdown",
        description: "Pick a built-in or external adapter to parse request/response data.",
        // Provide a short selection set, or dynamically gather from keys of adapters_map
        // options_callback: 'adapter.get_adapters_as_options',
        options_callback: () => {
          this.get_adapters_as_options();
        },
        // UNTESTED
        default: "openai"
      },
      "[CHAT_ADAPTER].id": {
        name: "Model Name",
        type: "text",
        description: "Enter the model name for your endpoint if needed."
      },
      "[CHAT_ADAPTER].protocol": {
        name: "Protocol",
        type: "text",
        description: "e.g. http or https"
      },
      "[CHAT_ADAPTER].hostname": {
        name: "Hostname",
        type: "text",
        description: "e.g. localhost or some.remote.host"
      },
      "[CHAT_ADAPTER].port": {
        name: "Port",
        type: "number",
        description: "Port number or leave blank"
      },
      "[CHAT_ADAPTER].path": {
        name: "Path",
        type: "text",
        description: "Path portion of the URL (leading slash optional)"
      },
      "[CHAT_ADAPTER].streaming": {
        name: "Streaming",
        type: "toggle",
        description: "Enable streaming if your API supports it."
      },
      "[CHAT_ADAPTER].max_input_tokens": {
        name: "Max Input Tokens",
        type: "number",
        description: "Max number of tokens your model can handle in the prompt."
      },
      "[CHAT_ADAPTER].api_key": {
        name: "API Key",
        type: "password",
        description: "If your service requires an API key, add it here."
      }
    };
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-chat-model/adapters/groq.js
var SmartChatModelGroqAdapter = class extends SmartChatModelApiAdapter {
  static key = "groq";
  static defaults = {
    description: "Groq",
    type: "API",
    endpoint: "https://api.groq.com/openai/v1/chat/completions",
    streaming: true,
    adapter: "Groq",
    models_endpoint: "https://api.groq.com/openai/v1/models",
    default_model: "llama3-8b-8192",
    signup_url: "https://groq.com"
  };
  /**
   * Request adapter class
   * @returns {typeof SmartChatModelGroqRequestAdapter}
   */
  get req_adapter() {
    return SmartChatModelGroqRequestAdapter;
  }
  /**
   * Response adapter class
   * @returns {typeof SmartChatModelGroqResponseAdapter}
   */
  get res_adapter() {
    return SmartChatModelGroqResponseAdapter;
  }
  get models_endpoint_method() {
    return "GET";
  }
  /**
   * Parse model data from Groq API format to a dictionary keyed by model ID.
   * The API returns a list of model objects like:
   * {
   *   "object": "list",
   *   "data": [ { "id": "...", "object": "model", ... }, ... ]
   * }
   * 
   * We'll convert each model to:
   * {
   *   model_name: model.id,
   *   id: model.id,
   *   max_input_tokens: model.context_window,
   *   description: `Owned by: ${model.owned_by}, context: ${model.context_window}`,
   *   multimodal: Check if model name or description suggests multimodality
   * }
   */
  parse_model_data(model_data) {
    if (model_data.object !== "list" || !Array.isArray(model_data.data)) {
      return { "_": { id: "No models found." } };
    }
    const parsed = {};
    for (const m of model_data.data) {
      parsed[m.id] = {
        model_name: m.id,
        id: m.id,
        max_input_tokens: m.context_window || 8192,
        description: `Owned by: ${m.owned_by}, context: ${m.context_window}`,
        // A basic heuristic for multimodal: if 'vision' or 'tool' is in model id
        // Adjust as needed based on known capabilities
        multimodal: m.id.includes("vision")
      };
    }
    return parsed;
  }
};
var SmartChatModelGroqRequestAdapter = class extends SmartChatModelRequestAdapter {
  _get_openai_content(message) {
    if (["assistant", "tool"].includes(message.role)) {
      if (Array.isArray(message.content)) {
        return message.content.map((part) => {
          if (typeof part === "string") return part;
          if (part?.text) return part.text;
          return "";
        }).join("\n");
      }
    }
    return message.content;
  }
};
var SmartChatModelGroqResponseAdapter = class extends SmartChatModelResponseAdapter {
};

// node_modules/obsidian-smart-env/node_modules/smart-chat-model/adapters/xai.js
var SmartChatModelXaiAdapter = class extends SmartChatModelApiAdapter {
  static key = "xai";
  static defaults = {
    description: "xAI Grok",
    type: "API",
    adapter: "xAI_Grok",
    endpoint: "https://api.x.ai/v1/chat/completions",
    streaming: true,
    models_endpoint: "https://api.x.ai/v1/models",
    default_model: "grok-3-mini-beta",
    signup_url: "https://ide.x.ai"
  };
  get req_adapter() {
    return XaiCompletionRequestAdapter;
  }
  get res_adapter() {
    return XaiCompletionResponseAdapter;
  }
  /* ------------------------------------------------------------------ *
   *  Model-list helpers
   * ------------------------------------------------------------------ */
  get models_endpoint_method() {
    return "GET";
  }
  parse_model_data(model_data = {}) {
    const list = model_data.data || model_data.models || [];
    return list.reduce((acc, m) => {
      const id = m.id || m.name;
      acc[id] = {
        id,
        model_name: id,
        description: m.description || `context: ${m.context_length || "n/a"}`,
        max_input_tokens: m.context_length || 128e3,
        multimodal: !!m.modality && m.modality.includes("vision"),
        raw: m
      };
      return acc;
    }, {});
  }
  /* ------------------------------------------------------------------ *
   *  Files helpers
   * ------------------------------------------------------------------ */
  get files_endpoint() {
    try {
      const origin = new URL(this.endpoint).origin;
      return `${origin}/v1/files`;
    } catch {
      return "https://api.x.ai/v1/files";
    }
  }
  get_file_delete_endpoint(file_id) {
    const safe_id = encodeURIComponent(String(file_id || "").trim());
    return `${this.files_endpoint}/${safe_id}`;
  }
  /* ------------------------------------------------------------------ *
   *  Complete / Stream overrides
   * ------------------------------------------------------------------ */
  async complete(req) {
    let uploaded_file_ids = [];
    try {
      const prepared = await this.prepare_req_with_uploaded_files(req);
      uploaded_file_ids = prepared.uploaded_file_ids;
      const resp = await super.complete(prepared.req);
      return resp;
    } catch (error) {
      return { error: normalize_error(error?.data || error) };
    } finally {
      await this.delete_uploaded_files(uploaded_file_ids);
    }
  }
  async stream(req, handlers = {}) {
    let uploaded_file_ids = [];
    try {
      const prepared = await this.prepare_req_with_uploaded_files(req);
      uploaded_file_ids = prepared.uploaded_file_ids;
      this.active_uploaded_file_ids = uploaded_file_ids.slice();
      return await super.stream(prepared.req, handlers);
    } catch (error) {
      const normalized = normalize_error(error?.data || error);
      if (typeof handlers?.error === "function") handlers.error(normalized);
      throw normalized;
    } finally {
      const to_delete = Array.isArray(this.active_uploaded_file_ids) ? this.active_uploaded_file_ids.slice() : uploaded_file_ids;
      this.active_uploaded_file_ids = [];
      await this.delete_uploaded_files(to_delete);
    }
  }
  stop_stream() {
    super.stop_stream();
    const to_delete = Array.isArray(this.active_uploaded_file_ids) ? this.active_uploaded_file_ids.slice() : [];
    this.active_uploaded_file_ids = [];
    this.delete_uploaded_files(to_delete).catch((e) => {
      console.warn("xAI file cleanup failed during stop_stream():", e);
    });
  }
  /**
   * Upload inline files and replace them with file references.
   * Returns:
   *  - req: transformed request with inline file bytes removed
   *  - uploaded_file_ids: list of newly uploaded file ids to cleanup after request
   */
  async prepare_req_with_uploaded_files(req = {}) {
    if (!req || !Array.isArray(req.messages) || req.messages.length === 0) {
      return { req, uploaded_file_ids: [] };
    }
    const needs_upload = req.messages.some((m) => this.message_has_inline_file(m));
    if (!needs_upload) {
      return { req, uploaded_file_ids: [] };
    }
    const uploaded_file_ids = [];
    const messages = await Promise.all(
      req.messages.map(async (message) => {
        return await this.transform_message_for_xai(message, uploaded_file_ids);
      })
    );
    return {
      req: {
        ...req,
        messages
      },
      uploaded_file_ids
    };
  }
  message_has_inline_file(message = {}) {
    if (!Array.isArray(message.content)) return false;
    return message.content.some((part) => {
      if (!part || typeof part !== "object") return false;
      if (part.type === "file") {
        const file = part.file || {};
        return typeof file.file_data === "string" || typeof file.data === "string";
      }
      if (part.type === "input_file") {
        return typeof part.file_data === "string";
      }
      return false;
    });
  }
  async transform_message_for_xai(message = {}, uploaded_file_ids = []) {
    if (!Array.isArray(message.content)) return message;
    const content = await Promise.all(
      message.content.map(async (part) => {
        return await this.transform_content_part_for_xai(part, uploaded_file_ids);
      })
    );
    return {
      ...message,
      content
    };
  }
  async transform_content_part_for_xai(part, uploaded_file_ids = []) {
    if (!part || typeof part !== "object") return part;
    if (part.type === "image_url") {
      return this.normalize_image_part(part);
    }
    if (part.type === "file") {
      return await this.normalize_or_upload_file_part(part, uploaded_file_ids);
    }
    if (part.type === "input_file") {
      return await this.normalize_or_upload_input_file_part(part, uploaded_file_ids);
    }
    return part;
  }
  normalize_image_part(part = {}) {
    const image_url = part.image_url;
    if (typeof image_url === "string") {
      return {
        ...part,
        image_url: { url: image_url }
      };
    }
    return part;
  }
  async normalize_or_upload_file_part(part = {}, uploaded_file_ids = []) {
    const file = part.file || {};
    const existing_file_id = file.file_id || file.id || part.file_id || part.id;
    if (typeof existing_file_id === "string" && existing_file_id.length > 0) {
      return {
        type: "file",
        file: { file_id: existing_file_id }
      };
    }
    const file_data = file.file_data || file.data;
    if (typeof file_data !== "string" || file_data.length === 0) return part;
    const filename = file.filename || file.name || part.filename || "document";
    const { mime_type, base64_data } = this.extract_mime_and_base64(
      file_data,
      file.mime_type || file.content_type
    );
    const content_type = mime_type || this.infer_mime_type_from_filename(filename) || "application/octet-stream";
    const uploaded = await this.upload_file_to_xai({
      name: filename,
      content_type,
      base64_data
    });
    const file_id = uploaded?.file_id || uploaded?.id;
    if (!file_id) {
      throw new Error(`xAI file upload succeeded but no file id was returned: ${JSON.stringify(uploaded || {})}`);
    }
    uploaded_file_ids.push(file_id);
    return {
      type: "file",
      file: { file_id }
    };
  }
  async normalize_or_upload_input_file_part(part = {}, uploaded_file_ids = []) {
    const existing_file_id = part.file_id || part.id;
    if (typeof existing_file_id === "string" && existing_file_id.length > 0) {
      return {
        type: "file",
        file: { file_id: existing_file_id }
      };
    }
    const file_data = part.file_data;
    if (typeof file_data !== "string" || file_data.length === 0) return part;
    const filename = part.filename || "document";
    const { mime_type, base64_data } = this.extract_mime_and_base64(file_data, part.mime_type);
    const content_type = mime_type || this.infer_mime_type_from_filename(filename) || "application/octet-stream";
    const uploaded = await this.upload_file_to_xai({
      name: filename,
      content_type,
      base64_data
    });
    const file_id = uploaded?.file_id || uploaded?.id;
    if (!file_id) {
      throw new Error(`xAI file upload succeeded but no file id was returned: ${JSON.stringify(uploaded || {})}`);
    }
    uploaded_file_ids.push(file_id);
    return {
      type: "file",
      file: { file_id }
    };
  }
  extract_mime_and_base64(file_data, fallback_mime_type = null) {
    const trimmed = String(file_data || "").trim();
    const match = trimmed.match(/^data:([^;]+);base64,(.*)$/);
    if (match) {
      return {
        mime_type: match[1] || fallback_mime_type,
        base64_data: match[2] || ""
      };
    }
    return {
      mime_type: fallback_mime_type,
      base64_data: trimmed
    };
  }
  infer_mime_type_from_filename(filename = "") {
    const lower = String(filename).toLowerCase();
    if (lower.endsWith(".pdf")) return "application/pdf";
    if (lower.endsWith(".txt")) return "text/plain";
    if (lower.endsWith(".md")) return "text/markdown";
    if (lower.endsWith(".json")) return "application/json";
    if (lower.endsWith(".csv")) return "text/csv";
    if (lower.endsWith(".png")) return "image/png";
    if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
    return null;
  }
  base64_to_uint8_array(base64_data = "") {
    const clean = String(base64_data || "").trim().replace(/^data:[^;]+;base64,/, "");
    if (typeof Buffer !== "undefined") {
      return new Uint8Array(Buffer.from(clean, "base64"));
    }
    if (typeof atob === "function") {
      const binary = atob(clean);
      const bytes = new Uint8Array(binary.length);
      for (let i = 0; i < binary.length; i++) {
        bytes[i] = binary.charCodeAt(i);
      }
      return bytes;
    }
    throw new Error("xAI file upload failed: no base64 decoder available (Buffer/atob)");
  }
  base64_to_blob(base64_data = "", content_type = "application/octet-stream") {
    if (typeof Blob !== "function") {
      throw new Error("xAI file upload failed: Blob is not available in this runtime");
    }
    const bytes = this.base64_to_uint8_array(base64_data);
    return new Blob([bytes], { type: content_type });
  }
  safe_parse_json(text = "") {
    try {
      return JSON.parse(String(text || ""));
    } catch {
      return null;
    }
  }
  /**
   * Upload a file to xAI's Files API.
   *
   * MUST be multipart/form-data with:
   *  - form field: "file"
   *  - form field: "purpose" (commonly "assistants")
   *
   * IMPORTANT: Do NOT set Content-Type manually (boundary must be auto-generated).
   */
  async upload_file_to_xai({ name, content_type, base64_data }) {
    if (!this.api_key) {
      throw new Error("xAI file upload failed: missing API key");
    }
    if (typeof FormData !== "function") {
      throw new Error("xAI file upload failed: FormData is not available in this runtime");
    }
    const file_blob = this.base64_to_blob(base64_data, content_type);
    const form_data = new FormData();
    form_data.append("file", file_blob, name);
    form_data.append("purpose", "assistants");
    const headers = {
      // DO NOT set Content-Type here
      "Authorization": `Bearer ${this.api_key}`
    };
    if (typeof fetch === "function") {
      const resp = await fetch(this.files_endpoint, {
        method: "POST",
        headers,
        body: form_data
      });
      const body_text2 = await resp.text();
      const json2 = this.safe_parse_json(body_text2);
      if (!resp.ok) {
        const err = new Error(`xAI file upload failed: HTTP ${resp.status} ${resp.statusText || ""}`.trim());
        err.details = {
          status: resp.status,
          status_text: resp.statusText,
          raw_text: body_text2?.slice?.(0, 2e3) || body_text2
        };
        if (json2) err.details.json = json2;
        throw err;
      }
      if (!json2) {
        throw new Error(`xAI file upload failed: expected JSON response, got: ${String(body_text2 || "").slice(0, 200)}`);
      }
      if (json2.error) {
        throw normalize_error(json2.error);
      }
      return json2;
    }
    const http_resp = await this.http_adapter.request({
      url: this.files_endpoint,
      method: "POST",
      headers,
      body: form_data
    });
    const status = typeof http_resp?.status === "number" ? http_resp.status : typeof http_resp?.status === "function" ? await http_resp.status() : null;
    const body_text = typeof http_resp?.text === "function" ? await http_resp.text() : typeof http_resp?.response?.text === "function" ? await http_resp.response.text() : "";
    const json = this.safe_parse_json(body_text);
    if (!status || status < 200 || status >= 300) {
      const err = new Error(`xAI file upload failed: HTTP ${status || "unknown"}`);
      err.details = {
        status: status || null,
        raw_text: body_text?.slice?.(0, 2e3) || body_text
      };
      if (json) err.details.json = json;
      throw err;
    }
    if (!json) {
      throw new Error(`xAI file upload failed: expected JSON response, got: ${String(body_text || "").slice(0, 200)}`);
    }
    if (json.error) {
      throw normalize_error(json.error);
    }
    return json;
  }
  /**
   * Delete a file from xAI Files API (best-effort cleanup).
   * Endpoint: DELETE /v1/files/{file_id} :contentReference[oaicite:5]{index=5}
   */
  async delete_file_from_xai(file_id) {
    const id = String(file_id || "").trim();
    if (!id) return null;
    if (!this.api_key) {
      throw new Error("xAI file delete failed: missing API key");
    }
    const url = this.get_file_delete_endpoint(id);
    const headers = {
      "Authorization": `Bearer ${this.api_key}`
    };
    if (typeof fetch === "function") {
      const resp = await fetch(url, { method: "DELETE", headers });
      const body_text2 = await resp.text();
      const json2 = this.safe_parse_json(body_text2);
      if (!resp.ok) {
        const err = new Error(`xAI file delete failed: HTTP ${resp.status} ${resp.statusText || ""}`.trim());
        err.details = {
          status: resp.status,
          status_text: resp.statusText,
          raw_text: body_text2?.slice?.(0, 2e3) || body_text2
        };
        if (json2) err.details.json = json2;
        throw err;
      }
      return json2 || { deleted: true, id };
    }
    const http_resp = await this.http_adapter.request({
      url,
      method: "DELETE",
      headers
    });
    const status = typeof http_resp?.status === "number" ? http_resp.status : typeof http_resp?.status === "function" ? await http_resp.status() : null;
    const body_text = typeof http_resp?.text === "function" ? await http_resp.text() : typeof http_resp?.response?.text === "function" ? await http_resp.response.text() : "";
    const json = this.safe_parse_json(body_text);
    if (!status || status < 200 || status >= 300) {
      const err = new Error(`xAI file delete failed: HTTP ${status || "unknown"}`);
      err.details = {
        status: status || null,
        raw_text: body_text?.slice?.(0, 2e3) || body_text
      };
      if (json) err.details.json = json;
      throw err;
    }
    return json || { deleted: true, id };
  }
  /**
   * Delete all uploaded files (deduped), best-effort.
   */
  async delete_uploaded_files(file_ids = []) {
    const unique_ids = Array.from(new Set((file_ids || []).filter(Boolean).map((v) => String(v).trim()))).filter(Boolean);
    if (unique_ids.length === 0) return;
    const results = await Promise.allSettled(
      unique_ids.map(async (id) => await this.delete_file_from_xai(id))
    );
    const rejected = results.filter((r) => r.status === "rejected");
    if (rejected.length > 0) {
      console.warn("xAI cleanup: some file deletions failed:", rejected.map((r) => r.reason));
    }
  }
};
var XaiCompletionRequestAdapter = class extends SmartChatModelRequestAdapter {
  /**
   * Convert request to xAI format
   * @param {boolean} streaming
   * @returns {Object} Request params
   */
  to_platform(streaming = false) {
    return this.to_xai(streaming);
  }
  /**
   * xAI is largely OpenAI-compatible for /v1/chat/completions,
   * but file parts must be shaped as "input_file".
   * @param {boolean} streaming
   * @returns {Object} Request params
   */
  to_xai(streaming = false) {
    const req = super.to_openai(streaming);
    let body = {};
    try {
      body = JSON.parse(req.body || "{}");
    } catch {
      body = {};
    }
    body.messages = (body.messages || []).map((message) => {
      return this._transform_message_for_xai(message);
    });
    req.body = JSON.stringify(body);
    return req;
  }
  _transform_message_for_xai(message = {}) {
    if (!message || typeof message !== "object") return message;
    if (!Array.isArray(message.content)) return message;
    const content = message.content.map((part) => this._transform_content_part_for_xai(part));
    return {
      ...message,
      content
    };
  }
  _transform_content_part_for_xai(part) {
    if (!part || typeof part !== "object") return part;
    if (part.type === "image_url") {
      const image_url = part.image_url;
      if (typeof image_url === "string") {
        return { ...part, image_url: { url: image_url } };
      }
      return part;
    }
    if (part.type === "file") {
      const file = part.file || {};
      const file_id = file.file_id || file.id || part.file_id || part.id;
      if (typeof file_id === "string" && file_id.length > 0) {
        return {
          type: "input_file",
          file_id
        };
      }
      return part;
    }
    if (part.type === "input_file") {
      if (typeof part.file_id === "string" && part.file_id.length > 0) return part;
      const file_id = part.file?.file_id || part.file?.id || part.id;
      if (typeof file_id === "string" && file_id.length > 0) {
        return {
          type: "input_file",
          file_id
        };
      }
      return part;
    }
    return part;
  }
};
var XaiCompletionResponseAdapter = class extends SmartChatModelResponseAdapter {
  static get platform_res() {
    return {
      id: "",
      object: "chat.completion",
      created: 0,
      model: "",
      choices: [],
      usage: {}
    };
  }
  /**
   * Convert response to OpenAI-like format.
   * @returns {Object}
   */
  to_openai() {
    if (this.error) return { error: normalize_error(this.error, this.status) };
    const object = this._normalize_object_type(this._res.object);
    return {
      id: this._res.id || `xai_${Date.now()}`,
      object: object || "chat.completion",
      created: this._res.created || Math.floor(Date.now() / 1e3),
      model: this._res.model || this.adapter?.model_key || "",
      choices: this._transform_choices_to_openai(),
      usage: this._transform_usage_to_openai(),
      raw: this._res
    };
  }
  _normalize_object_type(object_value) {
    if (typeof object_value !== "string") return object_value;
    if (object_value.endsWith(".chunk")) return object_value.replace(".chunk", "");
    return object_value;
  }
  _transform_usage_to_openai() {
    if (!this._res.usage) {
      return {
        prompt_tokens: 0,
        completion_tokens: 0,
        total_tokens: 0
      };
    }
    return this._res.usage;
  }
  /**
   * Streaming chunk handler.
   * xAI streams data lines in OpenAI-like SSE form:
   *   "data: {...json...}"
   * and ends with:
   *   "data: [DONE]"
   */
  handle_chunk(chunk) {
    if (chunk === "data: [DONE]") return;
    const json_text = String(chunk || "").startsWith("data: ") ? String(chunk).slice(6) : String(chunk);
    let parsed;
    try {
      parsed = JSON.parse(json_text);
    } catch {
      return;
    }
    if (!parsed || typeof parsed !== "object") return;
    if (!this._res.id && parsed.id) this._res.id = parsed.id;
    if (parsed.model) this._res.model = parsed.model;
    if (parsed.created) this._res.created = parsed.created;
    if (parsed.object) this._res.object = parsed.object;
    if (parsed.usage) this._res.usage = parsed.usage;
    if (!Array.isArray(this._res.choices)) this._res.choices = [];
    if (!this._res.choices[0]) {
      this._res.choices[0] = {
        index: 0,
        message: {
          role: "assistant",
          content: ""
        }
      };
    }
    const delta = parsed.choices?.[0]?.delta;
    if (!delta || typeof delta !== "object") return;
    if (delta.role) {
      this._res.choices[0].message.role = delta.role;
    }
    let raw;
    if (typeof delta.content === "string") {
      raw = delta.content;
      this._res.choices[0].message.content += delta.content;
    }
    if (Array.isArray(delta.tool_calls)) {
      if (!Array.isArray(this._res.choices[0].message.tool_calls)) {
        this._res.choices[0].message.tool_calls = [{
          id: "",
          type: "function",
          function: { name: "", arguments: "" }
        }];
      }
      const incoming = delta.tool_calls[0] || {};
      const target = this._res.choices[0].message.tool_calls[0];
      if (incoming.id) target.id += incoming.id;
      if (incoming.function?.name) target.function.name += incoming.function.name;
      if (incoming.function?.arguments) target.function.arguments += incoming.function.arguments;
    }
    const finish_reason = parsed.choices?.[0]?.finish_reason;
    if (finish_reason) {
      this._res.choices[0].finish_reason = finish_reason;
    }
    return raw;
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-chat-model/adapters/deepseek.js
var SmartChatModelDeepseekAdapter = class extends SmartChatModelApiAdapter {
  static key = "deepseek";
  static defaults = {
    description: "DeepSeek",
    type: "API",
    endpoint: "https://api.deepseek.com/chat/completions",
    streaming: true,
    adapter: "DeepSeek",
    models_endpoint: "https://api.deepseek.com/models",
    default_model: "deepseek-base",
    signup_url: "https://deepseek.com/signup"
  };
  /**
   * Get the request adapter class
   * @returns {typeof SmartChatModelDeepseekRequestAdapter} Request adapter class
   */
  get req_adapter() {
    return SmartChatModelDeepseekRequestAdapter;
  }
  /**
   * Get the response adapter class
   * @returns {typeof SmartChatModelDeepseekResponseAdapter} Response adapter class
   */
  get res_adapter() {
    return SmartChatModelDeepseekResponseAdapter;
  }
  get models_endpoint_method() {
    return "GET";
  }
  /**
   * Parse the raw model data from DeepSeek's /v1/models endpoint
   * into a structured map of model objects keyed by model ID.
   * @param {Object} model_data - Raw JSON from DeepSeek
   * @returns {Object} Map of model objects
   */
  parse_model_data(model_data) {
    if (!model_data?.data || !Array.isArray(model_data.data)) {
      return { "_": { id: "No models found." } };
    }
    const parsed = {};
    for (const m of model_data.data) {
      parsed[m.id] = {
        model_name: m.id,
        id: m.id,
        max_input_tokens: m.context_size || 8192,
        description: m.description || m.name || m.id,
        raw: m
      };
    }
    return parsed;
  }
  /**
   * Estimate tokens in user input.
   * @param {string|Object} input - Input text or structured message
   * @returns {Promise<number>} Token count estimate
   */
  async count_tokens(input) {
    const text = typeof input === "string" ? input : JSON.stringify(input);
    return Math.ceil(text.length / 4);
  }
  /**
   * Check if an incoming streaming chunk signals end of stream.
   * @param {CustomEvent} event - SSE event with data
   * @returns {boolean} True if end of stream
   */
  is_end_of_stream(event) {
    if (!event?.data) return false;
    return event.data.includes('"done":true') || event.data.includes("[DONE]");
  }
};
var SmartChatModelDeepseekRequestAdapter = class extends SmartChatModelRequestAdapter {
  /**
   * Convert incoming request to DeepSeek's expected format
   * Often just reuse the base "to_openai()" if that matches DeepSeek's design
   * @param {boolean} streaming - True if streaming
   * @returns {Object} Request parameters
   */
  to_platform(streaming = false) {
    return this.to_openai(streaming);
  }
};
var SmartChatModelDeepseekResponseAdapter = class extends SmartChatModelResponseAdapter {
};

// node_modules/obsidian-smart-env/default.config.js
var import_obsidian41 = require("obsidian");

// node_modules/obsidian-smart-env/node_modules/smart-sources/utils/get_line_range.js
function get_line_range2(content, start_line, end_line) {
  const lines = content.split("\n");
  return lines.slice(start_line - 1, end_line).join("\n");
}

// node_modules/obsidian-smart-env/node_modules/smart-blocks/content_parsers/parse_blocks.js
function parse_blocks(source, content) {
  let { blocks: blocks_obj, task_lines, tasks, codeblock_ranges } = parse_markdown_blocks(content);
  const last_read_at = source.data.last_read?.at || Date.now();
  for (const [sub_key, line_range] of Object.entries(blocks_obj)) {
    const block_key = source.key + sub_key;
    const existing_block = source.block_collection.get(block_key);
    const block_content = get_line_range2(content, line_range[0], line_range[1]);
    const next_hash = murmur_hash_32_alphanumeric(block_content);
    if (existing_block && existing_block.lines?.[0] === line_range[0] && existing_block.lines?.[1] === line_range[1] && existing_block.size === block_content.length && existing_block.data?.last_read?.hash === next_hash) {
      continue;
    }
    const block_outlinks = get_markdown_links(block_content);
    const bases_links = get_bases_cache_links({
      source,
      links: block_outlinks
    });
    const block_data = {
      key: block_key,
      lines: line_range,
      size: block_content.length,
      outlinks: [
        ...block_outlinks,
        ...bases_links
      ],
      last_read: {
        at: last_read_at,
        hash: next_hash
      }
    };
    const block_changed = has_block_data_changes(existing_block, block_data);
    if (!block_changed && existing_block?.vec) {
      continue;
    }
    if (!existing_block || existing_block?.data.last_read?.hash !== block_data.last_read.hash) {
      const new_item = new source.block_collection.item_type(source.env, block_data);
      if (block_changed) new_item.queue_save();
      source.block_collection.set(new_item);
    } else if (block_changed) {
      existing_block.data = {
        ...existing_block.data,
        ...block_data
        // overwrites lines, last_read
      };
      existing_block.queue_save();
    }
  }
  clean_and_update_source_blocks(source, blocks_obj, task_lines, tasks, codeblock_ranges);
  for (const block of source.blocks) {
    if (!block.vec || block.embed_hash !== block.read_hash) {
      block.queue_embed();
    }
  }
}
function has_block_data_changes(existing_block, block_data) {
  if (!existing_block) return true;
  const existing_lines = existing_block.lines || [];
  if (existing_lines[0] !== block_data.lines[0] || existing_lines[1] !== block_data.lines[1]) {
    return true;
  }
  if (existing_block.size !== block_data.size) {
    return true;
  }
  if (existing_block?.data?.last_read?.hash !== block_data.last_read.hash) {
    return true;
  }
  const existing_outlinks = existing_block?.data?.outlinks || [];
  if (JSON.stringify(existing_outlinks) !== JSON.stringify(block_data.outlinks)) {
    return true;
  }
  return false;
}
function clean_and_update_source_blocks(source, blocks_obj, task_lines = [], tasks = {}, codeblock_ranges = {}) {
  const current_block_keys = new Set(Object.keys(blocks_obj).map((sk) => source.key + sk));
  const blocks = source.blocks;
  for (let i = 0; i < blocks.length; i++) {
    if (!current_block_keys.has(blocks[i].key)) {
      blocks[i].deleted = true;
      blocks[i].queue_save();
    }
  }
  source.data.blocks = blocks_obj;
  source.data.task_lines = task_lines;
  source.data.tasks = tasks;
  source.data.codeblock_ranges = codeblock_ranges;
  source.queue_save();
}

// node_modules/obsidian-smart-env/node_modules/smart-collections/utils/ajson_merge.js
function ajson_merge(existing, new_obj) {
  if (new_obj === null) return null;
  if (new_obj === void 0) return existing;
  if (typeof new_obj !== "object") return new_obj;
  if (typeof existing !== "object" || existing === null) existing = {};
  const keys = Object.keys(new_obj);
  const length = keys.length;
  for (let i = 0; i < length; i++) {
    const key = keys[i];
    const new_val = new_obj[key];
    const existing_val = existing[key];
    if (Array.isArray(new_val)) {
      existing[key] = new_val.slice();
    } else if (is_object(new_val)) {
      existing[key] = ajson_merge(is_object(existing_val) ? existing_val : {}, new_val);
    } else if (new_val !== void 0) {
      existing[key] = new_val;
    }
  }
  return existing;
}
function is_object(obj) {
  return obj !== null && typeof obj === "object" && !Array.isArray(obj);
}

// node_modules/obsidian-smart-env/node_modules/smart-collections/adapters/ajson_single_file.js
var class_to_collection_key2 = {
  "SmartSource": "smart_sources",
  "SmartNote": "smart_sources",
  // DEPRECATED
  "SmartBlock": "smart_blocks",
  "SmartDirectory": "smart_directories"
};
function _parse_ajson_key(ajson_key) {
  let changed = false;
  let [collection_key, ...item_key] = ajson_key.split(":");
  if (class_to_collection_key2[collection_key]) {
    collection_key = class_to_collection_key2[collection_key];
    changed = true;
  }
  return {
    collection_key,
    item_key: item_key.join(":"),
    changed
  };
}
var AjsonSingleFileCollectionDataAdapter = class extends AjsonMultiFileCollectionDataAdapter {
  /**
   * Returns the single shared `.ajson` file path for this collection.
   * @param {string} [key] - (unused) Item key, ignored in single-file mode.
   * @returns {string} The single .ajson file path for the entire collection.
   */
  get_item_data_path(key) {
    const file_name = (this.collection?.collection_key || "collection") + ".ajson";
    const sep = this.fs?.sep || "/";
    const dir = this.collection.data_dir || "data";
    return [dir, file_name].join(sep);
  }
  /**
   * Override process_load_queue to parse the entire single-file .ajson once,
   * distributing final states to items.
   *
   * @async
   * @returns {Promise<void>}
   */
  async process_load_queue() {
    this.collection.emit_event("collection:load_started");
    if (!await this.fs.exists(this.collection.data_dir)) {
      await this.fs.mkdir(this.collection.data_dir);
    }
    const path = this.get_item_data_path();
    if (!await this.fs.exists(path)) {
      for (const item of Object.values(this.collection.items)) {
        if (item._queue_load) {
          item.queue_import?.();
        }
      }
      this.collection.emit_event("collection:load_halted");
      return;
    }
    const raw_data = await this.fs.read(path, "utf-8", { no_cache: true });
    if (!raw_data) {
      for (const item of Object.values(this.collection.items)) {
        if (item._queue_load) {
          item.queue_import?.();
        }
      }
      this.collection.emit_event("collection:load_halted");
      return;
    }
    const { rewrite, file_data } = this.parse_single_file_ajson(raw_data);
    if (rewrite) {
      if (file_data.length) {
        await this.fs.write(path, file_data);
      } else {
        await this.fs.remove(path);
      }
    }
    for (const item of Object.values(this.collection.items)) {
      item._queue_load = false;
      item.loaded_at = Date.now();
    }
    this.collection.emit_event("collection:load_completed");
  }
  /**
   * Helper to parse single-file .ajson content, distributing states to items.
   *
   * @param {string} raw
   * @returns {{ rewrite: boolean, file_data: string }}
   */
  parse_single_file_ajson(raw) {
    let rewrite = false;
    const lines = raw.trim().split("\n").filter(Boolean);
    let data_map = {};
    let line_count = 0;
    for (let i = 0; i < lines.length; i++) {
      const line = lines[i].trim();
      if (!line.endsWith(",")) {
        rewrite = true;
      }
      const trimmed = line.replace(/,$/, "");
      const combined = "{" + trimmed + "}";
      try {
        const obj = JSON.parse(combined);
        const [fullKey, value] = Object.entries(obj)[0];
        let { collection_key, item_key, changed } = _parse_ajson_key(fullKey);
        const newKey = `${collection_key}:${item_key}`;
        if (!value) {
          delete data_map[newKey];
          if (changed || newKey !== fullKey) {
            delete data_map[fullKey];
          }
          rewrite = true;
        } else {
          data_map[newKey] = value;
          if (changed || newKey !== fullKey) {
            delete data_map[fullKey];
            rewrite = true;
          }
        }
      } catch (err) {
        console.warn("parse error for line: ", line, err);
        rewrite = true;
      }
      line_count++;
    }
    for (const [ajson_key, val] of Object.entries(data_map)) {
      const [collection_key, ...rest] = ajson_key.split(":");
      const item_key = rest.join(":");
      const collection = this.collection.env[collection_key];
      if (!collection) continue;
      let item = collection.get(item_key);
      if (!item) {
        const ItemClass = collection.item_type;
        item = new ItemClass(this.env, val);
        collection.set(item);
      } else {
        item.data = ajson_merge(item.data, val);
      }
      item.loaded_at = Date.now();
      item._queue_load = false;
      if (!val.key) val.key = item_key;
    }
    if (line_count > Object.keys(data_map).length) {
      rewrite = true;
    }
    let minimal_lines = [];
    for (const [ajson_key, val] of Object.entries(data_map)) {
      minimal_lines.push(`${JSON.stringify(ajson_key)}: ${JSON.stringify(val)},`);
    }
    return {
      rewrite,
      file_data: minimal_lines.join("\n")
    };
  }
  /**
   * Override process_save_queue for single-file approach.
   * We'll simply call save_item for each queued item, which appends a line to the same `.ajson`.
   *
   * @async
   * @returns {Promise<void>}
   */
  async process_save_queue() {
    this.collection.emit_event("collection:save_started");
    const save_queue = Object.values(this.collection.items).filter((item) => item._queue_save);
    const time_start = Date.now();
    const batch_size = 50;
    for (let i = 0; i < save_queue.length; i += batch_size) {
      const batch = save_queue.slice(i, i + batch_size);
      await Promise.all(batch.map((item) => {
        const adapter = this.create_item_adapter(item);
        return adapter.save().catch((err) => {
          console.warn(`Error saving item ${item.key}`, err);
          item.queue_save();
        });
      }));
    }
    const deleted_items = Object.values(this.collection.items).filter((item) => item.deleted);
    if (deleted_items.length) {
      deleted_items.forEach((item) => {
        delete this.collection.items[item.key];
      });
    }
    console.log(`Saved (single-file) ${this.collection.collection_key} in ${Date.now() - time_start}ms`);
    this.collection.emit_event("collection:save_completed");
  }
};
var AjsonSingleFileItemDataAdapter = class extends AjsonMultiFileItemDataAdapter {
  /**
   * Overridden to always return the single file path from the parent collection adapter.
   * @returns {string}
   */
  get data_path() {
    return this.collection_adapter.get_item_data_path(this.item.key);
  }
  /**
   * Load logic:
   * In single-file mode, we typically rely on the collection's `process_load_queue()`
   * to parse the entire file. This direct `load()` will do a naive re-parse as well
   * if used individually.
   */
  async load() {
    const path = this.data_path;
    if (!await this.fs.exists(path)) {
      this.item.queue_import?.();
      return;
    }
    try {
      const raw_data = await this.fs.read(path, "utf-8", { no_cache: true });
      if (!raw_data) {
        this.item.queue_import?.();
        return;
      }
      const { rewrite } = this.collection_adapter.parse_single_file_ajson(raw_data);
    } catch (err) {
      console.warn(`Error loading single-file item ${this.item.key}`, err);
      this.item.queue_import?.();
    }
  }
};
var ajson_single_file_default = {
  collection: AjsonSingleFileCollectionDataAdapter,
  item: AjsonSingleFileItemDataAdapter
};

// node_modules/obsidian-smart-env/node_modules/smart-components/smart_component.js
var SmartComponent = class extends CollectionItem {
  static key = "smart_component";
  static collection_key = "smart_components";
  collection_key = "smart_components";
  get_key() {
    if (this.data?.key) return this.data.key;
    const scope_key = this.scope_key;
    const component_key = this.component_key;
    const version4 = Number.isFinite(this.data?.version) ? this.data.version : 0;
    const hash = this.data?.hash || "nohash";
    const key_pcs = [];
    if (!component_key.includes(scope_key) && scope_key !== "global") key_pcs.push(scope_key);
    key_pcs.push(component_key);
    return `${key_pcs.join("_").replace(/\./g, "_")}#${[version4, hash].join("#")}`;
  }
  get scope_key() {
    return this.data?.scope_key;
  }
  get component_key() {
    return this.data?.component_key;
  }
  get component_adapter() {
    return this._component_adapter;
  }
  /**
   * Delegates render logic to the adapter.
   * @param {object} component_scope
   * @param {object} [opts={}]
   * @returns {Promise<*>}
   */
  async render(component_scope, opts = {}) {
    if (!this.component_adapter) {
      throw new Error(`SmartComponent: adapter missing for ${this.component_key}`);
    }
    return await this.component_adapter.render(component_scope, opts);
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-components/adapters/_adapter.js
function parse_component_properties(component_properties = []) {
  const parts = component_properties.filter(Boolean).map((part) => part.toString());
  const component_key = parts.pop();
  const scope_key = parts.length ? parts.join(".") : "global";
  return { scope_key, component_key };
}
async function build_component_data(component_properties, component_module) {
  const { scope_key, component_key } = parse_component_properties(component_properties);
  if (!component_key) return null;
  const render_fn = typeof component_module === "function" ? component_module : component_module?.render;
  const version4 = typeof render_fn?.version === "number" ? render_fn.version : 0;
  const hash = await murmur_hash_32_alphanumeric(render_fn.toString());
  return { scope_key, component_key, version: version4, hash };
}
var SmartComponentAdapter = class {
  constructor(item, component_module) {
    this.item = item;
    this.module = component_module;
    this.item.env.create_env_getter(this);
  }
  static should_use_adapter(component_module) {
    return true;
  }
  static async register_component(collection, component_properties, component_module) {
    if (!this.should_use_adapter(component_module)) return null;
    const data = await build_component_data(component_properties, component_module);
    if (!data) return null;
    const item = await collection.create_or_update({ ...data });
    if (!item) return null;
    item._component_module = component_module;
    item._component_adapter = new this(item, component_module);
    return item;
  }
  /**
   * Render the component for the provided scope.
   * @abstract
   * @param {Object} scope - Render scope from the hosting environment.
   * @param {Object} [opts] - Optional render options.
   * @returns {Promise<*>} Rendered output for the component.
   */
  async render(scope, opts) {
    throw new Error("render() not implemented");
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-components/adapters/smart_view_component_adapter.js
var SmartViewComponentAdapter = class extends SmartComponentAdapter {
  static should_use_adapter(component_module) {
    return typeof component_module === "function" || typeof component_module?.render === "function";
  }
  get smart_view() {
    if (!this._smart_view) {
      this._smart_view = this.env.init_module("smart_view");
    }
    return this._smart_view;
  }
  async render(scope, opts = {}) {
    const render_fn = typeof this.module === "function" ? this.module : this.module?.render;
    if (typeof render_fn !== "function") {
      throw new Error("SmartViewComponentAdapter: render() missing on module");
    }
    return await render_fn.call(this.smart_view, scope, opts);
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-components/smart_components.js
function flatten_components_config(config, path = [], acc = []) {
  if (!config || typeof config !== "object") return acc;
  Object.entries(config).forEach(([key, value]) => {
    const next_path = [...path, key];
    if (!value) return;
    if (typeof value === "function" || typeof value?.render === "function") {
      acc.push({ properties: next_path, module: value });
      return;
    }
    if (typeof value === "object") {
      flatten_components_config(value, next_path, acc);
    }
  });
  return acc;
}
var SmartComponents = class extends Collection {
  static key = "smart_components";
  static collection_key = "smart_components";
  collection_key = "smart_components";
  async init() {
    await this.load_components_from_config();
  }
  get component_adapters() {
    if (Array.isArray(this.opts?.component_adapters)) {
      return this.opts.component_adapters;
    }
    if (this.opts?.component_adapters && typeof this.opts.component_adapters === "object") {
      return Object.values(this.opts.component_adapters);
    }
    return this.constructor.default_component_adapters || [];
  }
  /**
   * @private
   * @returns {Promise<void>}
   */
  async load_components_from_config() {
    const records = flatten_components_config(this.env.config?.components || {});
    for (const record of records) {
      await this.register_component(record.properties, record.module);
    }
  }
  /**
   * @private
   * @param {string[]} component_properties
   * @param {Object|Function} component_module
   * @returns {Promise<import('./smart_component.js').SmartComponent|null>}
   */
  async register_component(component_properties, component_module) {
    for (const AdapterClass of this.component_adapters) {
      const item = await AdapterClass.register_component(this, component_properties, component_module);
      if (item) return item;
    }
    return null;
  }
  async render_component(component_key, scope, opts = {}) {
    const components = this.filter((item) => {
      if (item.key.startsWith(component_key + "#")) return true;
      return item.component_key === component_key;
    }).sort((a, b) => {
      const a_scope_match = a.scope_key === scope.key ? 1 : 0;
      const b_scope_match = b.scope_key === scope.key ? 1 : 0;
      return b_scope_match - a_scope_match;
    });
    if (components.length === 0) {
      throw new Error(`SmartComponents: no component found for key ${component_key}`);
    }
    const selected_component = components[0];
    return await selected_component.render(scope, opts);
  }
};
var smart_components_default = {
  class: SmartComponents,
  item_type: SmartComponent,
  data_adapter: ajson_single_file_default,
  component_adapters: {
    SmartViewComponentAdapter
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-components/index.js
var smart_components_default2 = smart_components_default;

// node_modules/obsidian-smart-env/node_modules/smart-contexts/context_item.js
var ContextItem = class extends CollectionItem {
  // special handling because current name_to_collection_key removes "Items" suffix
  get collection_key() {
    return "context_items";
  }
  get context_type_adapter() {
    if (!this._context_type_adapter) {
      const Class = this.collection.context_item_adapters.find((adapter_class) => adapter_class.detect(this.key, this.data));
      if (!Class) throw new Error(`No context item adapter found for key: ${this.key}`);
      this._context_type_adapter = new Class(this);
    }
    return this._context_type_adapter;
  }
  get exists() {
    return this.context_type_adapter.exists;
  }
  get icon_type() {
    return this.context_type_adapter.icon_type || null;
  }
  // v3
  async get_text() {
    const item_text = await this.context_type_adapter.get_text();
    if (typeof item_text !== "string") return item_text;
    if (typeof this.actions.context_item_merge_template === "function") {
      return await this.actions.context_item_merge_template(item_text);
    }
    return item_text;
  }
  async get_base64() {
    if (this.is_media) {
      return await this.context_type_adapter.get_base64();
    }
    return { error: `Context item is not media type: ${this.key}` };
  }
  async open(event = null) {
    return await this.context_type_adapter.open(event);
  }
  get is_media() {
    return this.context_type_adapter.is_media || false;
  }
  get item_ref() {
    return this.context_type_adapter.ref || null;
  }
  get size() {
    return this.data.size || this.context_type_adapter.size || 0;
  }
  get mtime() {
    return this.data.mtime || this.context_type_adapter.mtime || null;
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-contexts/adapters/context-items/_adapter.js
var ContextItemAdapter = class {
  constructor(item) {
    this.item = item;
  }
  static detect(key, data = {}) {
    return false;
  }
  get env() {
    return this.item.env;
  }
  get exists() {
    return true;
  }
  get icon_type() {
    return null;
  }
  // v3 API
  /**
   * for calculating context size
   */
  get size() {
    return 0;
  }
  async get_text() {
  }
  async open() {
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-contexts/adapters/context-items/block.js
var BlockContextItemAdapter = class extends ContextItemAdapter {
  static order = 6;
  static detect(key) {
    return key.includes("#");
  }
  get ref() {
    return this.env.smart_blocks.get(this.item.key);
  }
  get inlinks() {
    return this.ref.inlinks || [];
  }
  get outlinks() {
    return this.ref.outlinks || [];
  }
  get exists() {
    return !!(this.ref && !this.ref.is_gone);
  }
  get mtime() {
    return this.ref?.mtime || null;
  }
  get size() {
    return this.ref?.size || 0;
  }
  async get_text() {
    const block = this.ref;
    if (!block) return { error: "Block not found" };
    return await block.read();
  }
  async open(event = null) {
    this.ref.actions.source_open(event);
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-contexts/adapters/context-items/source.js
var SourceContextItemAdapter = class extends ContextItemAdapter {
  static order = 7;
  // default lowest priority
  static detect(key) {
    return true;
  }
  get ref() {
    return this.env.smart_sources.get(this.item.key);
  }
  get inlinks() {
    return this.ref.inlinks || [];
  }
  get outlinks() {
    return this.ref.outlinks || [];
  }
  get exists() {
    return !!(this.ref && !this.ref.is_gone);
  }
  get size() {
    return this.ref?.size || 0;
  }
  get mtime() {
    return this.ref?.mtime || null;
  }
  async get_text() {
    return await this.ref?.read() || "MISSING SOURCE";
  }
  async open(event = null) {
    this.ref.actions.source_open(event);
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-contexts/utils/image_extension_regex.js
var image_extension_regex = /\.(png|jpe?g|gif|bmp|webp|ico|mp4)$/i;

// node_modules/obsidian-smart-env/node_modules/smart-contexts/adapters/context-items/image.js
var ImageContextItemAdapter = class extends ContextItemAdapter {
  static detect(key) {
    if (image_extension_regex.test(key)) return "image";
    return false;
  }
  get exists() {
    return this.item.env.smart_sources.fs.exists_sync(this.item.key);
  }
  get icon_type() {
    return "image-file";
  }
  get is_media() {
    return true;
  }
  async get_base64() {
    const ext = this.item.key.split(".").pop().toLowerCase();
    try {
      const base64_data = await this.item.env.fs.read(this.item.key, "base64");
      const base64_url = `data:image/${ext};base64,${base64_data}`;
      return {
        type: "image_url",
        key: this.item.key,
        name: this.item.key.split(/[\\/]/).pop(),
        url: base64_url
      };
    } catch (err) {
      console.warn(`Failed to convert image ${this.item.key} to base64`, err);
      return { error: `Failed to convert image to base64: ${err.message}` };
    }
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-contexts/adapters/context-items/pdf.js
var PdfContextItemAdapter = class extends ContextItemAdapter {
  static detect(key) {
    if (String(key || "").toLowerCase().endsWith(".pdf")) return "pdf";
    return false;
  }
  async add_to_snapshot(snapshot) {
    if (!snapshot.pdfs) snapshot.pdfs = [];
    snapshot.pdfs.push(this.item.key);
  }
  get icon_type() {
    return "file-text";
  }
  get is_media() {
    return true;
  }
  async get_base64() {
    try {
      const base64_data = await this.item.env.fs.read(this.item.key, "base64");
      const base64_url = `data:application/pdf;base64,${base64_data}`;
      return {
        type: "pdf_url",
        key: this.item.key,
        name: this.item.key.split(/[\\/]/).pop(),
        url: base64_url
      };
    } catch (err) {
      console.warn(`Failed to convert PDF ${this.item.key} to base64`, err);
      return { error: `Failed to convert PDF to base64: ${err.message}` };
    }
  }
  get exists() {
    return this.item.env.smart_sources.fs.exists_sync(this.item.key);
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-contexts/context_items.js
var ContextItems = class extends Collection {
  constructor(smart_context, opts = {}) {
    super(smart_context.env || smart_context, opts);
    this.smart_context = smart_context;
  }
  async load() {
    console.log("ContextItems: load called");
  }
  static version = 1;
  get context_item_adapters() {
    if (!this._context_item_adapters) {
      this._context_item_adapters = Object.values(this.opts.context_item_adapters).sort((a, b) => {
        const order_a = a.order || 0;
        const order_b = b.order || 0;
        return order_a - order_b;
      });
    }
    return this._context_item_adapters;
  }
  new_item(data) {
    const item = new this.item_type(this.env, data);
    this.set(item);
    return item;
  }
  process_load_queue() {
  }
  get settings_config() {
    return {
      ...this.env.config.actions.context_item_merge_template?.settings_config || {}
    };
  }
  static get default_settings() {
    return {
      template_preset: "xml_structured",
      template_before: '<item loc="{{KEY}}" at="{{TIME_AGO}}">',
      template_after: "</item>"
    };
  }
  /**
   * @param {object} context_items_data - data.context_items{}
   * @param {object} params
   * @param {string} [params.codeblock_source_key] - Optional key of the current source for codeblock context (glues name change sync)
   * @returns {ContextItem[]}
   */
  load_from_data(context_items_data, params = {}) {
    const loaded_items = [];
    if (!this.items) this.items = {};
    const named_context_stack = Array.isArray(params.named_context_stack) ? params.named_context_stack : [this.smart_context?.data?.name].filter(Boolean);
    const load_params = {
      ...params,
      named_context_stack
    };
    const entries = Object.entries(context_items_data || {});
    for (let i = 0; i < entries.length; i++) {
      const [key, item_data] = entries[i];
      const loaded = this.load_item_from_data(key, item_data, load_params);
      if (loaded) {
        if (Array.isArray(loaded)) {
          if (!loaded.length) continue;
          const total_size = loaded.reduce((sum, item) => sum + (item.size || 0), 0);
          const latest_mtime = Math.max(...loaded.map((item) => item.mtime));
          item_data.size = total_size;
          item_data.mtime = latest_mtime;
          item_data.group_items_ct = loaded.length;
          loaded_items.push(...loaded);
        } else {
          item_data.size = loaded.size;
          item_data.mtime = loaded.mtime;
          loaded_items.push(loaded);
        }
      }
    }
    return loaded_items;
  }
  /**
   * @param {string} key
   * @param {object} item_data
   * @param {object} params
   * @param {string} [params.codeblock_source_key] - Optional key of the current source for codeblock context (glues name change sync)
   * @return {ContextItem|ContextItem[]|null}
   */
  load_item_from_data(key, item_data, params = {}) {
    if (item_data.named_context) {
      return this.load_named_context_items(key, item_data, params);
    } else {
      return this.new_item({
        key,
        ...item_data
      });
    }
  }
  load_named_context_items(key, item_data, params = {}) {
    let resp = null;
    const named_context_name = item_data?.key || key;
    const named_context_stack = Array.isArray(params.named_context_stack) ? params.named_context_stack : [];
    const named_context = this.env.smart_contexts.filter((ctx) => ctx.data.name === named_context_name)[0];
    if (named_context) {
      if (named_context === this.smart_context || named_context_stack.includes(named_context_name)) {
        return null;
      }
      const loaded_items = this.load_from_data(named_context.data.context_items || {}, {
        ...params,
        named_context_stack: [...named_context_stack, named_context_name]
      });
      if (!loaded_items.length) return null;
      loaded_items.forEach((item) => {
        if (!item?.data || typeof item.data !== "object") return;
        item.data.from_named_context = named_context_name;
      });
      if (typeof params.codeblock_source_key !== "undefined") {
        if (!named_context.data.codeblock_inclusions) named_context.data.codeblock_inclusions = {};
        named_context.data.codeblock_inclusions[params.codeblock_source_key] = Date.now();
        named_context.queue_save();
      }
      resp = loaded_items;
    } else {
      console.warn(`ContextItems.load_from_data: named context "${item_data.key}" not found`);
      this.emit_error_event("context_items:load_from_data", {
        message: "Named context not found",
        named_context: item_data.named_context
      });
      resp = null;
    }
    return resp;
  }
};
var context_items_default = {
  version: ContextItems.version,
  class: ContextItems,
  collection_key: "context_items",
  item_type: ContextItem,
  context_item_adapters: {
    BlockContextItemAdapter,
    SourceContextItemAdapter,
    ImageContextItemAdapter,
    PdfContextItemAdapter
  }
};

// node_modules/obsidian-smart-env/src/collections/event_logs.js
var import_obsidian4 = require("obsidian");

// node_modules/obsidian-smart-env/node_modules/smart-events/event_log.js
function next_log_stats(prev = {}, at_ms) {
  const ct = (prev.ct || 0) + 1;
  const first_at = prev.first_at ?? at_ms;
  const last_at = at_ms;
  return { ct, first_at, last_at };
}
var EventLog = class extends CollectionItem {
  static version = 2e-3;
  /** @returns {{data: EventLogData}} */
  static get defaults() {
    return {
      data: {
        key: null,
        ct: 0,
        first_at: null,
        last_at: null
      }
    };
  }
  /**
   * Counters are updated via EventLogs listener.
   * @param {Partial<EventLogData>} [_input_data]
   */
  init(_input_data) {
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-events/event_level_utils.js
var notification_levels = Object.freeze([
  "milestone",
  "attention",
  "error",
  "warning",
  "info"
]);
var notification_level_set = new Set(notification_levels);
var severity_order = {
  attention: 1,
  warning: 2,
  error: 3
};
function normalize_event_level(level) {
  if (typeof level !== "string") return null;
  const normalized_level = level.trim().toLowerCase();
  if (!notification_level_set.has(normalized_level)) return null;
  return normalized_level;
}
function get_legacy_notification_level(event_key = "") {
  if (typeof event_key !== "string") return null;
  const trimmed_event_key = event_key.trim().toLowerCase();
  if (!trimmed_event_key.startsWith("notification:")) return null;
  const [, legacy_level = ""] = trimmed_event_key.split(":");
  return normalize_event_level(legacy_level);
}
function get_display_fallback_level(event_key = "") {
  if (typeof event_key !== "string") return null;
  const trimmed_event_key = event_key.trim().toLowerCase();
  const event_key_parts = trimmed_event_key.split(":").filter(Boolean);
  const last_part = event_key_parts[event_key_parts.length - 1];
  if (last_part === "error") return "error";
  return null;
}
function get_event_level(event_key = "", event = {}, params = {}) {
  const { allow_display_fallback = false } = params;
  const payload_level = normalize_event_level(event?.level);
  if (payload_level) return payload_level;
  const legacy_level = get_legacy_notification_level(event_key);
  if (legacy_level) return legacy_level;
  if (allow_display_fallback) {
    return get_display_fallback_level(event_key);
  }
  return null;
}
function get_event_severity(level) {
  const normalized_level = normalize_event_level(level);
  if (normalized_level === "milestone") return "attention";
  if (normalized_level === "attention") return "attention";
  if (normalized_level === "warning") return "warning";
  if (normalized_level === "error") return "error";
  return null;
}
function get_next_notification_status(current_status, event_key = "", event = {}) {
  const next_status = get_event_severity(get_event_level(event_key, event));
  if (!next_status) return current_status ?? null;
  const current_rank = severity_order[current_status] || 0;
  const next_rank = severity_order[next_status] || 0;
  if (next_rank > current_rank) return next_status;
  return current_status ?? next_status;
}

// node_modules/obsidian-smart-env/node_modules/smart-events/event_logs.js
var EXCLUDED_EVENT_KEYS = {
  "collection:save_started": true,
  "collection:save_completed": true,
  "notifications:seen": true,
  "notifications:seen_all": true,
  "event_logs:mute_changed": true,
  "event_log:first": true
};
var EventLogs = class extends Collection {
  static version = 4e-3;
  constructor(env, opts = {}) {
    super(env, opts);
    this.session_events = [];
    this.notification_status = null;
  }
  /**
   * Factory that attaches the collection to env and registers the wildcard listener.
   * @param {Object} env
   * @param {Object} [opts={}]
   * @returns {EventLogs}
   */
  static create(env, opts = {}) {
    const instance = new this(env, opts);
    instance.init();
    return instance;
  }
  /** Prefer an explicit item class to keep wiring thin. */
  get item_type() {
    return EventLog;
  }
  /**
   * Instance init
   * - Ensure env.events exists
   * - Register wildcard listener
   * - Idempotent across repeated calls
   */
  init() {
    if (!this.env?.events) SmartEvents.create(this.env);
    if (this._unsub_wildcard) this._unsub_wildcard();
    this._unsub_wildcard = this.env.events.on(WILDCARD_KEY, (event, event_key) => {
      this.on_any_event(event_key, event);
    });
  }
  /**
   * Handle any emitted event.
   * Persists counters and timestamps in epoch ms.
   *
   * @param {string} event_key
   * @param {Record<string, unknown>} event
   * @param {boolean} [event.skip_save_log_collection=false] - avoid unnecessary saves for high-frequency events
   * @returns {{event_key: string, event: Record<string, unknown>, at: number, level: string | null, unseen: boolean, native_notice_shown: boolean} | null}
   */
  on_any_event(event_key, event) {
    if (EXCLUDED_EVENT_KEYS[event_key]) return null;
    const at_ms = typeof event?.at === "number" ? event.at : Date.now();
    const derived_level = get_event_level(event_key, event);
    const session_entry = {
      event_key,
      event,
      at: at_ms,
      level: derived_level,
      unseen: Boolean(derived_level),
      native_notice_shown: false
    };
    this.session_events.push(session_entry);
    this.notification_status = get_next_notification_status(this.notification_status, event_key, event);
    try {
      if (typeof event_key !== "string") return session_entry;
      let event_log = this.get(event_key);
      if (!event_log) {
        event_log = new EventLog(this.env, { key: event_key });
        this.set(event_log);
        this.emit_event("event_log:first", { first_of_event_key: event_key });
      }
      const next = next_log_stats(
        {
          ct: event_log.data.ct,
          first_at: event_log.data.first_at,
          last_at: event_log.data.last_at
        },
        at_ms
      );
      event_log.data = { ...event_log.data, ...next };
      if (event?.event_source) {
        if (!event_log.data.event_sources) event_log.data.event_sources = {};
        if (!event_log.data.event_sources[event.event_source]) {
          event_log.data.event_sources[event.event_source] = 0;
        }
        event_log.data.event_sources[event.event_source] += 1;
      }
      event_log.queue_save();
      if (!event.skip_save_log_collection) this.queue_save();
    } catch (err) {
      console.error("[EventLogs] record failure", event_key, err);
    }
    return session_entry;
  }
  /**
   * Recompute severity status from unseen session entries.
   *
   * @returns {'attention'|'warning'|'error'|null}
   */
  refresh_notification_status() {
    this.notification_status = this.session_events.reduce((current_status, session_entry) => {
      if (!session_entry?.unseen) return current_status;
      return get_next_notification_status(current_status, session_entry.event_key, session_entry.event);
    }, null);
    return this.notification_status;
  }
  /**
   * Return unseen session entries.
   *
   * @returns {Array<object>}
   */
  get_unseen_notification_entries() {
    return this.session_events.filter((session_entry) => session_entry?.unseen === true);
  }
  /**
   * Count unseen canonical notifications.
   *
   * @returns {number}
   */
  get_unseen_notification_count() {
    return this.get_unseen_notification_entries().length;
  }
  /**
   * Mark a single session entry as seen.
   *
   * @param {object} session_entry
   * @param {object} [params={}]
   * @param {boolean} [params.native_notice_shown=false]
   * @returns {boolean}
   */
  mark_session_entry_seen(session_entry, params = {}) {
    if (!session_entry || session_entry.unseen !== true) return false;
    const { native_notice_shown = false } = params;
    session_entry.unseen = false;
    if (native_notice_shown) {
      session_entry.native_notice_shown = true;
    }
    this.refresh_notification_status();
    this.env?.events?.emit?.("notifications:seen", {
      event_key: session_entry.event_key,
      native_notice_shown
    });
    return true;
  }
  /**
   * Mark all unseen canonical notifications as seen.
   *
   * @returns {boolean}
   */
  mark_all_notification_entries_seen() {
    let seen_count = 0;
    for (const session_entry of this.session_events) {
      if (session_entry?.unseen !== true) continue;
      session_entry.unseen = false;
      seen_count += 1;
    }
    if (!seen_count) return false;
    this.refresh_notification_status();
    this.env?.events?.emit?.("notifications:seen_all", { count: seen_count });
    return true;
  }
  /**
   * Cleanly detach listeners and cancel pending save.
   */
  unload() {
    if (this._save_timer) {
      clearTimeout(this._save_timer);
      this._save_timer = null;
    }
    if (typeof this._unsub_wildcard === "function") {
      this._unsub_wildcard();
      this._unsub_wildcard = null;
    }
    return super.unload();
  }
};
var event_logs_default = {
  class: EventLogs,
  collection_key: "event_logs",
  data_adapter: AjsonSingleFileCollectionDataAdapter,
  item_type: EventLog
};

// node_modules/obsidian-smart-env/src/utils/event_logs_utils.js
var notice_timeout_ms = 7e3;
var milestone_notice_timeout_ms = 12e3;
function is_event_log_muted(event_log) {
  return Boolean(event_log?.data?.muted);
}
function get_notification_setting_key(level) {
  const normalized_level = normalize_event_level(level);
  if (!normalized_level) return null;
  if (!notification_levels.includes(normalized_level)) return null;
  return `native_notice_${normalized_level}`;
}
function get_notice_setting_value(instance, setting_key) {
  if (!setting_key) return false;
  const explicit_value = instance?.settings?.[setting_key];
  if (typeof explicit_value === "boolean") return explicit_value;
  const default_value = instance?.constructor?.default_settings?.[setting_key];
  if (typeof default_value === "boolean") return default_value;
  return false;
}
function should_show_native_notice(instance, params = {}) {
  const { event_key = "", event = {} } = params;
  const level = get_event_level(event_key, event);
  if (!level) return false;
  const setting_key = get_notification_setting_key(level);
  if (!setting_key) return false;
  if (!get_notice_setting_value(instance, setting_key)) return false;
  const event_log = typeof instance?.get === "function" ? instance.get(event_key) : instance?.items?.[event_key];
  if (is_event_log_muted(event_log)) return false;
  return true;
}
function get_timeout_override(event = {}) {
  const timeout_value = event.timeout_ms ?? event.timeout;
  if (typeof timeout_value !== "number" || !Number.isFinite(timeout_value)) return null;
  if (timeout_value < 0) return null;
  return timeout_value;
}
function get_native_notice_timeout(event_key, event = {}) {
  const timeout_override = get_timeout_override(event);
  if (timeout_override !== null) return timeout_override;
  const level = get_event_level(event_key, event);
  if (level === "milestone") return milestone_notice_timeout_ms;
  return notice_timeout_ms;
}
function get_native_notice_message(event_key, event = {}) {
  if (typeof event?.message === "string" && event.message.trim()) return event.message.trim();
  if (typeof event?.details === "string" && event.details.trim()) return event.details.trim();
  if (typeof event?.milestone === "string" && event.milestone.trim()) return event.milestone.trim();
  return event_key || "notification";
}

// node_modules/obsidian-smart-env/src/utils/notice_action_dispatch.js
function to_trimmed_string(value) {
  return typeof value === "string" ? value.trim() : "";
}
function is_plain_object4(value) {
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}
function get_source_event(params = {}) {
  if (is_plain_object4(params.source_event)) return params.source_event;
  if (is_plain_object4(params.event)) return params.event;
  return {};
}
function get_source_event_key(params = {}) {
  return to_trimmed_string(params.source_event_key) || to_trimmed_string(params.event_key);
}
function dispatch_btn_event_action(env, params = {}) {
  const {
    event_source = "native_notice_button",
    source_event_key = "",
    source_event = {}
  } = params;
  const btn_event_key = to_trimmed_string(source_event?.btn_event_key);
  if (!btn_event_key) return null;
  if (typeof env?.events?.emit !== "function") return false;
  const btn_event_payload = is_plain_object4(source_event?.btn_event_payload) ? source_event.btn_event_payload : {};
  env.events.emit(btn_event_key, {
    ...btn_event_payload,
    event_source,
    source_event_key,
    source_event
  });
  return true;
}
function dispatch_notice_action(env, callback_key, params = {}) {
  const event_source = to_trimmed_string(params.event_source) || "native_notice_button";
  const source_event_key = get_source_event_key(params);
  const source_event = get_source_event(params);
  const btn_event_result = dispatch_btn_event_action(env, {
    event_source,
    source_event_key,
    source_event
  });
  if (btn_event_result !== null) return btn_event_result;
  const next_callback_key = to_trimmed_string(callback_key);
  if (!next_callback_key) return false;
  const app_commands = env?.main?.app?.commands;
  if (app_commands?.commands?.[next_callback_key] && typeof app_commands.executeCommandById === "function") {
    app_commands.executeCommandById(next_callback_key);
    return true;
  }
  if (typeof env?.events?.emit !== "function") return false;
  env.events.emit(next_callback_key, {
    event_source,
    source_event_key,
    source_event
  });
  return true;
}

// node_modules/obsidian-smart-env/src/collections/event_logs.js
var settings_config4 = {
  native_notice_info: {
    name: "Info",
    description: 'Show Obsidian native notices for "info" level events.',
    type: "toggle"
  },
  native_notice_warning: {
    name: "Warning",
    description: 'Show Obsidian native notices for "warning" level events.',
    type: "toggle"
  },
  native_notice_error: {
    name: "Error",
    description: 'Show Obsidian native notices for "error" level events.',
    type: "toggle"
  },
  native_notice_attention: {
    name: "Attention",
    description: 'Show Obsidian native notices for "attention" level events.',
    type: "toggle"
  },
  native_notice_milestone: {
    name: "Milestone",
    description: 'Show Obsidian native notices for "milestone" level events.',
    type: "toggle"
  }
};
var native_notice_component_key_map = Object.freeze({
  milestone: "milestone_notification"
});
function get_native_notice_component_key(event_key, event = {}) {
  const level = get_event_level(event_key, event);
  if (!level) return null;
  return native_notice_component_key_map[level] || "default_notification";
}
var EventLogs2 = class extends EventLogs {
  static version = 4e-3;
  static get default_settings() {
    return {
      ...super.default_settings || {},
      native_notice_info: false,
      native_notice_warning: true,
      native_notice_error: true,
      native_notice_attention: false,
      native_notice_milestone: true
    };
  }
  get settings_config() {
    return { ...settings_config4 };
  }
  /**
   * @param {string} event_key
   * @param {Record<string, unknown>} event
   * @returns {object|null}
   */
  on_any_event(event_key, event = {}) {
    const session_entry = super.on_any_event(event_key, event);
    this.show_native_notice(session_entry, { event_key, event });
    return session_entry;
  }
  /**
   * @param {object|null} session_entry
   * @param {object} [params={}]
   * @param {string} [params.event_key='']
   * @param {Record<string, unknown>} [params.event={}]
   * @returns {boolean}
   */
  async show_native_notice(session_entry, params = {}) {
    const { event_key = "", event = {} } = params;
    if (!should_show_native_notice(this, { event_key, event })) return false;
    try {
      const notice_content = await this.build_native_notice_content(event_key, event);
      const notice_timeout = get_native_notice_timeout(event_key, event);
      new import_obsidian4.Notice(notice_content, notice_timeout);
    } catch (error) {
      console.error("EventLogs: failed to show native notice", {
        event_key,
        error
      });
      return false;
    }
    if (session_entry) {
      this.mark_session_entry_seen(session_entry, { native_notice_shown: true });
    }
    return true;
  }
  /**
   * @param {string} event_key
   * @param {Record<string, unknown>} [event={}]
   * @returns {string|DocumentFragment}
   */
  async build_native_notice_content(event_key, event = {}) {
    const notice_event = event?.btn_event_key && !event?.btn_callback ? { ...event, btn_callback: event.btn_event_key } : event;
    const component_key = get_native_notice_component_key(event_key, notice_event);
    if (component_key && this.env?.config?.components?.[component_key]) {
      const component_content = await this.env.smart_components.render_component(component_key, this.env, {
        event_key,
        event: notice_event,
        on_action: (callback_key) => this.run_notice_callback(callback_key, { event_key, event: notice_event }),
        on_mute: () => this.set_event_key_muted(event_key, true)
      });
      if (component_content) return component_content;
    }
    const notice_message = get_native_notice_message(event_key, notice_event);
    const btn_text = typeof notice_event?.btn_text === "string" ? notice_event.btn_text.trim() : "";
    const btn_callback = typeof notice_event?.btn_callback === "string" ? notice_event.btn_callback.trim() : "";
    if (!btn_text || !btn_callback || typeof document === "undefined") {
      return notice_message;
    }
    const frag = document.createDocumentFragment();
    const message_el = document.createElement("div");
    message_el.textContent = notice_message;
    frag.appendChild(message_el);
    const button_el = document.createElement("button");
    button_el.type = "button";
    button_el.className = "mod-cta";
    button_el.textContent = btn_text;
    button_el.addEventListener("click", () => {
      this.run_notice_callback(btn_callback, { event_key, event: notice_event });
    });
    frag.appendChild(button_el);
    return frag;
  }
  /**
   * @param {string} callback_key
   * @param {object} [params={}]
   * @param {string} [params.event_key='']
   * @param {Record<string, unknown>} [params.event={}]
   * @returns {boolean}
   */
  run_notice_callback(callback_key, params = {}) {
    return dispatch_notice_action(this.env, callback_key, {
      event_source: "native_notice_button",
      source_event_key: params.event_key,
      source_event: params.event || {}
    });
  }
  /**
   * @param {string} event_key
   * @returns {boolean}
   */
  is_event_key_muted(event_key) {
    return is_event_log_muted(this.get?.(event_key));
  }
  /**
   * @param {string} event_key
   * @param {boolean} [muted=true]
   * @returns {boolean}
   */
  set_event_key_muted(event_key, muted = true) {
    const event_log = this.get?.(event_key);
    if (!event_log) return false;
    event_log.data = {
      ...event_log.data,
      muted: Boolean(muted)
    };
    event_log.queue_save?.();
    this.queue_save?.();
    this.env?.events?.emit?.("event_logs:mute_changed", {
      event_key,
      muted: Boolean(muted)
    });
    return true;
  }
  /**
   * @param {string} event_key
   * @returns {boolean}
   */
  toggle_event_key_muted(event_key) {
    const next_muted = !this.is_event_key_muted(event_key);
    return this.set_event_key_muted(event_key, next_muted);
  }
};
var event_logs_default2 = {
  ...event_logs_default,
  class: EventLogs2,
  settings_config: settings_config4
};

// node_modules/obsidian-smart-env/src/modals/smart_fuzzy_suggest_modal.js
var import_obsidian5 = require("obsidian");

// node_modules/obsidian-smart-env/src/utils/smart_fuzzy_suggest_utils.js
function build_suggest_scope_items(modal, params = {}) {
  if (!modal) return [];
  const action_keys = Array.isArray(params.action_keys) ? params.action_keys : [];
  const action_configs = modal?.env?.config?.actions || {};
  const action_handlers = modal?.item_or_collection?.actions || {};
  const unique_action_keys = [...new Set(action_keys)];
  return unique_action_keys.reduce((acc, action_key) => {
    const action_handler = action_handlers[action_key];
    if (typeof action_handler !== "function") return acc;
    const action_config = action_configs[action_key] || {};
    const display_name7 = action_config.display_name || action_key;
    acc.push({
      select_action: () => {
        modal.update_suggestions(action_key);
        setTimeout(() => {
          modal.inputEl.focus();
        }, 100);
      },
      key: action_key,
      display: display_name7
    });
    return acc;
  }, []);
}
var should_handle_arrow_left = (modal, params = {}) => {
  const input_el = modal?.inputEl;
  const event_target = params.event_target;
  const input_value = typeof params.input_value === "string" ? params.input_value : input_el?.value || "";
  if (event_target === input_el && input_value) {
    return false;
  }
  return true;
};

// node_modules/obsidian-smart-env/src/modals/smart_fuzzy_suggest_modal.js
var SmartFuzzySuggestModal = class extends import_obsidian5.FuzzySuggestModal {
  constructor(item_or_collection) {
    const env = item_or_collection.env;
    const plugin = env.plugin;
    const app2 = plugin.app;
    super(app2);
    this.app = app2;
    env.create_env_getter(this);
    this.plugin = plugin;
    this.item_or_collection = item_or_collection;
    this.emptyStateText = "No suggestions available";
    this._set_custom_instructions = false;
  }
  /** Unique type key for this modal class. Subclasses override. */
  static get modal_type() {
    return "smart-fuzzy-suggest";
  }
  /** Human label used in commands. Subclasses override as needed. */
  static get display_text() {
    return "Smart Fuzzy Suggest";
  }
  /** Event name listened to on env.events to open this modal. */
  static get event_domain() {
    return `${this.modal_type}`;
  }
  /** Command id used with addCommand. */
  static get command_id() {
    return this.modal_type;
  }
  static open(item_or_collection, params) {
    const Modal12 = (
      /** @type {typeof SmartFuzzySuggestModal} */
      this
    );
    const modal = new Modal12(item_or_collection, params);
    modal.open(params);
    return modal;
  }
  static register_modal(plugin) {
    const Modal12 = (
      /** @type {typeof SmartFuzzySuggestModal} */
      this
    );
    const env = plugin?.env;
    const modal_config = {
      ...env.config.modals?.[this.modal_key] || {},
      class: null
    };
    console.log(`Registering modal: ${this.display_text}`, { modal_config, Modal: Modal12 });
    const open_handler = (payload = {}) => {
      const item = Modal12.resolve_item_from_payload(env, payload);
      const modal = Modal12.open(item, {
        ...modal_config,
        ...payload
        // spread since event payload is locked
      });
      return modal;
    };
    const disposers = [
      env?.events?.on?.(`${Modal12.event_domain}:open`, open_handler)
      // env.events?.on?.(`${Modal.event_domain}:suggest`, suggest_handler),
    ];
    const dispose_all = () => {
      disposers.forEach((dispose) => typeof dispose === "function" && dispose());
    };
    if (typeof plugin.register === "function") {
      plugin.register(() => dispose_all());
    }
    return {
      event_domain: Modal12.event_domain
    };
  }
  static resolve_item_from_payload(env, payload) {
    const item = env?.[payload.collection_key]?.items?.[payload.item_key];
    return item;
  }
  setInstructions(instructions, is_custom = true) {
    this._set_custom_instructions = is_custom;
    super.setInstructions(instructions);
  }
  set_default_instructions() {
    this.setInstructions([
      { command: "Enter", purpose: "Select" }
    ], false);
  }
  open(params = {}) {
    super.open();
    this.modalEl.addEventListener("keydown", (e) => {
      if (e.key === "Enter") {
        if (e.shiftKey) this.use_shift_select = true;
        this.selectActiveSuggestion(e);
      }
      const is_cursor_end_of_input = this.inputEl.selectionStart === this.inputEl.value.length;
      const should_handle_arrow_right = is_cursor_end_of_input || e.target !== this.inputEl || !this.inputEl.value;
      const should_handle_arrow_left_action = should_handle_arrow_left(this, {
        event_target: e.target,
        input_value: this.inputEl.value
      });
      if (e.key === "ArrowLeft" && should_handle_arrow_left_action) {
        this.use_arrow_left = true;
        this.selectActiveSuggestion(e);
        return;
      }
      if (e.key === "ArrowRight" && should_handle_arrow_right) {
        e.preventDefault();
        this.use_mod_select = true;
        this.use_arrow_right = true;
        this.selectActiveSuggestion(e);
        return;
      }
    });
  }
  getItems() {
    return this.get_suggestions();
  }
  getItemText(suggestion_item) {
    return suggestion_item.display;
  }
  filter_suggestions(suggestions) {
    return suggestions;
  }
  get_suggestions() {
    if (this.suggestions?.length) {
      this.suggestions = this.filter_suggestions(this.suggestions);
      if (this.suggestions.length > 0) {
        return this.suggestions;
      }
    }
    if (this.default_suggest_action_keys?.length) {
      if (this.default_suggest_action_keys.length === 1) {
        this.update_suggestions(this.default_suggest_action_keys[0]);
        return [];
      }
      return this.get_suggest_scopes();
    }
    return [];
  }
  get_suggest_scopes() {
    return build_suggest_scope_items(this, {
      action_keys: this.default_suggest_action_keys
    });
  }
  async update_suggestions(suggest_ref) {
    if (typeof suggest_ref === "string") {
      suggest_ref = this.item_or_collection.actions[suggest_ref];
    }
    if (typeof suggest_ref === "function") {
      this._set_custom_instructions = false;
      const result = await suggest_ref({ modal: this });
      console.log("Suggestion action result", result);
      if (Array.isArray(result) && result.length) {
        this.suggestions = result;
      }
    } else if (Array.isArray(suggest_ref)) {
      this.suggestions = suggest_ref;
    }
    if (Array.isArray(this.suggestions) && this.suggestions.length) {
      this.updateSuggestions();
    } else {
      this.env.events.emit("notification:error", { message: "Invalid suggestion action" });
      console.warn("Invalid suggestion action", suggest_ref);
    }
    if (!this._set_custom_instructions) {
      this.set_default_instructions();
    }
  }
  get default_suggest_action_keys() {
    if (Array.isArray(this.params?.default_suggest_action_keys)) {
      return this.params.default_suggest_action_keys;
    }
    return this.env.config.modals[this.modal_key]?.default_suggest_action_keys || [];
  }
  renderSuggestion(sug, el) {
    super.renderSuggestion(sug, el);
    const icon = sug?.icon || sug?.item?.icon;
    if (icon) {
      el.addClass("sc-modal-suggestion-has-icon");
      const icon_el = el.createEl("span");
      (0, import_obsidian5.setIcon)(icon_el, icon);
    }
    const display_right_raw = sug && Object.prototype.hasOwnProperty.call(sug, "display_right") ? sug.display_right : sug?.item?.display_right;
    const display_right = display_right_raw === null || display_right_raw === void 0 ? "" : String(display_right_raw).trim();
    if (display_right) {
      this.env.smart_components.render_component("suggest_display_right", display_right).then((right_el) => {
        el.appendChild(right_el);
      });
    }
    return el;
  }
  onChooseSuggestion(selected, evt, ...other) {
    this.prevent_close = true;
    const suggestion = selected.item;
    const is_arrow_left = this.use_arrow_left;
    const is_arrow_right = this.use_arrow_right;
    const is_shift_select = evt?.shiftKey || this.use_shift_select;
    const is_mod_select = import_obsidian5.Keymap.isModifier(evt, "Mod") || this.use_mod_select;
    this.use_arrow_right = false;
    this.use_mod_select = false;
    this.use_arrow_left = false;
    this.use_shift_select = false;
    if (is_arrow_left) {
      if (typeof suggestion.arrow_left_action === "function") {
        this.handle_choose_action(suggestion, "arrow_left_action");
      } else {
        if (this.last_input_value) {
          this.inputEl.value = this.last_input_value;
          setTimeout(() => {
            const len = this.inputEl.value.length;
            this.inputEl.setSelectionRange(len, len);
          }, 0);
          this.last_input_value = null;
        }
        this.suggestions = null;
        this.params.default_suggest_action_keys = null;
        this.updateSuggestions();
        return;
      }
    } else if (is_arrow_right && typeof suggestion.arrow_right_action === "function") {
      this.handle_choose_action(suggestion, "arrow_right_action");
    } else if (is_mod_select && typeof suggestion.mod_select_action === "function") {
      this.handle_choose_action(suggestion, "mod_select_action");
    } else if (is_shift_select && typeof suggestion.shift_select_action === "function") {
      this.handle_choose_action(suggestion, "shift_select_action");
    } else if (typeof suggestion.select_action === "function") {
      this.handle_choose_action(suggestion, "select_action");
    } else {
      this.env.events.emit("notification:warning", { selection_display: suggestion.display, message: "No action defined for this suggestion" });
    }
  }
  async handle_choose_action(suggestion, action_key) {
    let chosen_action = suggestion[action_key];
    const result = await chosen_action({ modal: this });
    if (Array.isArray(result) && result.length) {
      this.suggestions = result;
    } else if (Array.isArray(result)) {
      this.env.events.emit("notification:info", { message: "No suggestions returned from action" });
    }
    const idx = this.chooser.values.findIndex((i) => i.item?.display === suggestion.display);
    setTimeout(() => {
      this.updateSuggestions();
      if (idx !== -1) {
        this.chooser.setSelectedItem(idx);
      }
    }, 100);
  }
  close() {
    setTimeout(() => {
      if (!this.prevent_close) super.close();
      this.prevent_close = false;
    }, 10);
  }
  onClose() {
    this.item_or_collection.emit_event(`${this.constructor.event_domain}:closed`);
  }
};

// node_modules/obsidian-smart-env/src/modals/context_selector.js
var import_obsidian6 = require("obsidian");
var ContextModal = class extends SmartFuzzySuggestModal {
  /** Modal identity */
  static get modal_type() {
    return "context_selector";
  }
  static get display_text() {
    return "Context Selector";
  }
  static get event_domain() {
    return "context_selector";
  }
  static get command_id() {
    return this.modal_type;
  }
  static get modal_key() {
    return "context_selector";
  }
  get modal_key() {
    return "context_selector";
  }
  constructor(smart_context, params = {}) {
    super(smart_context);
    this.params = { ...params };
    this.smart_context = smart_context;
    this.set_default_instructions();
  }
  set_default_instructions() {
    this.setInstructions([
      { command: "Enter", purpose: "Add to context" },
      { command: `\u2192 / \u2190`, purpose: "Toggle block view" },
      { command: "Esc", purpose: "Close" }
    ]);
  }
  open(params = {}) {
    this.params = { ...this.params, ...params };
    super.open();
    this.render(this.params);
  }
  async render(params = this.params) {
    this.modalEl.style.display = "flex";
    this.modalEl.style.flexDirection = "column";
    this.modalEl.prepend(
      await this.env.smart_components.render_component(
        "smart_context_item",
        this.smart_context,
        params
      )
    );
  }
  filter_suggestions(suggestions) {
    return suggestions.filter((s) => {
      if (s.key && this.smart_context?.data?.context_items[s.key]) return false;
      return true;
    });
  }
};

// node_modules/obsidian-smart-env/src/modals/notifications_feed_modal.js
var import_obsidian7 = require("obsidian");
var NotificationsFeedModal = class extends import_obsidian7.Modal {
  constructor(app2, env, params = {}) {
    super(app2);
    this.env = env;
    this.params = params;
  }
  async onOpen() {
    if (this.modalEl?.classList) {
      this.modalEl.classList.add("smart-env-notifications-modal");
    }
    this.titleEl.setText("Events & notifications");
    this.contentEl.empty();
    const event_log = await this.env.smart_components.render_component("notifications_feed", this.env, {
      live_updates: true,
      auto_mark_seen: true,
      ...this.params,
      state: get_notifications_feed_state(this.params)
    });
    this.contentEl.appendChild(event_log);
  }
  onClose() {
    this.contentEl.empty();
    if (this.modalEl?.classList) {
      this.modalEl.classList.remove("smart-env-notifications-modal");
    }
  }
};
function get_notifications_feed_state(params = {}) {
  const state = params?.state && typeof params.state === "object" ? { ...params.state } : {};
  const target_entry_key = state.target_entry_key || get_target_entry_key(params?.event_key, params?.event);
  if (!target_entry_key) return state;
  const expanded_entry_keys = state.expanded_entry_keys instanceof Set ? new Set(state.expanded_entry_keys) : /* @__PURE__ */ new Set();
  expanded_entry_keys.add(target_entry_key);
  return {
    ...state,
    target_entry_key,
    expanded_entry_keys
  };
}
function get_target_entry_key(event_key = "", event = {}) {
  const next_event_key = typeof event_key === "string" ? event_key.trim() : "";
  if (!next_event_key) return "";
  const at = event?.at;
  if (typeof at !== "number" || !Number.isFinite(at)) return "";
  return `${next_event_key}:${at}`;
}

// node_modules/obsidian-smart-env/src/modals/milestones_modal.js
var import_obsidian8 = require("obsidian");
var MILESTONES_HELP_URL = "https://smartconnections.app/smart-environment/milestones/?utm_source=milestones_modal_help";
var MilestonesModal = class extends import_obsidian8.Modal {
  constructor(app2, env) {
    super(app2);
    this.env = env;
  }
  async onOpen() {
    render_milestones_modal_title(this.titleEl, this.env);
    this.contentEl.empty();
    const milestones = await this.env.smart_components.render_component("milestones", this.env, {});
    this.contentEl.appendChild(milestones);
  }
  onClose() {
    this.contentEl.empty();
  }
};
function render_milestones_modal_title(title_el, env) {
  if (!title_el) return;
  title_el.empty();
  title_el.classList.add("sc-milestones-modal__title");
  const row_el = document.createElement("div");
  row_el.className = "sc-milestones-modal__title-row";
  const text_el = document.createElement("div");
  text_el.className = "sc-milestones-modal__title-text";
  text_el.textContent = "Smart Milestones";
  const help_btn_el = document.createElement("button");
  help_btn_el.type = "button";
  help_btn_el.className = "sc-milestones-modal__help-btn";
  help_btn_el.setAttribute("aria-label", "Open Smart Milestones help");
  help_btn_el.setAttribute("title", "Help");
  render_help_icon(help_btn_el);
  help_btn_el.addEventListener("click", (evt) => {
    evt.preventDefault();
    evt.stopPropagation();
    window.open(MILESTONES_HELP_URL, "_external");
  });
  row_el.appendChild(text_el);
  row_el.appendChild(help_btn_el);
  title_el.appendChild(row_el);
}
function render_help_icon(icon_el) {
  const ok = set_icon_with_fallback(icon_el, ["circle-help", "help-circle", "help", "info"]);
  if (!ok) icon_el.textContent = "?";
}
function set_icon_with_fallback(icon_el, icon_ids) {
  if (!icon_el) return false;
  const ids = Array.isArray(icon_ids) ? icon_ids : [];
  for (const icon_id of ids) {
    if (typeof icon_id !== "string" || icon_id.length === 0) continue;
    icon_el.textContent = "";
    try {
      (0, import_obsidian8.setIcon)(icon_el, icon_id);
    } catch (err) {
      continue;
    }
    if (icon_el.querySelector("svg")) return true;
  }
  return false;
}

// node_modules/obsidian-smart-env/src/modals/browse_plugins_modal.js
var import_obsidian9 = require("obsidian");
var BrowseSmartPlugins = class extends import_obsidian9.Modal {
  constructor(app2, env) {
    super(app2);
    this.env = env;
  }
  async onOpen() {
    if (this.modalEl?.classList) {
      this.modalEl.classList.add("smart-env-pro-plugins-modal");
    }
    if (this.modalEl?.style) {
      this.modalEl.style.width = "min(920px, 92vw)";
      this.modalEl.style.maxHeight = "min(820px, 88vh)";
    }
    this.titleEl.setText("Smart Plugins");
    this.contentEl.empty();
    const plugin_store = await this.env.smart_components.render_component("smart_plugins_list", this.env, {
      event_source: "browse_plugins_modal"
    });
    if (plugin_store) {
      this.contentEl.appendChild(plugin_store);
    }
  }
  onClose() {
    this.contentEl.empty();
    if (this.modalEl?.classList) {
      this.modalEl.classList.remove("smart-env-pro-plugins-modal");
    }
    if (this.modalEl?.style) {
      this.modalEl.style.width = "";
      this.modalEl.style.maxHeight = "";
    }
  }
};

// node_modules/obsidian-smart-env/default.settings.js
var default_settings = {
  is_obsidian_vault: true,
  smart_blocks: {
    embed_blocks: true,
    min_chars: 200
  },
  smart_sources: {
    min_chars: 200,
    embed_model: {
      adapter: "transformers",
      transformers: {
        model_key: "TaylorAI/bge-micro-v2"
      }
    },
    excluded_headings: "",
    file_exclusions: "Untitled",
    folder_exclusions: ""
  },
  language: "en",
  re_import_wait_time: 13,
  smart_chat_threads: {
    chat_model: {
      adapter: "ollama",
      ollama: {}
    }
  },
  smart_notices: {},
  smart_view_filter: {
    expanded_view: false,
    render_markdown: true,
    show_full_path: false
  },
  version: "",
  new_user: true,
  // DEPRECATED: 2025-06-05 (use localStorage instead???)
  // 2025-11-26
  models: {
    embedding_platform: "transformers",
    chat_completion_platform: "open_router"
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-models/items/model.js
var Model = class extends CollectionItem {
  /**
   * Default properties for an instance of CollectionItem.
   * @returns {Object}
   */
  static get defaults() {
    return {
      data: {
        api_key: "",
        provider_key: "",
        model_key: ""
      }
    };
  }
  get_key() {
    if (!this.data.key) {
      this.data.created_at = Date.now();
      this.data.key = `${this.data.provider_key}#${this.data.created_at}`;
    }
    return this.data.key;
  }
  get provider_key() {
    return this.data.provider_key;
  }
  get env_config() {
    return this.collection.env_config;
  }
  get provider_config() {
    return this.env_config.providers?.[this.provider_key] || {};
  }
  get ProviderAdapterClass() {
    return this.provider_config.class;
  }
  get instance() {
    if (!this._instance) {
      if (!this.ProviderAdapterClass) {
        const new_default_model = this.collection.new_model({ provider_key: this.collection.default_provider_key });
        return new_default_model.instance;
      }
      const Class = this.ProviderAdapterClass;
      this._instance = new Class(this);
      if (typeof this._instance.load_background === "function") {
        this._instance.load_background();
      } else {
        this._instance.load();
      }
      this.once_event("model:changed", () => {
        this._instance.unload?.();
        this._instance = null;
      });
    }
    return this._instance;
  }
  async count_tokens(text) {
    return this.instance.count_tokens(text);
  }
  get api_key() {
    return this.data.api_key;
  }
  /**
   * Create (or reuse) a proxy around a target settings object so that
   * any mutations trigger queue_save on the model.
   * Proxies are cached per-target via WeakMap to support deep nested objects.
   *
   * @param {Object} target - The settings object or nested object to wrap.
   * @returns {Object} Proxied object or original value if not an object.
   * @private
   */
  create_settings_proxy(target) {
    if (!target || typeof target !== "object") return target;
    if (!this._settings_proxy_map) {
      this._settings_proxy_map = /* @__PURE__ */ new WeakMap();
    }
    const existing = this._settings_proxy_map.get(target);
    if (existing) return existing;
    const self = this;
    const handler = {
      get(target_obj, prop, receiver) {
        const value = Reflect.get(target_obj, prop, receiver);
        if (value && typeof value === "object") {
          return self.create_settings_proxy(value);
        }
        return value;
      },
      set(target_obj, prop, value, receiver) {
        const previous = target_obj[prop];
        const result = Reflect.set(target_obj, prop, value, receiver);
        if (previous !== value) {
          self.debounce_save();
        }
        return result;
      },
      deleteProperty(target_obj, prop) {
        const had = Object.prototype.hasOwnProperty.call(target_obj, prop);
        const result = Reflect.deleteProperty(target_obj, prop);
        if (had) {
          self.debounce_save();
        }
        return result;
      }
    };
    const proxy = new Proxy(target, handler);
    this._settings_proxy_map.set(target, proxy);
    return proxy;
  }
  /**
   * @private
   * @param {number} [ms=100]
   */
  debounce_save(ms = 100) {
    this.emit_event("model:changed");
    if (this._debounce_save_timeout) {
      clearTimeout(this._debounce_save_timeout);
    }
    this._debounce_save_timeout = setTimeout(() => {
      this.queue_save();
      this.collection.process_save_queue();
      this._debounce_save_timeout = null;
    }, ms);
  }
  async get_model_key_options() {
    const model_configs = await this.instance.get_models();
    return Object.entries(model_configs).map(([key, model_config]) => ({
      label: model_config.name || key,
      value: model_config.key || key
    })).sort((a, b) => {
      if (a.label.toLowerCase().includes("free") && !b.label.toLowerCase().includes("free")) {
        return -1;
      }
      if (!a.label.toLowerCase().includes("free") && b.label.toLowerCase().includes("free")) {
        return 1;
      }
      return a.label.localeCompare(b.label);
    });
  }
  /**
   * @private
   * @param {string} key
   * @param {*} value
   * @param {*} elm
   */
  model_changed(key, value, elm) {
    if (key === "model_key") {
      this.data.model_key = value;
      const model_defaults = this.data.provider_models?.[this.data.model_key] || {};
      const adapter_defaults = this.ProviderAdapterClass.defaults || {};
      delete this.data.test_passed;
      this.data = {
        ...this.data,
        ...adapter_defaults,
        ...model_defaults
      };
    }
    if (!["api_key", "meta.name"].includes(key)) {
      this.emit_event("model:changed");
    }
  }
  /**
   * @abstract should be implemented by subclasses
   */
  async test_model() {
  }
  get display_name() {
    return this.data.meta?.name || `${this.data.provider_key} - ${this.data.model_key}`;
  }
  get settings_config() {
    const model = this;
    return {
      provider_key: {
        type: "html",
        value: `<p><strong>Provider:</strong> ${this.data.provider_key}</p>`
      },
      "meta.name": {
        type: "text",
        name: "Name",
        description: "A friendly name for this model configuration."
      },
      model_key: {
        type: "dropdown",
        name: "Model",
        description: "The model to use from the selected provider.",
        options_callback() {
          return model.get_model_key_options();
        },
        callback(value, setting) {
          return model.model_changed("model_key", value, setting);
        }
      },
      // add model_changed callback to each provider setting that doesn't already have callback defined
      ...Object.fromEntries(
        Object.entries(this.provider_config.settings_config || {}).map(
          ([setting_key, setting_config]) => {
            const callback = setting_config.callback || ((value, setting) => {
              return model.model_changed(setting_key, value, setting);
            });
            return [setting_key, { ...setting_config, callback }];
          }
        )
      )
    };
  }
  delete_model() {
    this.delete();
    this.debounce_save();
  }
  /**
   * Reactive settings view for this model.
   * Mutating any property (including nested objects/arrays) via this proxy
   * will call queue_save().
   *
   * @returns {Object} Proxied view of this.data.
   */
  get settings() {
    return this.create_settings_proxy(this.data);
  }
  get model_key() {
    return this.data.model_key;
  }
  /**
   * @deprecated included for backward compatibility (2026-02-11)
   */
  get opts() {
    return this.settings;
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-models/collections/models.js
var Models = class extends Collection {
  model_type = "Model type";
  // replace in subclass
  new_model(data = {}) {
    if (!data.provider_key) throw new Error("provider_key is required to create a new model");
    const existing_from_provider = this.filter((m) => m.provider_key === data.provider_key).sort((a, b) => b.data.created_at - a.data.created_at)[0];
    if (existing_from_provider) {
      if (!data.api_key && existing_from_provider.data.api_key) {
        data.api_key = existing_from_provider.data.api_key;
      }
    }
    const item = new this.item_type(this.env, {
      ...data
    });
    this.set(item);
    this.settings.default_model_key = item.key;
    this.emit_event("model:changed");
    item.queue_save();
    return item;
  }
  /**
   * Retrieve the provider key used when creating a default model.
   * @abstract
   * @returns {string} provider key for the default model.
   */
  get default_provider_key() {
    throw new Error("default_provider_key not implemented");
  }
  get default_model_key() {
    const should_update_default = !this.settings.default_model_key || !this.get(this.settings.default_model_key) || this.get(this.settings.default_model_key).deleted;
    if (should_update_default) {
      const existing = this.filter((m) => !m.deleted).sort((a, b) => b.data.created_at - a.data.created_at)[0];
      if (existing) {
        this.settings.default_model_key = existing.key;
      } else {
        const new_default = this.new_model({ provider_key: this.default_provider_key });
        new_default.queue_save();
        this.process_save_queue();
        this.settings.default_model_key = new_default.key;
      }
    }
    return this.settings.default_model_key;
  }
  get default() {
    return this.get(this.default_model_key);
  }
  get env_config() {
    return this.env.config.collections[this.collection_key];
  }
  get_model_key_options() {
    return this.filter((i) => !i.deleted && i.ProviderAdapterClass).map((model) => ({
      label: model.data.meta?.name || `${model.provider_key} - ${model.data.model_key}`,
      value: model.key
    }));
  }
};
function settings_config5(scope) {
  return {
    default_model_key: {
      type: "dropdown",
      name: `Default ${scope.model_type.toLowerCase()} model`,
      description: `Used as the default ${scope.model_type.toLowerCase()} model when no other is specified.`,
      options_callback: () => {
        return scope.get_model_key_options();
      },
      callback: async (value, setting) => {
        scope.emit_event("model:changed");
      }
    }
  };
}

// node_modules/obsidian-smart-env/node_modules/smart-models/items/embedding_model.js
var EmbeddingModel = class extends Model {
  /**
   * Default properties for an instance of CollectionItem.
   * @returns {Object}
   */
  static get defaults() {
    return {
      data: {
        api_key: "",
        provider_key: "transformers",
        model_key: "TaylorAI/bge-micro-v2",
        dims: 384,
        // ???
        max_tokens: 512
        // ???
      }
    };
  }
  async embed(input) {
    if (typeof input === "string") {
      input = [{ embed_input: input }];
    }
    return (await this.embed_batch(input))[0];
  }
  async embed_batch(inputs) {
    return this.instance.embed_batch(inputs);
  }
  async test_model() {
    try {
      const resp = await this.embed("test input");
      const success = resp && !resp?.error;
      this.data.test_passed = success;
      this.debounce_save();
      return { success, response: resp };
    } catch (e) {
      this.data.test_passed = false;
      return { error: e.message || String(e) };
    }
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-models/collections/embedding_models.js
var EmbeddingModels = class extends Models {
  model_type = "Embedding";
  get default_provider_key() {
    return "transformers";
  }
};
var embedding_models_collection = {
  class: EmbeddingModels,
  data_dir: "embedding_models",
  collection_key: "embedding_models",
  data_adapter: ajson_single_file_default,
  item_type: EmbeddingModel,
  providers: {
    // transformers // replace with platform-specific import in obsidian-smart-env
  },
  settings_config: settings_config5
};
var embedding_models_default = embedding_models_collection;

// dist-text:C:\Users\brian\Documents\smart-connections-obsidian\node_modules\obsidian-smart-env\src\adapters\embedding-model\transformers_v4.iframe.js
var transformers_v4_iframe_default = 'var __defProp = Object.defineProperty;\nvar __name = (target, value) => __defProp(target, "name", { value, configurable: true });\n\n// ../jsbrains/smart-model/adapters/_adapter.js\nvar SmartModelAdapter = class {\n  static {\n    __name(this, "SmartModelAdapter");\n  }\n  /**\n   * Create a SmartModelAdapter instance.\n   * @param {SmartModel} model - The parent SmartModel instance\n   */\n  constructor(model2) {\n    this.model = model2;\n    this.state = "unloaded";\n  }\n  /**\n   * Load the adapter.\n   * @async\n   * @returns {Promise<void>}\n   */\n  async load() {\n    this.set_state("loaded");\n  }\n  /**\n   * Unload the adapter.\n   * @returns {void}\n   */\n  unload() {\n    this.set_state("unloaded");\n  }\n  /**\n   * Get all settings.\n   * @returns {Object} All settings\n   */\n  get settings() {\n    return this.model.settings;\n  }\n  /**\n   * Get the current model key.\n   * @returns {string} Current model identifier\n   */\n  get model_key() {\n    return this.model.model_key;\n  }\n  /**\n   * Get the models.\n   * @returns {Object} Map of model objects\n   */\n  get models() {\n    const models = this.model.data.provider_models;\n    if (typeof models === "object" && Object.keys(models || {}).length > 0) return models;\n    else {\n      return {};\n    }\n  }\n  /**\n   * Get available models from the API.\n   * @abstract\n   * @param {boolean} [refresh=false] - Whether to refresh cached models\n   * @returns {Promise<Object>} Map of model objects\n   */\n  async get_models(refresh = false) {\n    throw new Error("get_models not implemented");\n  }\n  /**\n   * Get available models as dropdown options synchronously.\n   * @returns {Array<Object>} Array of model options.\n   */\n  get_models_as_options() {\n    const models = this.models;\n    if (!Object.keys(models || {}).length) {\n      this.get_models(true);\n      return [{ value: "", name: "No models currently available" }];\n    }\n    return Object.entries(models).map(([id, model2]) => ({ value: id, name: model2.name || id })).sort((a, b) => a.name.localeCompare(b.name));\n  }\n  /**\n   * Set the adapter\'s state.\n   * @deprecated should be handled in SmartModel (only handle once)\n   * @param {(\'unloaded\'|\'loading\'|\'loaded\'|\'unloading\')} new_state - The new state\n   * @throws {Error} If the state is invalid\n   */\n  set_state(new_state) {\n    const valid_states = ["unloaded", "loading", "loaded", "unloading"];\n    if (!valid_states.includes(new_state)) {\n      throw new Error(`Invalid state: ${new_state}`);\n    }\n    this.state = new_state;\n  }\n  // Replace individual state getters/setters with a unified state management\n  get is_loading() {\n    return this.state === "loading";\n  }\n  get is_loaded() {\n    return this.state === "loaded";\n  }\n  get is_unloading() {\n    return this.state === "unloading";\n  }\n  get is_unloaded() {\n    return this.state === "unloaded";\n  }\n};\n\n// ../jsbrains/smart-embed-model/adapters/_adapter.js\nvar SmartEmbedAdapter = class extends SmartModelAdapter {\n  static {\n    __name(this, "SmartEmbedAdapter");\n  }\n  /**\n   * @override in sub-class with adapter-specific default configurations\n   * @property {string} id - The adapter identifier\n   * @property {string} description - Human-readable description\n   * @property {string} type - Adapter type ("API")\n   * @property {string} endpoint - API endpoint\n   * @property {string} adapter - Adapter identifier\n   * @property {string} default_model - Default model to use\n   */\n  static defaults = {};\n  /**\n   * Count tokens in input text\n   * @abstract\n   * @param {string} input - Text to tokenize\n   * @returns {Promise<Object>} Token count result\n   * @property {number} tokens - Number of tokens in input\n   * @throws {Error} If not implemented by subclass\n   */\n  async count_tokens(input) {\n    throw new Error("count_tokens method not implemented");\n  }\n  /**\n   * Generate embeddings for single input\n   * @abstract\n   * @param {string|Object} input - Text to embed\n   * @returns {Promise<Object>} Embedding result\n   * @property {number[]} vec - Embedding vector\n   * @property {number} tokens - Number of tokens in input\n   * @throws {Error} If not implemented by subclass\n   */\n  async embed(input) {\n    if (typeof input === "string") input = { embed_input: input };\n    return (await this.embed_batch([input]))[0];\n  }\n  /**\n   * Generate embeddings for multiple inputs\n   * @abstract\n   * @param {Array<string|Object>} inputs - Texts to embed\n   * @returns {Promise<Array<Object>>} Array of embedding results\n   * @property {number[]} vec - Embedding vector for each input\n   * @property {number} tokens - Number of tokens in each input\n   * @throws {Error} If not implemented by subclass\n   */\n  async embed_batch(inputs) {\n    throw new Error("embed_batch method not implemented");\n  }\n  get settings_config() {\n    return {\n      "[ADAPTER].model_key": {\n        name: "Embedding model",\n        type: "dropdown",\n        description: "Select an embedding model.",\n        options_callback: "adapter.get_models_as_options",\n        callback: "model_changed",\n        default: this.constructor.defaults.default_model\n      }\n    };\n  }\n  get dims() {\n    return this.model.data.dims;\n  }\n  get max_tokens() {\n    return this.model.data.max_tokens;\n  }\n  get batch_size() {\n    return this.model.data.batch_size || 1;\n  }\n};\n\n// ../obsidian-smart-env/src/adapters/embedding-model/transformers_v4.iframe.js\nvar transformers_defaults = {\n  adapter: "transformers",\n  description: "Transformers (Local, built-in)",\n  default_model: "TaylorAI/bge-micro-v2",\n  models: transformers_models\n};\nvar DEVICE_CONFIGS = Object.freeze({\n  webgpu: Object.freeze({\n    device: "webgpu",\n    preferred_dtypes: ["fp32", "fp16", "q8", "q4"]\n  }),\n  cpu: Object.freeze({\n    preferred_dtypes: ["q8", "q4", "fp32", "fp16"]\n  })\n});\nfunction build_device_configs(available_dtypes = [], params = {}) {\n  const {\n    use_gpu = false\n  } = params;\n  const normalized_available_dtypes = new Set(\n    Array.isArray(available_dtypes) ? available_dtypes : []\n  );\n  const configs = [];\n  const push_scope_configs = /* @__PURE__ */ __name((scope_key) => {\n    const scope_config = DEVICE_CONFIGS[scope_key];\n    if (!scope_config) return;\n    scope_config.preferred_dtypes.forEach((dtype) => {\n      if (normalized_available_dtypes.size && !normalized_available_dtypes.has(dtype)) return;\n      configs.push({\n        config_key: `${scope_key}_${dtype}`,\n        ...scope_config.device ? { device: scope_config.device } : {},\n        dtype\n      });\n    });\n  }, "push_scope_configs");\n  if (use_gpu) {\n    push_scope_configs("webgpu");\n  }\n  push_scope_configs("cpu");\n  if (!configs.length) {\n    if (use_gpu) {\n      configs.push({\n        config_key: "webgpu_auto",\n        device: "webgpu"\n      });\n    }\n    configs.push({\n      config_key: "cpu_auto"\n    });\n    return configs;\n  }\n  if (!configs.some(({ config_key }) => config_key === "cpu_auto")) {\n    configs.push({\n      config_key: "cpu_auto"\n    });\n  }\n  return configs;\n}\n__name(build_device_configs, "build_device_configs");\nvar retryable_webgpu_error_code = "WEBGPU_RETRYABLE_ERROR";\nvar retryable_webgpu_error_patterns = [\n  /no available backend found/i,\n  /webgpuinit is not a function/i,\n  /subgroupminsize/i\n];\nfunction get_error_message(error) {\n  return error?.message || String(error || "");\n}\n__name(get_error_message, "get_error_message");\nfunction is_retryable_webgpu_error(error) {\n  const error_message = get_error_message(error);\n  return retryable_webgpu_error_patterns.some((pattern) => pattern.test(error_message));\n}\n__name(is_retryable_webgpu_error, "is_retryable_webgpu_error");\nfunction create_retryable_webgpu_error(error) {\n  const error_message = get_error_message(error);\n  if (error_message.includes(retryable_webgpu_error_code)) {\n    return error instanceof Error ? error : new Error(error_message);\n  }\n  const wrapped_error = new Error(`${retryable_webgpu_error_code}: ${error_message}`);\n  try {\n    wrapped_error.cause = error;\n  } catch (_error) {\n  }\n  return wrapped_error;\n}\n__name(create_retryable_webgpu_error, "create_retryable_webgpu_error");\nfunction should_bubble_webgpu_error(active_config_key, error) {\n  return String(active_config_key || "").includes("webgpu") && is_retryable_webgpu_error(error);\n}\n__name(should_bubble_webgpu_error, "should_bubble_webgpu_error");\nvar is_webgpu_available = /* @__PURE__ */ __name(async () => {\n  if (!("gpu" in navigator)) return false;\n  const adapter = await navigator.gpu.requestAdapter();\n  if (!adapter) return false;\n  return true;\n}, "is_webgpu_available");\nvar SmartEmbedTransformersAdapter = class extends SmartEmbedAdapter {\n  static {\n    __name(this, "SmartEmbedTransformersAdapter");\n  }\n  static defaults = transformers_defaults;\n  /**\n   * @param {import("../smart_embed_model.js").SmartEmbedModel} model\n   */\n  constructor(model2) {\n    super(model2);\n    this.pipeline = null;\n    this.tokenizer = null;\n    this.active_config_key = null;\n    this.has_gpu = false;\n  }\n  /**\n   * Load the underlying transformers pipeline with WebGPU \u2192 WASM fallback.\n   * @returns {Promise<void>}\n   */\n  async load() {\n    this.has_gpu = await is_webgpu_available();\n    try {\n      if (this.loading) {\n        console.warn("[Transformers v4] load already in progress, waiting...");\n        while (this.loading) {\n          await new Promise((resolve) => setTimeout(resolve, 100));\n        }\n      } else {\n        this.loading = true;\n        if (this.pipeline) {\n          this.loaded = true;\n          this.loading = false;\n          return;\n        }\n        await this.load_transformers_with_fallback();\n        this.loading = false;\n        this.loaded = true;\n        console.log(`[Transformers v4] model loaded using ${this.active_config_key}`, this);\n      }\n    } catch (e) {\n      this.loading = false;\n      this.loaded = false;\n      console.error("[Transformers v4] load failed", e);\n      throw e;\n    }\n  }\n  /**\n   * Unload the pipeline and free resources.\n   * @returns {Promise<void>}\n   */\n  async unload() {\n    try {\n      if (this.pipeline) {\n        if (typeof this.pipeline.destroy === "function") {\n          this.pipeline.destroy();\n        } else if (typeof this.pipeline.dispose === "function") {\n          this.pipeline.dispose();\n        }\n      }\n    } catch (err) {\n      console.warn("[Transformers v4] error while disposing pipeline", err);\n    }\n    this.pipeline = null;\n    this.tokenizer = null;\n    this.active_config_key = null;\n    this.loaded = false;\n  }\n  /**\n   * Available models \u2013 reuses the v1 transformers model catalog.\n   * @returns {Object}\n   */\n  get models() {\n    return transformers_models;\n  }\n  /**\n   * Maximum tokens per input.\n   * @returns {number}\n   */\n  get max_tokens() {\n    return this.model.data.max_tokens || 512;\n  }\n  /**\n   * Effective batch size.\n   * Prefers small deterministic batches when not explicitly configured.\n   * @returns {number}\n   */\n  get batch_size() {\n    const configured = this.model.data.batch_size;\n    if (configured && configured > 0) return configured;\n    return this.gpu_enabled ? 16 : 8;\n  }\n  get gpu_enabled() {\n    if (this.has_gpu) {\n      const explicit = typeof this.model.data.use_gpu === "boolean" ? this.model.data.use_gpu : null;\n      if (explicit === false) return false;\n      return true;\n    } else {\n      return false;\n    }\n  }\n  /**\n   * Initialize transformers pipeline with WebGPU \u2192 WASM fallback.\n   * @private\n   * @returns {Promise<void>}\n   */\n  async load_transformers_with_fallback() {\n    const { pipeline, env, AutoTokenizer, ModelRegistry, LogLevel } = await import("https://cdn.jsdelivr.net/npm/@huggingface/transformers@4.1.0");\n    env.logLevel = LogLevel.ERROR;\n    let available_dtypes = [];\n    try {\n      available_dtypes = await ModelRegistry.get_available_dtypes(this.model_key);\n      console.log({ available_dtypes });\n    } catch (error) {\n      console.warn("[Transformers v4] failed to probe available dtypes, falling back to runtime defaults", error);\n    }\n    env.allowLocalModels = false;\n    if (typeof env.useBrowserCache !== "undefined") {\n      env.useBrowserCache = true;\n    }\n    let last_error = null;\n    const config_list = build_device_configs(available_dtypes, {\n      use_gpu: this.gpu_enabled\n    });\n    const try_create = /* @__PURE__ */ __name(async (device_config) => {\n      const pipeline_params = {};\n      if (device_config.device) {\n        pipeline_params.device = device_config.device;\n      }\n      if (device_config.dtype) {\n        pipeline_params.dtype = device_config.dtype;\n      }\n      const pipe = await pipeline("feature-extraction", this.model_key, pipeline_params);\n      return pipe;\n    }, "try_create");\n    for (const device_config of config_list) {\n      const config_key = device_config.config_key;\n      if (this.pipeline) break;\n      try {\n        console.log(`[Transformers v4] trying to load pipeline on ${config_key}`);\n        this.pipeline = await try_create(device_config);\n        this.active_config_key = config_key;\n        break;\n      } catch (err) {\n        console.warn(`[Transformers v4: ${config_key}] failed to load pipeline on ${config_key}`, err);\n        if (device_config.device === "webgpu" && is_retryable_webgpu_error(err)) {\n          throw create_retryable_webgpu_error(err);\n        }\n        last_error = err;\n      }\n    }\n    if (this.pipeline) {\n      console.log(`[Transformers v4: ${this.active_config_key}] pipeline initialized using ${this.active_config_key}`);\n    } else {\n      throw last_error || new Error("Failed to initialize transformers pipeline");\n    }\n    this.tokenizer = await AutoTokenizer.from_pretrained(this.model_key);\n  }\n  /**\n   * Count tokens in input text.\n   * @param {string} input\n   * @returns {Promise<{tokens:number}>}\n   */\n  async count_tokens(input) {\n    if (!this.tokenizer) {\n      await this.load();\n    }\n    const { input_ids } = await this.tokenizer(input);\n    return { tokens: input_ids.data.length };\n  }\n  /**\n   * Generate embeddings for multiple inputs.\n   * @param {Array<Object>} inputs\n   * @returns {Promise<Array<Object>>}\n   */\n  async embed_batch(inputs) {\n    if (!this.pipeline) {\n      await this.load();\n    }\n    const filtered_inputs = inputs.filter((item) => item.embed_input && item.embed_input.length > 0);\n    if (!filtered_inputs.length) return [];\n    const results = [];\n    for (let i = 0; i < filtered_inputs.length; i += this.batch_size) {\n      const batch = filtered_inputs.slice(i, i + this.batch_size);\n      const batch_results = await this._process_batch(batch);\n      results.push(...batch_results);\n    }\n    return results;\n  }\n  /**\n   * Process a single batch \u2013 with per-item retry on failure.\n   * @private\n   * @param {Array<Object>} batch_inputs\n   * @returns {Promise<Array<Object>>}\n   */\n  async _process_batch(batch_inputs) {\n    const prepared = await Promise.all(\n      batch_inputs.map((item) => this._prepare_input(item.embed_input))\n    );\n    const embed_inputs = prepared.map((p) => p.text);\n    const tokens = prepared.map((p) => p.tokens);\n    try {\n      const resp = await this.pipeline(embed_inputs, { pooling: "mean", normalize: true });\n      return batch_inputs.map((item, i) => {\n        const vec = Array.from(resp[i].data).map((val) => Math.round(val * 1e8) / 1e8);\n        item.vec = vec;\n        item.tokens = tokens[i];\n        return item;\n      });\n    } catch (err) {\n      if (should_bubble_webgpu_error(this.active_config_key, err)) {\n        throw create_retryable_webgpu_error(err);\n      }\n      console.error("[Transformers v4] batch embed failed \\u2013 retrying items individually", err);\n      return await this._retry_items_individually(batch_inputs);\n    }\n  }\n  /**\n   * Prepare a single input by truncating to max_tokens if necessary.\n   * @private\n   * @param {string} embed_input\n   * @returns {Promise<{text:string,tokens:number}>}\n   */\n  async _prepare_input(embed_input) {\n    let { tokens } = await this.count_tokens(embed_input);\n    if (tokens <= this.max_tokens) {\n      return { text: embed_input, tokens };\n    }\n    let truncated = embed_input;\n    while (tokens > this.max_tokens && truncated.length > 0) {\n      const pct = this.max_tokens / tokens;\n      const max_chars = Math.floor(truncated.length * pct * 0.9);\n      truncated = truncated.slice(0, max_chars);\n      const last_space = truncated.lastIndexOf(" ");\n      if (last_space > 0) {\n        truncated = truncated.slice(0, last_space);\n      }\n      tokens = (await this.count_tokens(truncated)).tokens;\n    }\n    return { text: truncated, tokens };\n  }\n  /**\n   * Retry each item individually after a batch failure.\n   * @private\n   * @param {Array<Object>} batch_inputs\n   * @returns {Promise<Array<Object>>}\n   */\n  async _retry_items_individually(batch_inputs) {\n    await this._reset_pipeline_after_error();\n    const results = [];\n    for (const item of batch_inputs) {\n      try {\n        const prepared = await this._prepare_input(item.embed_input);\n        const resp = await this.pipeline(prepared.text, { pooling: "mean", normalize: true });\n        const vec = Array.from(resp[0].data).map((val) => Math.round(val * 1e8) / 1e8);\n        results.push({\n          ...item,\n          vec,\n          tokens: prepared.tokens\n        });\n      } catch (single_err) {\n        if (should_bubble_webgpu_error(this.active_config_key, single_err)) {\n          throw create_retryable_webgpu_error(single_err);\n        }\n        console.error("[Transformers v4] single item embed failed \\u2013 skipping", single_err);\n        results.push({\n          ...item,\n          vec: [],\n          tokens: 0,\n          error: single_err.message\n        });\n      }\n    }\n    return results;\n  }\n  /**\n   * Reset pipeline after a failure \u2013 falling back to WASM if needed.\n   * @private\n   * @returns {Promise<void>}\n   */\n  async _reset_pipeline_after_error() {\n    try {\n      if (this.pipeline) {\n        if (typeof this.pipeline.destroy === "function") {\n          this.pipeline.destroy();\n        } else if (typeof this.pipeline.dispose === "function") {\n          this.pipeline.dispose();\n        }\n      }\n    } catch (err) {\n      console.warn("[Transformers v4] error while resetting pipeline", err);\n    }\n    this.pipeline = null;\n    await this.load_transformers_with_fallback();\n  }\n  /**\n   * V2 intentionally exposes only model selection in the settings UI.\n   * @returns {Object}\n   */\n  get settings_config() {\n    return super.settings_config;\n  }\n};\nvar transformers_models = {\n  "TaylorAI/bge-micro-v2": {\n    "id": "TaylorAI/bge-micro-v2",\n    "batch_size": 1,\n    "dims": 384,\n    "max_tokens": 512,\n    "name": "BGE-micro-v2",\n    "description": "Local, 512 tokens, 384 dim (recommended)",\n    "adapter": "transformers"\n  },\n  "Snowflake/snowflake-arctic-embed-xs": {\n    "id": "Snowflake/snowflake-arctic-embed-xs",\n    "batch_size": 1,\n    "dims": 384,\n    "max_tokens": 512,\n    "name": "Snowflake Arctic Embed XS",\n    "description": "Local, 512 tokens, 384 dim",\n    "adapter": "transformers"\n  },\n  "Snowflake/snowflake-arctic-embed-s": {\n    "id": "Snowflake/snowflake-arctic-embed-s",\n    "batch_size": 1,\n    "dims": 384,\n    "max_tokens": 512,\n    "name": "Snowflake Arctic Embed Small",\n    "description": "Local, 512 tokens, 384 dim",\n    "adapter": "transformers"\n  },\n  "Snowflake/snowflake-arctic-embed-m": {\n    "id": "Snowflake/snowflake-arctic-embed-m",\n    "batch_size": 1,\n    "dims": 768,\n    "max_tokens": 512,\n    "name": "Snowflake Arctic Embed Medium",\n    "description": "Local, 512 tokens, 768 dim",\n    "adapter": "transformers"\n  },\n  "TaylorAI/gte-tiny": {\n    "id": "TaylorAI/gte-tiny",\n    "batch_size": 1,\n    "dims": 384,\n    "max_tokens": 512,\n    "name": "GTE-tiny",\n    "description": "Local, 512 tokens, 384 dim",\n    "adapter": "transformers"\n  },\n  "onnx-community/embeddinggemma-300m-ONNX": {\n    "id": "onnx-community/embeddinggemma-300m-ONNX",\n    "batch_size": 1,\n    "dims": 768,\n    "max_tokens": 2048,\n    "name": "EmbeddingGemma-300M",\n    "description": "Local, 2,048 tokens, 768 dim",\n    "adapter": "transformers"\n  },\n  "Mihaiii/Ivysaur": {\n    "id": "Mihaiii/Ivysaur",\n    "batch_size": 1,\n    "dims": 384,\n    "max_tokens": 512,\n    "name": "Ivysaur",\n    "description": "Local, 512 tokens, 384 dim",\n    "adapter": "transformers"\n  },\n  "andersonbcdefg/bge-small-4096": {\n    "id": "andersonbcdefg/bge-small-4096",\n    "batch_size": 1,\n    "dims": 384,\n    "max_tokens": 4096,\n    "name": "BGE-small-4K",\n    "description": "Local, 4,096 tokens, 384 dim",\n    "adapter": "transformers"\n  },\n  // Too slow and persistent crashes\n  // "jinaai/jina-embeddings-v2-base-de": {\n  //   "id": "jinaai/jina-embeddings-v2-base-de",\n  //   "batch_size": 1,\n  //   "dims": 768,\n  //   "max_tokens": 4096,\n  //   "name": "jina-embeddings-v2-base-de",\n  //   "description": "Local, 4,096 tokens, 768 dim, German",\n  //   "adapter": "transformers"\n  // },\n  "Xenova/jina-embeddings-v2-base-zh": {\n    "id": "Xenova/jina-embeddings-v2-base-zh",\n    "batch_size": 1,\n    "dims": 768,\n    "max_tokens": 8192,\n    "name": "Jina-v2-base-zh-8K",\n    "description": "Local, 8,192 tokens, 768 dim, Chinese/English bilingual",\n    "adapter": "transformers"\n  },\n  "Xenova/jina-embeddings-v2-small-en": {\n    "id": "Xenova/jina-embeddings-v2-small-en",\n    "batch_size": 1,\n    "dims": 512,\n    "max_tokens": 8192,\n    "name": "Jina-v2-small-en",\n    "description": "Local, 8,192 tokens, 512 dim",\n    "adapter": "transformers"\n  },\n  "nomic-ai/nomic-embed-text-v1.5": {\n    "id": "nomic-ai/nomic-embed-text-v1.5",\n    "batch_size": 1,\n    "dims": 768,\n    "max_tokens": 2048,\n    "name": "Nomic-embed-text-v1.5",\n    "description": "Local, 8,192 tokens, 768 dim",\n    "adapter": "transformers"\n  },\n  "Xenova/bge-small-en-v1.5": {\n    "id": "Xenova/bge-small-en-v1.5",\n    "batch_size": 1,\n    "dims": 384,\n    "max_tokens": 512,\n    "name": "BGE-small",\n    "description": "Local, 512 tokens, 384 dim",\n    "adapter": "transformers"\n  },\n  "nomic-ai/nomic-embed-text-v1": {\n    "id": "nomic-ai/nomic-embed-text-v1",\n    "batch_size": 1,\n    "dims": 768,\n    "max_tokens": 2048,\n    "name": "Nomic-embed-text",\n    "description": "Local, 2,048 tokens, 768 dim",\n    "adapter": "transformers"\n  }\n};\nvar transformers_settings_config = {\n  // "[ADAPTER].legacy_transformers": {\n  //   name: \'Legacy transformers (no GPU)\',\n  //   type: "toggle",\n  //   description: "Use legacy transformers (v2) instead of v3. This may resolve issues if the local embedding isn\'t working.",\n  //   callback: \'embed_model_changed\',\n  //   default: true,\n  // },\n};\nvar settings_config = {\n  // "legacy_transformers": {\n  //   name: \'Legacy transformers (no GPU)\',\n  //   type: "toggle",\n  //   description: "Use legacy transformers (v2) instead of v3. This may resolve issues if the local embedding isn\'t working.",\n  //   // callback: \'embed_model_changed\',\n  //   // default: false,\n  // },\n};\nvar transformers_v4_iframe_default = {\n  class: SmartEmbedTransformersAdapter,\n  settings_config\n};\nvar model = null;\nasync function process_message(data) {\n  const { method, params, id, iframe_id } = data;\n  try {\n    let result;\n    switch (method) {\n      case "init":\n        console.log("init");\n        break;\n      case "load":\n        const model_params = { data: params, ...params };\n        console.log("load", { model_params });\n        model = new SmartEmbedTransformersAdapter(model_params);\n        await model.load();\n        result = { model_loaded: true, model_config_key: model.active_config_key };\n        break;\n      case "embed_batch":\n        if (!model) throw new Error("Model not loaded");\n        result = await model.embed_batch(params.inputs);\n        break;\n      case "count_tokens":\n        if (!model) throw new Error("Model not loaded");\n        result = await model.count_tokens(params.input);\n        break;\n      default:\n        throw new Error(`Unknown method: ${method}`);\n    }\n    return { id, result, iframe_id };\n  } catch (error) {\n    console.error("Error processing message:", error);\n    return { id, error: get_error_message(error), iframe_id };\n  }\n}\n__name(process_message, "process_message");\nprocess_message({ method: "init" });\nexport {\n  DEVICE_CONFIGS,\n  SmartEmbedTransformersAdapter,\n  transformers_v4_iframe_default as default,\n  settings_config,\n  transformers_defaults,\n  transformers_models,\n  transformers_settings_config\n};\n';

// node_modules/obsidian-smart-env/src/adapters/embedding-model/transformers_v4_iframe.js
var retryable_webgpu_error_patterns = [
  /\bWEBGPU_RETRYABLE_ERROR\b/i,
  /no available backend found/i,
  /webgpuinit is not a function/i,
  /subgroupminsize/i
];
var iframe_timeout_error_code = "IFRAME_REPLY_TIMEOUT";
var iframe_reply_timeout_ms = Object.freeze({
  load: 12e4,
  default: 3e4
});
function is_retryable_webgpu_error(error_message = "") {
  const normalized_error_message = String(error_message || "");
  return retryable_webgpu_error_patterns.some((pattern) => pattern.test(normalized_error_message));
}
function is_iframe_timeout_error(error_message = "") {
  return new RegExp(`\\b${iframe_timeout_error_code}\\b`, "i").test(String(error_message || ""));
}
function create_iframe_timeout_error(method = "") {
  const normalized_method = String(method || "unknown");
  return new Error(`${iframe_timeout_error_code}: No reply from transformers iframe for "${normalized_method}"`);
}
function to_error(error) {
  return error instanceof Error ? error : new Error(String(error || "Unknown error"));
}
var TransformersIframeEmbeddingModelAdapter = class extends SmartEmbedTransformersIframeAdapter {
  constructor(model) {
    super(model);
    const old_connector = this.connector;
    this._old_connector = old_connector;
    this.connector = transformers_v4_iframe_default;
    this._disable_webgpu = false;
    this._reload_in_v4_promise = null;
    this._reload_without_webgpu_promise = null;
    this._reload_with_v3_promise = null;
    this._using_v3_connector = false;
    console.log("transformers iframe connector", this.model);
  }
  get use_gpu() {
    if (this._disable_webgpu) return false;
    if (typeof this.model?.data?.use_gpu === "boolean") {
      return this.model.data.use_gpu;
    }
    return void 0;
  }
  get models() {
    return {
      "TaylorAI/bge-micro-v2": {
        "id": "TaylorAI/bge-micro-v2",
        "batch_size": 1,
        "dims": 384,
        "max_tokens": 512,
        "name": "BGE-micro-v2 (fastest)",
        "description": "Local, 512 tokens, 384 dim (recommended)",
        "adapter": "transformers"
      },
      "Snowflake/snowflake-arctic-embed-xs": {
        "id": "Snowflake/snowflake-arctic-embed-xs",
        "batch_size": 1,
        "dims": 384,
        "max_tokens": 512,
        "name": "Snowflake Arctic Embed XS (fast)",
        "description": "Local, 512 tokens, 384 dim",
        "adapter": "transformers"
      },
      "Xenova/multilingual-e5-small": {
        "id": "Xenova/multilingual-e5-small",
        "batch_size": 1,
        "dims": 384,
        "max_tokens": 512,
        "name": "Multilingual E5 Small",
        "description": "Local, 512 tokens, 384 dim",
        "adapter": "transformers"
      }
    };
  }
  get_message_timeout_ms(method = "") {
    return iframe_reply_timeout_ms[method] || iframe_reply_timeout_ms.default;
  }
  clear_message_timeout(queue_entry = null) {
    if (!queue_entry?.timeout_id) return;
    clearTimeout(queue_entry.timeout_id);
    queue_entry.timeout_id = null;
  }
  /**
   * Store method/params so a recoverable iframe error can be retried transparently.
   * @protected
   * @param {string} method
   * @param {Object} params
   * @returns {Promise<any>}
   */
  async _send_message(method, params, message_options = {}) {
    return new Promise((resolve, reject) => {
      const id = `${this.message_prefix}${this.message_id++}`;
      const queue_entry = {
        resolve,
        reject,
        method,
        params,
        retried_in_v4: Boolean(message_options.retried_in_v4),
        retried_without_webgpu: Boolean(message_options.retried_without_webgpu),
        fell_back_to_v3: Boolean(message_options.fell_back_to_v3),
        timeout_id: null
      };
      queue_entry.timeout_id = setTimeout(() => {
        if (!this.message_queue[id]) return;
        const timeout_error = create_iframe_timeout_error(method);
        this._handle_message_result(id, null, timeout_error.message);
      }, this.get_message_timeout_ms(method));
      this.message_queue[id] = queue_entry;
      try {
        this._post_message({ method, params, id });
      } catch (error) {
        this.clear_message_timeout(queue_entry);
        delete this.message_queue[id];
        reject(to_error(error));
      }
    });
  }
  should_retry_without_webgpu(error, queue_entry = {}) {
    if (this.use_gpu === false) return false;
    if (queue_entry.retried_without_webgpu) return false;
    return is_retryable_webgpu_error(error) || is_iframe_timeout_error(error);
  }
  should_retry_in_v4(error, queue_entry = {}) {
    if (queue_entry.method === "load") return false;
    if (queue_entry.retried_in_v4) return false;
    if (queue_entry.retried_without_webgpu) return false;
    if (this._using_v3_connector) return false;
    if (is_retryable_webgpu_error(error)) return false;
    if (is_iframe_timeout_error(error)) return false;
    return true;
  }
  can_fallback_to_v3(queue_entry = {}) {
    return Boolean(this._old_connector) && !this._using_v3_connector && !queue_entry.fell_back_to_v3;
  }
  should_fallback_to_v3(error, queue_entry = {}) {
    if (!this.can_fallback_to_v3(queue_entry)) return false;
    return Boolean(
      queue_entry.method === "load" || queue_entry.retried_in_v4 || queue_entry.retried_without_webgpu || this._disable_webgpu || is_iframe_timeout_error(error)
    );
  }
  async reload_iframe_in_v4() {
    if (this._reload_in_v4_promise) {
      return this._reload_in_v4_promise;
    }
    this.state = "loading";
    this.model.model_loaded = false;
    this.model.load_result = null;
    this._reload_in_v4_promise = this.load().finally(() => {
      this._reload_in_v4_promise = null;
    });
    return this._reload_in_v4_promise;
  }
  async reload_iframe_without_webgpu() {
    if (this._reload_without_webgpu_promise) {
      return this._reload_without_webgpu_promise;
    }
    this._disable_webgpu = true;
    this.state = "loading";
    this.model.model_loaded = false;
    this.model.load_result = null;
    this._reload_without_webgpu_promise = this.load().finally(() => {
      this._reload_without_webgpu_promise = null;
    });
    return this._reload_without_webgpu_promise;
  }
  async reload_iframe_with_v3() {
    if (this._reload_with_v3_promise) {
      return this._reload_with_v3_promise;
    }
    if (!this._old_connector) {
      throw new Error("Missing transformers v3 iframe connector");
    }
    this._using_v3_connector = true;
    this.connector = this._old_connector;
    this.state = "loading";
    this.model.model_loaded = false;
    this.model.load_result = null;
    this._reload_with_v3_promise = this.load().finally(() => {
      this._reload_with_v3_promise = null;
    });
    return this._reload_with_v3_promise;
  }
  async retry_message_in_v4(queue_entry) {
    await this.reload_iframe_in_v4();
    return await this._send_message(queue_entry.method, queue_entry.params, {
      retried_in_v4: true,
      retried_without_webgpu: queue_entry.retried_without_webgpu,
      fell_back_to_v3: queue_entry.fell_back_to_v3
    });
  }
  async retry_message_without_webgpu(queue_entry) {
    await this.reload_iframe_without_webgpu();
    if (queue_entry.method === "load") {
      return this.model.load_result || { model_loaded: true, webgpu_disabled: true };
    }
    return await this._send_message(queue_entry.method, queue_entry.params, {
      retried_in_v4: queue_entry.retried_in_v4,
      retried_without_webgpu: true,
      fell_back_to_v3: queue_entry.fell_back_to_v3
    });
  }
  async fallback_to_v3_and_retry(queue_entry) {
    queue_entry.fell_back_to_v3 = true;
    await this.reload_iframe_with_v3();
    if (queue_entry.method === "load") {
      return this.model.load_result || { model_loaded: true, v3_fallback: true };
    }
    return await this._send_message(queue_entry.method, queue_entry.params, {
      retried_in_v4: queue_entry.retried_in_v4,
      retried_without_webgpu: queue_entry.retried_without_webgpu,
      fell_back_to_v3: true
    });
  }
  /**
   * Handle response message from worker/iframe
   * ADDS WEBGPU-SPECIFIC RETRY BEFORE FALLBACK TO OLD CONNECTOR (v3.8.0)
   * @protected
   * @param {string} id - Message ID
   * @param {*} result - Response result
   * @param {Error} [error] - Response error
   */
  _handle_message_result(id, result, error) {
    if (!id.startsWith(this.message_prefix)) return;
    if (result?.model_loaded) {
      console.log("model loaded");
      this.state = "loaded";
      this.model.model_loaded = true;
      this.model.load_result = result;
    }
    const queue_entry = this.message_queue[id];
    if (!queue_entry) return;
    this.clear_message_timeout(queue_entry);
    if (error) {
      if (this.should_retry_without_webgpu(error, queue_entry)) {
        queue_entry.retried_without_webgpu = true;
        delete this.message_queue[id];
        console.warn("Retrying transformers v4 iframe without WebGPU due to recoverable error:", error);
        this.retry_message_without_webgpu(queue_entry).then((retry_result) => {
          queue_entry.resolve(retry_result);
        }).catch((retry_error) => {
          if (this.should_fallback_to_v3(retry_error, queue_entry)) {
            console.warn("Falling back to transformers v3 iframe connector after v4 CPU retry failed:", retry_error);
            this.fallback_to_v3_and_retry(queue_entry).then((fallback_result) => {
              queue_entry.resolve(fallback_result);
            }).catch((fallback_error) => {
              queue_entry.reject(to_error(fallback_error));
            });
            return;
          }
          queue_entry.reject(to_error(retry_error));
        });
        return;
      }
      if (this.should_retry_in_v4(error, queue_entry)) {
        queue_entry.retried_in_v4 = true;
        delete this.message_queue[id];
        console.warn("Retrying transformers v4 iframe after hard non-load error:", error);
        this.retry_message_in_v4(queue_entry).then((retry_result) => {
          queue_entry.resolve(retry_result);
        }).catch((retry_error) => {
          if (this.should_fallback_to_v3(retry_error, queue_entry)) {
            console.warn("Falling back to transformers v3 iframe connector after bounded v4 retry failed:", retry_error);
            this.fallback_to_v3_and_retry(queue_entry).then((fallback_result) => {
              queue_entry.resolve(fallback_result);
            }).catch((fallback_error) => {
              queue_entry.reject(to_error(fallback_error));
            });
            return;
          }
          queue_entry.reject(to_error(retry_error));
        });
        return;
      }
      if (this.should_fallback_to_v3(error, queue_entry)) {
        delete this.message_queue[id];
        console.warn("Falling back to transformers v3 iframe connector due to error:", error);
        this.fallback_to_v3_and_retry(queue_entry).then((fallback_result) => {
          queue_entry.resolve(fallback_result);
        }).catch((fallback_error) => {
          queue_entry.reject(to_error(fallback_error));
        });
        return;
      }
      queue_entry.reject(to_error(error));
      delete this.message_queue[id];
      return;
    }
    queue_entry.resolve(result);
    delete this.message_queue[id];
  }
};
var transformers_v4_iframe_default2 = {
  class: TransformersIframeEmbeddingModelAdapter,
  settings_config: settings_config3
};

// node_modules/obsidian-smart-env/src/collections/embedding_models.js
embedding_models_default.providers = {
  transformers: transformers_v4_iframe_default2
};
var embedding_models_default2 = embedding_models_default;

// node_modules/obsidian-smart-env/src/items/lookup_list.js
var LookupList = class extends CollectionItem {
  static key = "lookup_list";
  static get defaults() {
    return { data: {} };
  }
  async pre_process(params) {
    if (typeof this.actions.lookup_list_pre_process === "function") {
      await this.actions.lookup_list_pre_process(params);
    }
  }
  async get_results(params = {}) {
    await this.pre_process(params);
    if (this.env.log_perf) this.start_ms = Date.now();
    let results = this.filter_and_score(params);
    if (this.env.log_perf) {
      this.end_ms = Date.now();
      console.log(`filter_and_score(${params.score_algo_key}) took ${this.end_ms - this.start_ms} ms (Date.now)`);
    }
    if (this.should_post_process) results = await this.post_process(results, params);
    this.emit_event("lookup:get_results");
    return results;
  }
  filter_and_score(params = {}) {
    const collection = this.env[params.results_collection_key] || this.env[this.collection.results_collection_key];
    const score_errors = [];
    const { results: raw_results } = Object.values(collection.items).reduce((acc, target) => {
      const scored = target.filter_and_score(params);
      if (!scored?.score) {
        if (scored?.error) score_errors.push(scored.error);
        return acc;
      }
      results_acc(acc, scored, params.limit || 20);
      return acc;
    }, { min: 0, results: /* @__PURE__ */ new Set() });
    const results = Array.from(raw_results).sort(sort_by_score_descending);
    if (!results.length) return results;
    while (!results.some((r) => r.score > 0.5)) {
      results.forEach((r) => r.score *= 2);
    }
    return results;
  }
  async post_process(results, params = {}) {
    return results;
  }
  get should_post_process() {
    return this.settings.lookup_post_process && this.settings.lookup_post_process !== "none";
  }
  // for compatibility with v3 connections list item
  get item() {
    return this;
  }
};

// node_modules/obsidian-smart-env/src/collections/lookup_lists.js
var settings_config6 = {
  results_collection_key: {
    name: "Lookup results type",
    type: "dropdown",
    description: "Choose whether results should be sources or blocks.",
    option_1: "smart_sources|Sources",
    option_2: "smart_blocks|Blocks",
    options_callback: (scope) => {
      const options = [
        { value: "smart_sources", name: "Sources" }
      ];
      if (scope.env.smart_blocks) {
        options.push({ value: "smart_blocks", name: "Blocks" });
      }
      return options;
    }
  }
};
var LookupLists = class extends Collection {
  static get default_settings() {
    return {
      results_collection_key: "smart_blocks",
      score_algo_key: "similarity",
      results_limit: 20
    };
  }
  static version = 0.01;
  new_item({ query, filter }) {
    if (!query || typeof query !== "string" || !query.trim()) {
      throw new Error("LookupLists.new_item requires a non-empty query string.");
    }
    const date = format_ymd(/* @__PURE__ */ new Date());
    const hash = murmur_hash_32_alphanumeric(query);
    const key = `${date}+${hash}`;
    if (this.items[key]) return this.items[key];
    const list = new this.item_type(this.env, {
      key,
      query,
      filter
    });
    this.set(list);
    return list;
  }
  get settings_config() {
    return { ...settings_config6 };
  }
  process_load_queue() {
  }
  get results_collection_key() {
    const stored_key = this.settings?.results_collection_key;
    if (this.env.collections?.[stored_key]) return stored_key;
    return "smart_sources";
  }
};
function format_ymd(d) {
  const y = d.getFullYear();
  const m = String(d.getMonth() + 1).padStart(2, "0");
  const day = String(d.getDate()).padStart(2, "0");
  return `${y}-${m}-${day}`;
}
var lookup_lists_default = {
  class: LookupLists,
  collection_key: "lookup_lists",
  item_type: LookupList,
  settings_config: settings_config6
};

// node_modules/obsidian-smart-env/node_modules/smart-blocks/utils/get_block_display_name.js
function get_block_display_name(key, show_full_path) {
  const [source_key, ...path_parts] = key.split("#").filter(Boolean);
  const source_name = get_item_display_name(source_key, show_full_path);
  if (show_full_path) return [source_name, ...path_parts].join(" > ");
  const last_heading = path_parts.findLast((part) => part && part[0] !== "{");
  return [source_name, last_heading].join(" > ");
}

// node_modules/obsidian-smart-env/node_modules/smart-blocks/smart_block.js
var SmartBlock = class extends SmartEntity {
  /**
   * Provides default values for a SmartBlock instance.
   * @static
   * @readonly
   * @returns {Object} The default values.
   */
  static get defaults() {
    return {
      data: {
        text: null,
        length: 0,
        last_read: {
          hash: null,
          at: 0
        }
      },
      _embed_input: ""
      // Stored temporarily
    };
  }
  get block_adapter() {
    if (!this._block_adapter) {
      this._block_adapter = new this.collection.opts.block_adapters.md(this);
    }
    return this._block_adapter;
  }
  /**
   * Initializes the SmartBlock instance by queuing an embed if embedding is enabled.
   * @returns {void}
   */
  init() {
    if (this.settings.embed_blocks) super.init();
  }
  /**
   * Queues the entity for embedding.
   * @returns {void}
   */
  queue_embed() {
    this._queue_embed = this.should_embed;
  }
  /**
   * Queues the block for import via the source.
   * @returns {void}
   */
  queue_import() {
    this.source?.queue_import();
  }
  /**
   * Prepares the embed input for the SmartBlock by reading content and generating a hash.
   * @async
   * @returns {Promise<string|false>} The embed input string or `false` if already embedded.
   */
  async get_embed_input(content = null) {
    if (typeof this._embed_input !== "string" || !this._embed_input.length) {
      if (!content) content = await this.read();
      this._embed_input = this.breadcrumbs + "\n" + content;
    }
    return this._embed_input;
  }
  // CRUD
  /**
   * @method read
   * @description Reads the block content by delegating to the block adapter.
   * @async
   * @returns {Promise<string>} The block content.
   */
  async read() {
    try {
      return await this.block_adapter.read();
    } catch (e) {
      if (e.message.includes("BLOCK NOT FOUND")) {
        return 'BLOCK NOT FOUND (run "Prune" to remove)';
      } else {
        throw e;
      }
    }
  }
  /**
   * Filters block using base key filters and optional source frontmatter include/exclude filters.
   * @param {Object} [filter_opts={}]
   * @param {Object} [filter_opts.frontmatter]
   * @returns {boolean}
   */
  filter(filter_opts = {}) {
    if (!super.filter(filter_opts)) return false;
    if (!filter_opts.frontmatter) return true;
    return filter_by_frontmatter(this.source?.metadata || {}, filter_opts.frontmatter);
  }
  /**
   * @method append
   * @description Appends content to this block by delegating to the block adapter.
   * @async
   * @param {string} content
   * @returns {Promise<void>}
   */
  async append(content) {
    await this.block_adapter.append(content);
    this.queue_save();
  }
  /**
   * @method update
   * @description Updates the block content by delegating to the block adapter.
   * @async
   * @param {string} new_block_content
   * @param {Object} [opts={}]
   * @returns {Promise<void>}
   */
  async update(new_block_content, opts = {}) {
    await this.block_adapter.update(new_block_content, opts);
    this.queue_save();
  }
  /**
   * @method remove
   * @description Removes the block by delegating to the block adapter.
   * @async
   * @returns {Promise<void>}
   */
  async remove() {
    await this.block_adapter.remove();
    this.queue_save();
  }
  /**
   * @method move_to
   * @description Moves the block to another location by delegating to the block adapter.
   * @async
   * @param {string} to_key
   * @returns {Promise<void>}
   */
  async move_to(to_key) {
    await this.block_adapter.move_to(to_key);
    this.queue_save();
  }
  get_display_name(params = {}) {
    return this.block_adapter?.get_display_name(params);
  }
  // Getters
  /**
   * Retrieves the breadcrumbs representing the block's path within the source.
   * @readonly
   * @returns {string} The breadcrumbs string.
   */
  get breadcrumbs() {
    return this.key.split("/").join(" > ").split("#").slice(0, -1).join(" > ").replace(".md", "");
  }
  /**
   * Determines if the block is excluded from embedding based on headings.
   * @readonly
   * @returns {boolean} `true` if excluded, `false` otherwise.
   */
  get excluded() {
    const block_headings = this.path.split("#").slice(1);
    if (this.source_collection.excluded_headings.some((heading) => block_headings.includes(heading))) return true;
    return this.source?.excluded;
  }
  /**
   * Retrieves the file path of the SmartSource associated with the block.
   * @readonly
   * @returns {string} The file path.
   */
  get file_path() {
    return this.source?.file_path;
  }
  /**
   * Retrieves the file type of the SmartSource associated with the block.
   * @readonly
   * @returns {string} The file type.
   */
  get file_type() {
    return this.source.file_type;
  }
  /**
   * Retrieves the folder path of the block.
   * @readonly
   * @returns {string} The folder path.
   */
  get folder() {
    return this.path.split("/").slice(0, -1).join("/");
  }
  /**
   * Retrieves the embed link for the block.
   * @readonly
   * @returns {string} The embed link.
   */
  get embed_link() {
    return `![[${this.link}]]`;
  }
  /**
   * Determines if the block has valid line range information.
   * @readonly
   * @returns {boolean} `true` if the block has both start and end lines, `false` otherwise.
   */
  get has_lines() {
    return this.lines && this.lines.length === 2;
  }
  /**
   * Determines if the entity is a block based on its key.
   * @readonly
   * @returns {boolean} `true` if it's a block, `false` otherwise.
   */
  get is_block() {
    return this.key.includes("#");
  }
  /**
   * Determines if the block is gone (i.e., the source file or block data no longer exists).
   * @readonly
   * @returns {boolean} `true` if gone, `false` otherwise.
   */
  get is_gone() {
    if (!this.source?.file) return true;
    if (!this.source?.data?.blocks?.[this.sub_key]) return true;
    return false;
  }
  get last_read() {
    return this.data.last_read;
  }
  /**
   * Retrieves the sub-key of the block.
   * @readonly
   * @returns {string} The sub-key.
   */
  get sub_key() {
    return "#" + this.key.split("#").slice(1).join("#");
  }
  /**
   * Retrieves the lines range of the block.
   * @readonly
   * @returns {Array<number>|undefined} An array containing the start and end lines or `undefined` if not set.
   */
  // get lines() { return this.source?.data?.blocks?.[this.sub_key]; }
  get lines() {
    return this.data.lines;
  }
  /**
   * Retrieves the starting line number of the block.
   * @readonly
   * @returns {number|undefined} The starting line number or `undefined` if not set.
   */
  get line_start() {
    return this.lines?.[0];
  }
  /**
   * Retrieves the ending line number of the block.
   * @readonly
   * @returns {number|undefined} The ending line number or `undefined` if not set.
   */
  get line_end() {
    return this.lines?.[1];
  }
  /**
   * Retrieves the link associated with the block, handling page numbers if present.
   * @readonly
   * @deprecated was specific to PDFs and removed this sort of PDF handling
   * @returns {string} The block link.
   */
  get link() {
    if (/^.*page\s*(\d+).*$/i.test(this.sub_key)) {
      const number = this.sub_key.match(/^.*page\s*(\d+).*$/i)[1];
      return `${this.source.path}#page=${number}`;
    } else {
      return this.source?.path || "MISSING SOURCE";
    }
  }
  // uses data.lines to get next block
  get next_block() {
    if (!this.data.lines) return null;
    const next_line = this.data.lines[1] + 1;
    return this.source.blocks?.find((block) => next_line === block.data?.lines?.[0]);
  }
  /**
   * Retrieves the paths of outlinks from the block.
   * @readonly
   * @returns {Array<string>} An array of outlink paths.
   */
  get outlinks() {
    return this.source.outlinks;
  }
  /**
   * Retrieves the path of the SmartBlock.
   * @readonly
   * @returns {string} The path of the SmartBlock.
   */
  get path() {
    return this.key;
  }
  /**
   * Determines if the block should be embedded based on its coverage and size.
   * @readonly
   * @returns {boolean} `true` if it should be embedded, `false` otherwise.
   */
  get should_embed() {
    try {
      const source_hash = this.source?.data?.last_read?.hash;
      const cached_should_embed = this._should_embed_cache;
      if (source_hash != null && cached_should_embed?.hash === source_hash) {
        return cached_should_embed.value;
      }
      const min_chars = this.settings?.min_chars;
      let should_embed = true;
      if (min_chars && this.size < min_chars) {
        should_embed = false;
      } else {
        const match_line_start = this.line_start + 1;
        const match_line_end = this.line_end;
        const blocks = this.source?.data?.blocks;
        const prefix = this.sub_key + "#";
        let has_line_start = null;
        let has_line_end = null;
        if (blocks) {
          for (const key in blocks) {
            if (!Object.prototype.hasOwnProperty.call(blocks, key)) continue;
            if (!key.startsWith(prefix)) continue;
            const range = blocks[key];
            if (range[0] === match_line_start) has_line_start = key;
            if (range[1] === match_line_end) has_line_end = key;
            if (has_line_start && has_line_end) break;
          }
        }
        if (has_line_start && has_line_end) {
          const start_block = this.collection.get(this.source_key + has_line_start);
          if (start_block?.should_embed) {
            const end_block = this.collection.get(this.source_key + has_line_end);
            if (end_block?.should_embed) should_embed = false;
          }
        }
      }
      if (source_hash != null) {
        this._should_embed_cache = {
          hash: source_hash,
          value: should_embed
        };
      }
      return should_embed;
    } catch (e) {
      console.error(e, e.stack);
      console.error(`Error getting should_embed for ${this.key}: ` + JSON.stringify(e || {}, null, 2));
    }
  }
  /**
   * Retrieves the size of the SmartBlock.
   * @readonly
   * @returns {number} The size of the SmartBlock.
   */
  get size() {
    return this.data.size;
  }
  /**
   * Retrieves the SmartSource associated with the block.
   * @readonly
   * @returns {import("smart-sources").SmartSource} The associated SmartSource instance.
   */
  get source() {
    return this.source_collection.get(this.source_key);
  }
  /**
   * Retrieves the SmartSources collection instance.
   * @readonly
   * @returns {import("smart-sources").SmartSources} The SmartSources collection.
   */
  get source_collection() {
    return this.env.smart_sources;
  }
  get source_key() {
    return this.key.split("#")[0];
  }
  get sub_blocks() {
    return this.source?.blocks?.filter((block) => block.key.startsWith(this.key + "#") && block.line_start > this.line_start && block.line_end <= this.line_end) || [];
  }
  // source dependent
  get excluded_lines() {
    return this.source.excluded_lines;
  }
  get file() {
    return this.source.file;
  }
  get is_media() {
    return this.source.is_media;
  }
  get mtime() {
    return this.source.mtime;
  }
  // DEPRECATED
  /**
   * Retrieves the display name of the block.
   * @readonly
   * @deprecated Use `get_item_display_name()` as platform-specific util. Move to sub-class in obsidian-smart-env to use respective util and settings (2026-03-27).
   * @returns {string} The display name.
   */
  get name() {
    return get_block_display_name(
      this.key,
      this.env.settings.smart_view_filter?.show_full_path
      // This probably does nothing (2026-03-27): see updated settings
    );
  }
  /**
   * @deprecated Use `source` instead. Removing after 2025-09-01.
   * @readonly
   * @returns {SmartSource} The associated SmartSource instance.
   */
  get note() {
    return this.source;
  }
  /**
   * @deprecated Use `source.key` instead. Removing after 2025-09-01.
   * @readonly
   * @returns {string} The source key.
   */
  get note_key() {
    return this.key.split("#")[0];
  }
};

// node_modules/obsidian-smart-env/src/utils/get_block_display_name.js
function get_block_display_name2(item, settings = {}) {
  if (!item?.key) return "";
  const show_full_path = settings.show_full_path ?? true;
  if (show_full_path) {
    return item.key.replace(/#/g, " > ").replace(/\//g, " > ");
  }
  const pcs = [];
  const [source_key, ...block_parts] = item.key.split("#");
  const filename = source_key.split("/").pop();
  pcs.push(filename);
  if (block_parts.length) {
    const last = block_parts[block_parts.length - 1];
    if (last.startsWith("{") && last.endsWith("}")) {
      block_parts.pop();
      pcs.push(block_parts.pop());
      if (item.lines) pcs.push(`Lines: ${item.lines.join("-")}`);
    } else {
      pcs.push(block_parts.pop());
    }
  }
  return pcs.filter(Boolean).join(" > ");
}

// node_modules/obsidian-smart-env/src/items/smart_block.js
var SmartBlock2 = class extends SmartBlock {
  /**
   * @param {Object} params - Parameters for display settings
   * @param {boolean} params.show_full_path
   */
  get_display_name(params = {}) {
    const display_settings = {
      ...this.env?.settings?.smart_view_filter || {},
      // DEPRECATED? settings scope
      ...params
    };
    return get_block_display_name2(this, display_settings);
  }
  // DEPRECATED
  /**
   * @deprecated avoid view-logic in Collection/Item AND prefix display_ where used anyway
   */
  get name() {
    return this.get_display_name();
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-blocks/smart_blocks.js
var SmartBlocks = class extends SmartEntities {
  /**
   * Initializes the SmartBlocks instance. Currently muted as processing is handled by SmartSources.
   * @returns {void}
   */
  init() {
  }
  get fs() {
    return this.env.smart_sources.fs;
  }
  /**
   * Retrieves the embedding model associated with the SmartSources collection.
   * @readonly
   * @returns {Object|undefined} The embedding model instance or `undefined` if not set.
   */
  get embed_model() {
    return this.source_collection?.embed_model;
  }
  /**
   * Retrieves the embedding model key from the SmartSources collection.
   * @readonly
   * @returns {string|undefined} The embedding model key or `undefined` if not set.
   */
  get embed_model_key() {
    return this.source_collection?.embed_model_key;
  }
  /**
   * Calculates the expected number of blocks based on the SmartSources collection.
   * @readonly
   * @returns {number} The expected count of blocks.
   */
  get expected_blocks_ct() {
    return Object.values(this.source_collection.items).reduce((acc, item) => acc += Object.keys(item.data.blocks || {}).length, 0);
  }
  /**
   * Retrieves the notices system from the environment.
   * @readonly
   * @returns {Object} The notices object.
   */
  get notices() {
    return this.env.smart_connections_plugin?.notices || this.env.main?.notices;
  }
  /**
   * Retrieves the settings configuration for SmartBlocks.
   * @readonly
   * @returns {Object} The settings configuration object.
   */
  get settings_config() {
    return this.process_settings_config({
      "embed_blocks": {
        name: "Embed blocks",
        type: "toggle",
        description: "Blocks represent parts/sections of notes. Get more granular results.",
        default: true
      },
      ...super.settings_config
    });
  }
  get data_dir() {
    return "multi";
  }
  /**
   * Retrieves the SmartSources collection instance.
   * @readonly
   * @returns {SmartSources} The SmartSources collection.
   */
  get source_collection() {
    return this.env.smart_sources;
  }
  /**
   * Processes the embed queue. Currently handled by SmartSources, so this method is muted.
   * @async
   * @returns {Promise<void>}
   */
  async process_embed_queue() {
  }
  /**
   * Processes the load queue. Currently muted as processing is handled by SmartSources.
   * @async
   * @returns {Promise<void>}
   */
  async process_load_queue() {
  }
  // TEMP: Methods in sources not implemented in blocks
  /**
   * @async
   * @abstract
   * @throws {Error} Throws an error indicating the method is not implemented.
   * @returns {Promise<void>}
   */
  async prune() {
    throw "Not implemented: prune";
  }
  /**
   * @throws {Error} Throws an error indicating the method is not implemented.
   * @abstract
   * @returns {void}
   */
  build_links_map() {
    throw "Not implemented: build_links_map";
  }
  /**
   * @async
   * @abstract
   * @throws {Error} Throws an error indicating the method is not implemented.
   * @returns {Promise<void>}
   */
  async refresh() {
    throw "Not implemented: refresh";
  }
  /**
   * @async
   * @abstract
   * @throws {Error} Throws an error indicating the method is not implemented.
   * @returns {Promise<void>}
   */
  async search() {
    throw "Not implemented: search";
  }
  /**
   * @async
   * @abstract
   * @throws {Error} Throws an error indicating the method is not implemented.
   * @returns {Promise<void>}
   */
  async run_refresh() {
    throw "Not implemented: run_refresh";
  }
  /**
   * @async
   * @abstract
   * @throws {Error} Throws an error indicating the method is not implemented.
   * @returns {Promise<void>}
   */
  async run_force_refresh() {
    throw "Not implemented: run_force_refresh";
  }
  // clear expired blocks
  // TODO/future: replaced by storing block data within source data
  async cleanup_blocks() {
    const expired_blocks = Object.values(this.items).filter((i) => i.is_gone);
    console.log(`Removing ${expired_blocks.length} expired blocks`);
    expired_blocks.forEach((i) => i.delete());
    await this.process_save_queue();
    expired_blocks.forEach((i) => {
      delete this.items[i.key];
    });
    this.emit_event("blocks:cleaned", { expired_blocks_ct: expired_blocks.length });
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-blocks/adapters/data/ajson_multi_file.js
var AjsonMultiFileBlocksDataAdapter = class extends AjsonMultiFileCollectionDataAdapter {
  ItemDataAdapter = AjsonMultiFileBlockDataAdapter;
  /**
   * Transforms the item key into a safe filename.
   * Replaces spaces, slashes, and dots with underscores.
   * @returns {string} safe file name
   */
  // get_data_file_name(key) {
  //   return super.get_data_file_name(key.split('#')[0]);
  // }
  get_data_file_name(key) {
    return key.split("#")[0].replace(/[\s\/\.]/g, "_").replace(".md", "");
  }
  /**
   * Process any queued save operations.
   * @async
   * @returns {Promise<void>}
   */
  async process_save_queue() {
    this.collection.emit_event("collection:save_started");
    const save_queue = Object.values(this.collection.items).filter((item) => item._queue_save);
    console.log(`Saving ${this.collection.collection_key}: ${save_queue.length} items`);
    const time_start = Date.now();
    const save_files = Object.entries(save_queue.reduce((acc, item) => {
      const file_name = this.get_item_data_path(item.key);
      acc[file_name] = acc[file_name] || [];
      acc[file_name].push(item);
      return acc;
    }, {}));
    for (let i = 0; i < save_files.length; i++) {
      const [file_name, items] = save_files[i];
      await this.fs.append(
        file_name,
        items.map((item) => this.get_item_ajson(item)).join("\n") + "\n"
      );
      items.forEach((item) => item._queue_save = false);
    }
    console.log(`Saved ${this.collection.collection_key} in ${Date.now() - time_start}ms`);
    this.collection.emit_event("collection:save_completed");
  }
  process_load_queue() {
    console.log(`Skipping loading ${this.collection.collection_key}...`);
  }
};
var AjsonMultiFileBlockDataAdapter = class extends AjsonMultiFileItemDataAdapter {
};

// node_modules/obsidian-smart-env/node_modules/smart-blocks/adapters/_adapter.js
var BlockContentAdapter = class {
  /**
   * @constructor
   * @param {import('smart-blocks').SmartBlock} item - The SmartBlock instance this adapter operates on.
   * The `item` should at least provide `data` and references to its parent source.
   */
  constructor(item) {
    this.item = item;
  }
  /**
   * @async
   * @method read
   * @abstract
   * @returns {Promise<string>} The content of the block.
   * @throws {Error} If not implemented by subclass.
   */
  async read() {
    throw new Error("Not implemented");
  }
  /**
   * @async
   * @method append
   * @abstract
   * @param {string} content Content to append to the block.
   * @returns {Promise<void>}
   * @throws {Error} If not implemented by subclass.
   */
  async append(content) {
    throw new Error("Not implemented");
  }
  /**
   * @async
   * @method update
   * @abstract
   * @param {string} new_content The new content for the block.
   * @param {Object} [opts={}] Additional update options.
   * @returns {Promise<void>}
   * @throws {Error} If not implemented by subclass.
   */
  async update(new_content, opts = {}) {
    throw new Error("Not implemented");
  }
  /**
   * @async
   * @method remove
   * @abstract
   * @returns {Promise<void>}
   * @throws {Error} If not implemented by subclass.
   */
  async remove() {
    throw new Error("Not implemented");
  }
  /**
   * @async
   * @method move_to
   * @abstract
   * @param {string} to_key The destination key (source or block reference).
   * @returns {Promise<void>}
   * @throws {Error} If not implemented by subclass.
   */
  async move_to(to_key) {
    throw new Error("Not implemented");
  }
  /**
   * @method get_display_name
   * @abstract
   * @param {Object} params Parameters for display name generation.
   * @returns {string} The display name of the block.
   * @throws {Error} If not implemented by subclass.
   */
  get_display_name(params) {
    throw new Error("Not implemented");
  }
  /**
   * @name data
   * @type {Object}
   * @readonly
   * @description Access the block’s data object. Useful for updating metadata like line references or hashes.
   */
  get data() {
    return this.item.data;
  }
  /**
   * @async
   * @method update_last_read
   * @param {string} content The current content of the block.
   * @returns {Promise<void>}
   * @description Update the block’s `last_read` hash and timestamp based on the given content.
   */
  async update_last_read(content) {
    this.data.last_read = {
      hash: this.create_hash(content),
      at: Date.now()
    };
  }
  /**
   * @method create_hash
   * @param {string} content The content to hash.
   * @returns {Promise<string>} The computed hash of the content.
   * @description Hash the block content to detect changes and prevent unnecessary re-embeddings.
   */
  create_hash(content) {
    return murmur_hash_32_alphanumeric(content);
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-blocks/adapters/markdown_block.js
var MarkdownBlockContentAdapter = class extends BlockContentAdapter {
  /**
   * Read the content of the block.
   * @async
   * @returns {Promise<string>} The block content as a string.
   * @throws {Error} If the block cannot be found.
   */
  async read() {
    const source_content = await this.item.source?.read();
    if (!source_content) {
      console.warn(`BLOCK NOT FOUND: ${this.item.key} has no source content.`);
      return "";
    }
    const content = this._extract_block(source_content);
    this.update_last_read(content);
    return content;
  }
  /**
   * Append content to the existing block.
   * This method inserts additional lines after the block's end, then re-parses the file to update line references.
   * @async
   * @param {string} content Content to append to the block.
   * @returns {Promise<void>}
   * @throws {Error} If the block cannot be found.
   */
  async append(content) {
    let full_content = await this.item.source.read();
    const { line_start, line_end } = this.item;
    if (!line_start || !line_end) {
      throw new Error(`Cannot append to block ${this.item.key}: invalid line references.`);
    }
    const lines = full_content.split("\n");
    lines.splice(line_end, 0, "", content);
    const updated_content = lines.join("\n");
    await this.item.source._update(updated_content);
    await this._reparse_source();
  }
  /**
   * Update the block with new content, replacing its current lines.
   * @async
   * @param {string} new_content New content for the block.
   * @param {Object} [opts={}] Additional options.
   * @returns {Promise<void>}
   * @throws {Error} If the block cannot be found.
   */
  async update(new_content, opts = {}) {
    let full_content = await this.item.source.read();
    const { line_start, line_end } = this.item;
    if (!line_start || !line_end) {
      throw new Error(`Cannot update block ${this.item.key}: invalid line references.`);
    }
    const lines = full_content.split("\n");
    const updated_lines = [
      ...lines.slice(0, line_start - 1),
      ...new_content.split("\n"),
      ...lines.slice(line_end)
    ];
    const updated_content = updated_lines.join("\n");
    await this.item.source._update(updated_content);
    await this._reparse_source();
  }
  /**
   * Remove the block entirely from the source.
   * @async
   * @returns {Promise<void>}
   * @throws {Error} If the block cannot be found.
   */
  async remove() {
    let full_content = await this.item.source.read();
    const { line_start, line_end } = this.item;
    if (!line_start || !line_end) {
      throw new Error(`Cannot remove block ${this.item.key}: invalid line references.`);
    }
    const lines = full_content.split("\n");
    const updated_lines = [
      ...lines.slice(0, line_start - 1),
      ...lines.slice(line_end)
    ];
    const updated_content = updated_lines.join("\n");
    await this.item.source._update(updated_content);
    await this._reparse_source();
  }
  /**
   * Move the block to a new location (another source or heading).
   * This involves reading the block content, removing it from the current source, and appending it to the target.
   * @async
   * @param {string} to_key The destination path or entity reference.
   * @returns {Promise<void>}
   * @throws {Error} If the block or target is invalid.
   */
  async move_to(to_key) {
    const content = await this.read();
    await this.remove();
    const is_block_ref = to_key.includes("#");
    let target_source_key = is_block_ref ? to_key.split("#")[0] : to_key;
    const target_source = this.item.env.smart_sources.get(target_source_key);
    if (!target_source) {
      await this.item.env.smart_sources.create(target_source_key, content);
      return;
    }
    if (is_block_ref) {
      const target_block = this.item.env.smart_blocks.get(to_key);
      if (target_block) {
        await target_block.append(content);
      } else {
        await target_source.append(content);
      }
    } else {
      await target_source.append(content);
    }
  }
  /**
   * Extract the block content using current line references from a full source content.
   * @private
   * @param {string} source_content Full source file content.
   * @returns {string} Extracted block content.
   * @throws {Error} If the block cannot be found.
   */
  _extract_block(source_content) {
    if (!source_content) {
      console.warn(`BLOCK NOT FOUND: ${this.item.key} has no source content.`);
      return "";
    }
    const { line_start, line_end } = this.item;
    if (!line_start || !line_end) {
      throw new Error(`BLOCK NOT FOUND: ${this.item.key} has invalid line references.`);
    }
    return get_line_range2(source_content, line_start, line_end);
  }
  /**
   * Re-parse the source file after a CRUD operation to update line references for all blocks.
   * @private
   * @async
   * @returns {Promise<void>}
   */
  async _reparse_source() {
    await this.item.source.import();
  }
  get_display_name(params = {}) {
    if (!this.item?.key) return "";
    const show_full_path = params.show_full_path ?? true;
    if (show_full_path) {
      return this.item.key.replace(/#/g, " > ").replace(/\//g, " > ");
    }
    const pcs = [];
    const [source_key, ...block_parts] = this.item.key.split("#");
    const filename = source_key.split("/").pop();
    pcs.push(filename);
    if (block_parts.length) {
      const last = block_parts[block_parts.length - 1];
      if (last.startsWith("{") && last.endsWith("}")) {
        block_parts.pop();
        pcs.push(block_parts.pop());
        if (this.item.lines) pcs.push(`Lines: ${this.item.lines.join("-")}`);
      } else {
        pcs.push(block_parts.pop());
      }
    }
    return pcs.filter(Boolean).join(" > ");
  }
};

// node_modules/obsidian-smart-env/src/collections/smart_blocks.js
var smart_blocks_default = {
  class: SmartBlocks,
  version: SmartBlocks.version,
  item_type: SmartBlock2,
  data_adapter: AjsonMultiFileBlocksDataAdapter,
  collection_key: "smart_blocks",
  block_adapters: {
    "md": MarkdownBlockContentAdapter,
    "txt": MarkdownBlockContentAdapter,
    "excalidraw.md": MarkdownBlockContentAdapter
    // "canvas": MarkdownBlockContentAdapter,
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-contexts/smart_context.js
var remove_context_item_data = (context_items, key) => {
  if (!key || !context_items?.[key]) return false;
  const item_data = context_items[key];
  if (item_data.folder || item_data.from_named_context) {
    if (item_data.exclude) {
      delete context_items[key];
      return true;
    }
    item_data.exclude = true;
    return true;
  }
  delete context_items[key];
  return true;
};
var SmartContext = class extends CollectionItem {
  static version = "2.0.2";
  static get defaults() {
    return {
      data: {
        key: "",
        context_items: {},
        context_opts: {}
        // REMOVE?
      }
    };
  }
  // queue_save to debounce process save queue
  queue_save() {
    super.queue_save();
    this.collection.queue_save();
  }
  /**
   * add_item
   * @param {string|object} item
   */
  add_item(item, params = {}) {
    const {
      emit_updated = true
    } = params;
    let key;
    if (typeof item === "object") {
      key = item.key || item.path;
    } else {
      key = item;
    }
    const existing = this.data.context_items[key];
    const context_item = {
      d: 0,
      at: Date.now(),
      ...existing || {},
      ...typeof item === "object" ? item : {}
    };
    if (!key) return console.error("SmartContext: add_item called with invalid item", item);
    this.data.context_items[key] = context_item;
    this.queue_save();
    if (emit_updated) this.emit_event("context:updated", { add_item: key });
  }
  /**
   * add_items
   * @param {string[]|object[]} items
   */
  add_items(items) {
    if (!Array.isArray(items)) items = [items];
    items.forEach((item) => this.add_item(item, { emit_updated: false }));
    this.emit_event("context:updated", {
      added_items: items.map((item) => typeof item === "object" ? item.key || item.path : item)
    });
  }
  /**
   * remove_item
   * Removes a path/ref from context and emits context:updated
   * @param {string} key
   * @param {object} params
   * @param {boolean} params.emit_updated
   */
  remove_item(key, params = {}) {
    const { emit_updated = true } = params;
    const removed = remove_context_item_data(this.data.context_items, key);
    if (!removed) return;
    this.queue_save();
    if (emit_updated) this.emit_event("context:updated", { removed_key: key, removed_keys: [key] });
  }
  /**
   * remove_items
   * Removes paths/refs from context and emits context:updated once
   * @param {string[]|string} keys
   * @param {object} params
   * @param {boolean} params.emit_updated
   * @returns {string[]}
   */
  remove_items(keys, params = {}) {
    const { emit_updated = true } = params;
    const items = Array.isArray(keys) ? keys : [keys];
    const removed_keys = [];
    items.forEach((item_key) => {
      if (remove_context_item_data(this.data.context_items, item_key)) {
        removed_keys.push(item_key);
      }
    });
    if (!removed_keys.length) return [];
    this.queue_save();
    if (emit_updated) this.emit_event("context:updated", { removed_keys });
    return removed_keys;
  }
  clear_all() {
    this.data.context_items = {};
    this.queue_save();
    this.emit_event("context:updated", { cleared: true });
  }
  get context_item_keys() {
    return Object.entries(this.data?.context_items || {}).filter(([, item_data]) => !item_data.exclude).map(([key]) => key);
  }
  get excluded_context_item_keys() {
    return Object.entries(this.data?.context_items || {}).filter(([, item_data]) => item_data?.exclude).map(([key]) => key);
  }
  get key() {
    if (!this.data.key) {
      this.data.key = Date.now().toString();
    }
    return this.data.key;
  }
  get has_context_items() {
    return this.item_count > 0;
  }
  get has_excluded_context_items() {
    return this.excluded_item_count > 0;
  }
  get excluded_item_count() {
    return this.excluded_context_item_keys.length;
  }
  get name() {
    return this.data.name;
  }
  set name(name) {
    if (typeof name !== "string") throw new TypeError("Name must be a string");
    const previous_name = typeof this.data.name === "string" ? this.data.name : "";
    const was_nameless = !previous_name || String(previous_name).trim().length === 0;
    this.data.name = name;
    if (was_nameless) {
      this.emit_info_event("context:named", { name });
    } else {
      this.emit_info_event("context:renamed", {
        old_name: previous_name,
        name
      });
    }
    this.queue_save();
  }
  get size() {
    let size = 0;
    Object.values(this.context_items.items || {}).forEach((item) => {
      if (item.size) size += item.size;
    });
    return size;
  }
  get item_count() {
    return Object.entries(this.data?.context_items || {}).filter(([, item_data]) => !item_data.exclude).length;
  }
  // v3
  async get_text(params = {}) {
    const segments = [];
    const context_items = this.context_items.filter(params.filter).sort((a, b) => a.data.d - b.data.d);
    console.log("get_text context_items", context_items);
    for (const item of context_items) {
      if (item.is_media) continue;
      const item_text = await item.get_text();
      if (typeof item_text === "string") segments.push(item_text);
      else this.emit_get_text_error(item, item_text);
    }
    const context_items_text = segments.join("\n");
    if (typeof this.actions.context_merge_template === "function") {
      return await this.actions.context_merge_template(context_items_text, { context_items });
    }
    return context_items_text;
  }
  /**
   * Build a ContextItems collection on demand.
   *
   * The builder sometimes needs excluded entries in addition to active items,
   * so this helper accepts the same params consumed by
   * ContextItems.load_from_data(...).
   *
   * @param {object} [params={}]
   * @returns {import('smart-contexts/context_items.js').ContextItems}
   */
  get_context_items(params = {}) {
    const config = this.env.config.collections.context_items;
    const Class = config.class;
    const context_items = new Class(this, { ...config, class: null });
    context_items.load_from_data(this.data.context_items || {}, params);
    return context_items;
  }
  get context_items() {
    return this.get_context_items();
  }
  /**
   * @private
   */
  emit_get_text_error(item, item_text) {
    this.emit_event("notification:error", {
      message: `Context item did not return text: ${item.key}`,
      ...item_text && typeof item_text === "object" ? item_text : {}
    });
  }
  /**
   * Move below to pro subclass
   */
  async get_media(params = {}) {
    const context_items = this.context_items.filter(params.filter);
    const out = [];
    for (const item of context_items) {
      if (!item.is_media) continue;
      const item_base64 = await item.get_base64();
      if (item_base64.error) this.emit_get_media_error(item, item_base64);
      else out.push(item_base64);
    }
    return out;
  }
  /**
   * @private
   */
  emit_get_media_error(item, item_base64) {
    this.emit_event("notification:error", {
      message: `Context item did not return media: ${item.key}`,
      ...item_base64 && typeof item_base64 === "object" ? item_base64 : {}
    });
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-contexts/smart_contexts.js
var SmartContexts = class extends Collection {
  static version = "2.0.1";
  /**
   * new_context
   * @param {object} data
   * @param {object} opts
   * @param {string[]} opts.add_items
   * @returns {SmartContext}
   */
  new_context(data = {}, opts = {}) {
    const item = new this.item_type(this.env, data);
    if (Array.isArray(opts.add_items)) item.add_items(opts.add_items);
    this.set(item);
    item.queue_save();
    item.emit_event("context:created");
    return item;
  }
  get_named_context(name) {
    return this.filter((ctx) => ctx.data?.name === name)[0];
  }
  /**
   * Default settings for all SmartContext items in this collection.
   * @readonly
   */
  static get default_settings() {
    return {
      template_preset: "xml_structured",
      template_before: "<context>\n{{FILE_TREE}}",
      template_after: "</context>"
    };
  }
  get settings_config() {
    return {
      ...this.env.config.actions.context_merge_template?.settings_config || {}
    };
  }
};
var smart_contexts_default = {
  class: SmartContexts,
  collection_key: "smart_contexts",
  data_adapter: AjsonSingleFileCollectionDataAdapter,
  item_type: SmartContext,
  version: SmartContexts.version
};

// node_modules/obsidian-smart-env/node_modules/smart-contexts/index.js
var smart_contexts_default_config = {
  class: SmartContexts,
  data_adapter: AjsonSingleFileCollectionDataAdapter,
  item_type: SmartContext
};
var smart_contexts_default2 = smart_contexts_default_config;

// node_modules/obsidian-smart-env/src/items/smart_context.js
var SmartContext2 = class extends SmartContext {
  get named_contexts() {
    return Object.entries(this.data?.context_items || {}).filter(([name, item_data]) => item_data?.named_context).map(([name, item_data]) => this.env.smart_contexts.get_named_context(item_data?.key || name)).filter(Boolean);
  }
  /**
   * @param {string} target_path
   */
  remove_by_path(target_path, params = {}) {
    return this.remove_by_paths([
      {
        path: target_path,
        ...params.folder ? { folder: true } : {}
      }
    ], params);
  }
  /**
   * Remove multiple paths from context in one data pass.
   *
   * @param {Array<string|{ path:string, folder?:boolean }>} target_paths
   * @param {object} [params={}]
   * @param {boolean} [params.emit_updated=true]
   * @param {boolean} [params.queue_save=true]
   * @returns {string[]}
   */
  remove_by_paths(target_paths = [], params = {}) {
    const {
      emit_updated = true,
      queue_save = true
    } = params;
    const targets = normalize_remove_targets(target_paths, params);
    if (!targets.length) return [];
    const context_items = this.data?.context_items || {};
    const remove_keys = [];
    Object.keys(context_items).forEach((key) => {
      const target = targets.find((target2) => item_matches_remove_path(key, target2.norm_key));
      if (!target) return;
      remove_keys.push(key);
    });
    remove_keys.forEach((key) => {
      delete context_items[key];
    });
    if (!remove_keys.length) return [];
    if (emit_updated) {
      this.emit_event("context:updated", {
        removed_key: targets[0].path,
        removed_keys: targets.map((target) => target.path),
        ...targets.some((target) => target.folder) ? { folder: true } : {}
      });
    }
    if (queue_save) this.queue_save();
    return remove_keys;
  }
  /**
   * add_item
   * this override adds implementation-specific (source/block pattern) logic to remove redundant block items when a parent source is added
   * @param {string|object} item
   */
  add_item(item, params = {}) {
    const {
      emit_updated = true
    } = params;
    let key;
    if (typeof item === "object") {
      key = item.key || item.path;
    } else {
      key = item;
    }
    const existing = this.data.context_items[key];
    const context_item = {
      d: 0,
      at: Date.now(),
      ...existing || {},
      ...typeof item === "object" ? item : {}
    };
    if (!key) return console.error("SmartContext: add_item called with invalid item", item);
    const emit_payload = { add_item: key };
    const remove_sub_keys = Object.entries(this.data.context_items).filter(([existing_key]) => existing_key !== key && existing_key.startsWith(key)).map(([existing_key]) => existing_key);
    if (remove_sub_keys.length) {
      this.remove_items(remove_sub_keys, { emit_updated: false });
      emit_payload.removed_keys = remove_sub_keys;
      emit_payload.message = "Parent item added, removed redundant sub-items";
    }
    this.data.context_items[key] = context_item;
    this.queue_save();
    if (emit_updated) this.emit_event("context:updated", emit_payload);
  }
};
function normalize_remove_targets(target_paths = [], params = {}) {
  const items = Array.isArray(target_paths) ? target_paths : [target_paths];
  const targets = [];
  items.forEach((target) => {
    const path = typeof target === "string" ? target : target?.path;
    const normalized_path = String(path || "").trim();
    if (!normalized_path) return;
    const next_target = {
      path: normalized_path,
      norm_key: normalize_remove_path(normalized_path),
      folder: target?.folder === true || params.folder === true
    };
    for (const existing_target of targets) {
      if (item_matches_remove_path(next_target.norm_key, existing_target.norm_key)) return;
    }
    for (let i = targets.length - 1; i >= 0; i -= 1) {
      if (item_matches_remove_path(targets[i].norm_key, next_target.norm_key)) {
        targets.splice(i, 1);
      }
    }
    targets.push(next_target);
  });
  return targets;
}
function normalize_remove_path(path = "") {
  return String(path || "").replace(/\/+$/g, "");
}
function item_matches_remove_path(item_key = "", target_path = "") {
  const normalized_item_key = normalize_remove_path(item_key);
  const normalized_target_path = normalize_remove_path(target_path);
  if (!normalized_item_key || !normalized_target_path) return false;
  return normalized_item_key === normalized_target_path || normalized_item_key.startsWith(normalized_target_path + "/") || normalized_item_key.startsWith(normalized_target_path + "#") || normalized_item_key.startsWith(normalized_target_path + "{");
}

// node_modules/obsidian-smart-env/src/collections/smart_contexts.js
smart_contexts_default2.class = SmartContexts;
smart_contexts_default2.version = SmartContexts.version;
smart_contexts_default2.item_type = SmartContext2;
var smart_contexts_default3 = smart_contexts_default2;

// node_modules/obsidian-smart-env/src/utils/format_collection_name.js
function format_collection_name(key) {
  return key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
}

// node_modules/obsidian-smart-env/src/components/collection_settings.js
async function build_html(collection, opts = {}) {
  const settings_html = Object.entries(collection.settings_config).map(([setting_key, setting_config]) => {
    if (!setting_config.setting) setting_config.setting = setting_key;
    return this.render_setting_html(setting_config);
  }).join("\n");
  const html = `<div><div class="collection-settings-container"><div class="source-settings collection-settings">
    <h2>${format_collection_name(collection.collection_key)}</h2>
    ${settings_html}
  </div></div></div>`;
  return html;
}
async function render(collection, opts = {}) {
  const html = await build_html.call(this, collection, opts);
  const frag = this.create_doc_fragment(html);
  await this.render_setting_components(frag, { scope: collection });
  if (opts.settings_container) {
    this.empty(opts.settings_container);
    opts.settings_container.appendChild(frag.querySelector(".collection-settings"));
  } else {
    collection.settings_container = frag.querySelector(".collection-settings-container");
  }
  return collection.settings_container;
}

// node_modules/obsidian-smart-env/src/utils/register_block_hover_popover.js
var import_obsidian10 = require("obsidian");
function register_block_hover_popover(parent, target, env, block_key, params = {}) {
  const app2 = env?.plugin?.app || window.app;
  target.addEventListener("mouseover", async (ev) => {
    if (import_obsidian10.Keymap.isModEvent(ev)) {
      const block = env.smart_blocks.get(block_key);
      const markdown = await block?.read();
      if (markdown) {
        const popover = new import_obsidian10.HoverPopover(parent, target);
        const frag = env.smart_view.create_doc_fragment(`<div class="markdown-embed is-loaded">
                <div class="markdown-embed-content node-insert-event">
                  <div class="markdown-preview-view markdown-rendered node-insert-event show-indentation-guide allow-fold-headings allow-fold-lists">
                    <div class="markdown-preview-sizer markdown-preview-section">
                    </div>
                  </div>
                </div>
              </div>`);
        popover.hoverEl.classList.add("smart-block-popover");
        popover.hoverEl.appendChild(frag);
        const sizer = popover.hoverEl.querySelector(".markdown-preview-sizer");
        import_obsidian10.MarkdownRenderer.render(app2, markdown, sizer, "/", popover);
        const event_domain = params.event_key_domain || "block";
        block.emit_event(`${event_domain}:hover_preview`);
      }
    }
  });
}

// node_modules/obsidian-smart-env/src/utils/register_item_hover_popover.js
var import_obsidian11 = require("obsidian");
function register_item_hover_popover(container, item, params = {}) {
  const app2 = item.env?.plugin?.app || window.app;
  if (item.key.indexOf("{") === -1) {
    container.addEventListener("mouseover", (event) => {
      const linktext_path = item.key.replace(/#$/, "");
      app2.workspace.trigger("hover-link", {
        event,
        source: "smart-connections-view",
        hoverParent: container.parentElement,
        targetEl: container,
        linktext: linktext_path
      });
      if (import_obsidian11.Keymap.isModEvent(event)) {
        const event_domain = params.event_key_domain || item.collection_key || "item";
        item.emit_event(`${event_domain}:hover_preview`);
      }
    });
  } else {
    register_block_hover_popover(container.parentElement, container, item.env, item.key);
  }
}

// node_modules/obsidian-smart-env/src/components/context-item/leaf.js
var import_obsidian12 = require("obsidian");
function format_score(score) {
  const numeric_score = typeof score === "number" ? score : Number.parseFloat(score);
  if (!Number.isFinite(numeric_score)) return null;
  return Number.parseFloat(numeric_score.toFixed(2)).toString();
}
function format_size(size) {
  const numeric_size = typeof size === "number" ? size : Number.parseFloat(size);
  if (!Number.isFinite(numeric_size) || numeric_size < 0) return null;
  const units = ["B", "KB", "MB", "GB"];
  let size_value = numeric_size;
  let unit_index = 0;
  while (size_value >= 1024 && unit_index < units.length - 1) {
    size_value /= 1024;
    unit_index += 1;
  }
  const precision = size_value >= 10 || Number.isInteger(size_value) ? 0 : 1;
  const rounded_value = Number.parseFloat(size_value.toFixed(precision));
  return `${rounded_value.toString()} ${units[unit_index]}`;
}
function format_size_percent(size, context_size) {
  const numeric_size = typeof size === "number" ? size : Number.parseFloat(size);
  const numeric_context_size = typeof context_size === "number" ? context_size : Number.parseFloat(context_size);
  if (!Number.isFinite(numeric_size) || numeric_size < 0) return null;
  if (!Number.isFinite(numeric_context_size) || numeric_context_size <= 0) return null;
  const percent_value = numeric_size / numeric_context_size * 100;
  const precision = percent_value >= 10 || Number.isInteger(percent_value) ? 0 : 1;
  const rounded_value = Number.parseFloat(percent_value.toFixed(precision));
  return `${rounded_value.toString()}%`;
}
function format_size_label(size, context_size) {
  const size_label = format_size(size);
  if (!size_label) return null;
  const percent_label = format_size_percent(size, context_size);
  if (!percent_label) return size_label;
  return `${percent_label} (${size_label})`;
}
function build_badge_html(label, class_name, params = {}) {
  if (!label) return "";
  const icon_attr = params.icon ? ` data-icon="${escape_html(params.icon)}"` : "";
  const tooltip = params.title ? ` aria-label="${escape_html(params.title)}"` : "";
  const named_context_attr = params.named_context ? ` role="button" data-named-context="${escape_html(params.named_context)}"` : "";
  const icon_html = params.icon ? '<span class="sc-context-item-badge-icon"></span>' : "";
  const label_html = params.icon ? `` : escape_html(label);
  return `<span class="${class_name}"${icon_attr}${tooltip}${named_context_attr}>${icon_html}${label_html}</span>`;
}
function get_context_item_name(context_item) {
  const item_ref = context_item?.item_ref || null;
  const item_key = String(context_item?.key || "");
  if (item_ref?.key) {
    const item_ref_key = String(item_ref.key);
    if (item_ref_key.includes("#")) {
      const name_pcs = item_ref_key.split("/").pop().split("#").filter(Boolean);
      const last_pc = name_pcs.pop();
      if (last_pc && last_pc.startsWith("{")) {
        const lines = Array.isArray(item_ref.lines) ? item_ref.lines.join("-") : "";
        return lines ? `Lines: ${lines}` : last_pc;
      }
      return last_pc;
    }
    return item_ref_key.split("/").pop();
  }
  return item_key.split("/").pop();
}
function get_folder_source(context_item) {
  const data = context_item?.data || {};
  if (typeof data.from_folder === "string" && data.from_folder.trim()) return data.from_folder;
  if (typeof data.folder === "string" && data.folder.trim()) return data.folder;
  if (data.folder === true) return data.key || context_item?.key || "";
  return "";
}
function format_folder_badge_label(folder_source = "") {
  const normalized = String(folder_source || "").replace(/^external:/, "").replace(/\\+/g, "/").replace(/\/+$/g, "");
  return normalized.split("/").filter(Boolean).pop() || normalized;
}
function get_origin_badges(context_item) {
  const data = context_item?.data || {};
  const badges = [];
  const folder_source = get_folder_source(context_item);
  if (folder_source) {
    badges.push({
      icon: "folder",
      label: format_folder_badge_label(folder_source),
      title: `Included from folder: ${folder_source}`,
      class_name: "sc-context-item-origin-badge sc-context-item-origin-folder"
    });
  }
  const named_context = typeof data.from_named_context === "string" ? data.from_named_context.trim() : "";
  if (named_context) {
    badges.push({
      icon: "smart-named-contexts",
      label: named_context,
      named_context,
      title: `Included from named context: ${named_context}`,
      class_name: "sc-context-item-origin-badge sc-context-item-origin-named-context"
    });
  }
  return badges;
}
function build_origin_badges_html(context_item) {
  const badges = get_origin_badges(context_item);
  if (!badges.length) return "";
  return `<span class="sc-context-item-origin-badges">${badges.map((badge) => build_badge_html(badge.label, badge.class_name, badge)).join("")}</span>`;
}
function render_inline_icons(container) {
  container.querySelectorAll("[data-icon]").forEach((icon_el) => {
    const icon = icon_el.getAttribute("data-icon");
    if (!icon) return;
    const target = icon_el.querySelector(".sc-context-item-badge-icon") || icon_el;
    (0, import_obsidian12.setIcon)(target, icon);
  });
}
function set_remove_pending(remove_btn) {
  remove_btn.classList.add("is-removing");
  remove_btn.textContent = "";
  remove_btn.setAttribute("aria-label", "Removing item");
  remove_btn.setAttribute("aria-busy", "true");
}
function clear_remove_pending(remove_btn) {
  remove_btn.classList.remove("is-removing");
  remove_btn.textContent = "\xD7";
  remove_btn.removeAttribute("aria-busy");
  remove_btn.setAttribute("aria-label", "Remove item");
}
function build_html2(context_item, params = {}) {
  const name = get_context_item_name(context_item);
  const item_key = String(context_item?.key || "");
  const score = format_score(context_item?.data?.score);
  const item_size = context_item?.size ?? context_item?.data?.size;
  const size = format_size_label(item_size, params.context_size);
  const score_html = build_badge_html(score, "sc-context-item-score");
  const size_html = build_badge_html(size, "sc-context-item-size");
  const origin_badges_html = build_origin_badges_html(context_item);
  const icon_type = context_item?.icon_type || null;
  const icon_html = icon_type ? `<span class="sc-context-item-type-icon" data-icon="${escape_html(icon_type)}"></span>` : "";
  const missing_class = context_item?.exists === false ? " missing" : "";
  const remove_disabled = params.remove_disabled === true;
  const remove_class = remove_disabled ? "sc-context-item-remove is-disabled" : "sc-context-item-remove";
  const remove_label = remove_disabled ? "Open named context to edit included item" : "Remove item";
  const remove_disabled_attr = remove_disabled ? ' aria-disabled="true"' : "";
  return `<span class="sc-context-item-leaf">
  <span class="${remove_class}" data-path="${escape_html(item_key)}" aria-label="${escape_html(remove_label)}"${remove_disabled_attr}>\xD7</span>
  ${score_html}
  ${icon_html}
  <span class="sc-context-item-name${missing_class}">${escape_html(name || item_key)}</span>
  ${size_html}
  ${origin_badges_html}
  </span>`;
}
async function render2(context_item, params = {}) {
  const html = build_html2(context_item, params);
  const frag = this.create_doc_fragment(html);
  const container = frag.firstElementChild;
  post_process.call(this, context_item, container, params);
  return container;
}
async function post_process(context_item, container, params = {}) {
  render_inline_icons(container);
  const remove_btn = container.querySelector(".sc-context-item-remove");
  if (remove_btn) {
    if (params.remove_disabled === true) {
      remove_btn.setAttribute("title", "Open the named context to remove this included item.");
    }
    remove_btn.addEventListener("click", (event) => {
      event.preventDefault();
      event.stopPropagation();
      if (params.remove_disabled === true) {
        if (typeof params.on_remove_disabled === "function") {
          params.on_remove_disabled(event, context_item);
        }
        return;
      }
      if (typeof params.on_remove !== "function") return;
      if (remove_btn.classList.contains("is-removing")) return;
      set_remove_pending(remove_btn);
      try {
        const result = params.on_remove(event, context_item);
        if (result && typeof result.catch === "function") {
          result.catch((error) => {
            clear_remove_pending(remove_btn);
            console.warn("Smart Context: Failed to remove context item", error);
          });
        }
      } catch (error) {
        clear_remove_pending(remove_btn);
        throw error;
      }
    });
  }
  const name = container.querySelector(".sc-context-item-name");
  if (!name) return container;
  const is_missing = context_item?.exists === false;
  if (is_missing) {
    name.setAttribute("title", "Missing source");
  }
  const item_ref = context_item?.item_ref || null;
  if (item_ref && !is_missing) {
    name.setAttribute("title", `Hold ${import_obsidian12.Platform.isMacOS ? "\u2318" : "Ctrl"} to preview`);
    register_item_hover_popover(name, item_ref);
  }
  name.addEventListener("click", (event) => {
    if (typeof context_item.open !== "function") return;
    context_item.open(event);
  });
  return container;
}

// node_modules/obsidian-smart-env/src/components/default_notification.js
var import_obsidian13 = require("obsidian");

// node_modules/obsidian-smart-env/src/components/default_notification.css
var default_notification_default = ".notice:has(.smart-env-default-notice) {\n  padding: 0;\n  border: none;\n  background: transparent;\n  box-shadow: none;\n  min-width: 0;\n  max-width: min(420px, calc(100vw - 2rem));\n}\n\n.notice-message:has(.smart-env-default-notice) {\n  padding: 0;\n  background: transparent;\n}\n\n.smart-env-default-notice {\n  --smart-env-default-notice-accent: var(--text-muted);\n\n  width: 100%;\n  min-width: 0;\n  font-family: var(--font-interface);\n}\n\n.smart-env-default-notice[data-level='error'] {\n  --smart-env-default-notice-accent: var(--color-red);\n}\n\n.smart-env-default-notice[data-level='warning'] {\n  --smart-env-default-notice-accent: var(--color-orange);\n}\n\n.smart-env-default-notice[data-level='attention'] {\n  --smart-env-default-notice-accent: var(--color-yellow);\n}\n\n.smart-env-default-notice[data-level='milestone'] {\n  --smart-env-default-notice-accent: var(--color-accent);\n}\n\n.smart-env-default-notice[data-level='info'] {\n  --smart-env-default-notice-accent: var(--text-muted);\n}\n\n.smart-env-default-notice__surface {\n  display: grid;\n  grid-template-columns: auto 1fr;\n  gap: var(--size-4-3);\n  align-items: start;\n\n  width: 100%;\n  box-sizing: border-box;\n  padding: var(--size-4-3);\n  border: 1px solid var(--background-modifier-border);\n  border-inline-start: 3px solid var(--smart-env-default-notice-accent);\n  border-radius: var(--radius-l);\n  background: var(--background-secondary);\n}\n\n.smart-env-default-notice[data-muted='true'] .smart-env-default-notice__surface {\n  opacity: 0.86;\n}\n\n.smart-env-default-notice__icon.clickable-icon {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  align-self: start;\n\n  width: calc(var(--icon-l) + var(--size-4-4));\n  height: calc(var(--icon-l) + var(--size-4-4));\n  min-width: auto;\n  min-height: auto;\n  padding: var(--size-2-2);\n\n  color: var(--smart-env-default-notice-accent);\n  background: var(--background-primary);\n  border: 1px solid var(--background-modifier-border);\n  border-radius: var(--button-radius);\n}\n\n.smart-env-default-notice__icon.clickable-icon:hover:not(:disabled) {\n  color: var(--text-normal);\n  border-color: var(--background-modifier-border-hover, var(--background-modifier-border));\n}\n\n.smart-env-default-notice__icon.clickable-icon:disabled {\n  opacity: 0.6;\n  cursor: default;\n}\n\n.smart-env-default-notice__icon svg {\n  width: var(--icon-l);\n  height: var(--icon-l);\n}\n\n.smart-env-default-notice__body {\n  min-width: 0;\n}\n\n.smart-env-default-notice__eyebrow {\n  margin: 0 0 var(--size-2-1);\n  color: var(--text-muted);\n  font-size: var(--font-ui-small);\n  font-weight: var(--font-semibold);\n  letter-spacing: 0.08em;\n  line-height: 1.1;\n  text-transform: uppercase;\n}\n\n.smart-env-default-notice__title {\n  color: var(--text-normal);\n  font-size: var(--font-text-size);\n  font-weight: var(--font-semibold);\n  line-height: 1.3;\n  white-space: pre-wrap;\n}\n\n.smart-env-default-notice__details {\n  margin-top: var(--size-2-2);\n  color: var(--text-muted);\n  font-size: var(--font-ui-small);\n  line-height: 1.45;\n  white-space: pre-wrap;\n}\n\n.smart-env-default-notice__actions {\n  display: flex;\n  align-items: center;\n  gap: var(--size-2-2) var(--size-4-2);\n  flex-wrap: wrap;\n  margin-top: var(--size-4-2);\n}\n\n.smart-env-default-notice__button,\n.smart-env-default-notice__mute {\n  margin: 0;\n}\n\n.smart-env-default-notice__mute {\n  white-space: nowrap;\n}\n\n.smart-env-default-notice__link {\n  color: var(--text-accent, var(--color-accent));\n  font-size: var(--font-ui-small);\n  text-decoration: underline;\n  text-decoration-thickness: 1px;\n  text-underline-offset: 2px;\n}\n\n@media (max-width: 520px) {\n  .notice:has(.smart-env-default-notice) {\n    max-width: calc(100vw - 1.2rem);\n  }\n\n  .smart-env-default-notice__surface {\n    grid-template-columns: 1fr;\n    gap: var(--size-4-2);\n  }\n}\n";

// node_modules/obsidian-smart-env/src/components/default_notification.js
function to_trimmed_string2(value) {
  return typeof value === "string" ? value.trim() : "";
}
function get_default_notice_title(event_key, event = {}) {
  const message = to_trimmed_string2(event?.message);
  if (message) return message;
  return event_key || "Notification";
}
function get_default_notice_details(event = {}, title = "") {
  const details = to_trimmed_string2(event?.details);
  if (details && details !== title) return details;
  return "";
}
function format_level_label(level) {
  const value = typeof level === "string" ? level : "info";
  return value.slice(0, 1).toUpperCase() + value.slice(1);
}
function render_icon(icon_el, level) {
  if (!icon_el) return;
  const icon_ids = get_icon_ids(level);
  for (const icon_id of icon_ids) {
    if (typeof icon_id !== "string" || icon_id.length === 0) continue;
    icon_el.textContent = "";
    try {
      (0, import_obsidian13.setIcon)(icon_el, icon_id);
    } catch (_error) {
      continue;
    }
    if (icon_el.querySelector("svg")) return;
  }
  icon_el.textContent = "\u2022";
}
function get_icon_ids(level) {
  switch (level) {
    case "error":
      return ["circle-alert", "alert-circle", "octagon-alert"];
    case "warning":
      return ["triangle-alert", "alert-triangle", "circle-alert"];
    case "attention":
      return ["bell-ring", "bell", "info"];
    case "milestone":
      return ["sparkles", "badge-check", "circle-check"];
    default:
      return ["info", "badge-info", "circle-alert"];
  }
}
function get_default_notice_summary(event_key, event = {}) {
  const title = get_default_notice_title(event_key, event);
  const details = get_default_notice_details(event, title);
  if (!details) return title;
  return `${title}
${details}`;
}
function open_notifications_feed(env, params = {}) {
  const {
    on_open_feed = null,
    event_key = "",
    event = {}
  } = params;
  const open_params = {
    event_key,
    event
  };
  if (typeof on_open_feed === "function") {
    return on_open_feed(open_params) !== false;
  }
  if (typeof env?.open_notifications_feed_modal === "function") {
    env.open_notifications_feed_modal(open_params);
    return true;
  }
  return false;
}
function render3(env, params = {}) {
  const {
    event_key = "",
    event = {},
    on_action = null,
    on_mute = null,
    on_open_feed = null
  } = params;
  const level = get_event_level(event_key, event) || "info";
  const title = get_default_notice_title(event_key, event);
  const details = get_default_notice_details(event, title);
  const btn_text = to_trimmed_string2(event?.btn_text);
  const btn_callback = to_trimmed_string2(event?.btn_callback);
  const help_link = to_trimmed_string2(event?.link);
  if (typeof document === "undefined") {
    return get_default_notice_summary(event_key, event);
  }
  this.apply_style_sheet?.(default_notification_default);
  const run_action = typeof on_action === "function" ? on_action : (callback_key) => dispatch_notice_action(env, callback_key, {
    event_source: "default_notification",
    source_event_key: event_key,
    source_event: event
  });
  const run_mute = typeof on_mute === "function" ? on_mute : () => false;
  const can_open_feed = typeof on_open_feed === "function" || typeof env?.open_notifications_feed_modal === "function";
  const can_view_more = can_open_feed && Boolean(event_key);
  const frag = document.createDocumentFragment();
  const wrapper = document.createElement("div");
  wrapper.className = "smart-env-default-notice";
  wrapper.dataset.level = level;
  const surface_el = document.createElement("div");
  surface_el.className = "smart-env-default-notice__surface";
  const icon_el = document.createElement("button");
  icon_el.type = "button";
  icon_el.className = "smart-env-default-notice__icon clickable-icon";
  icon_el.setAttribute("aria-label", "Open events and notifications");
  icon_el.setAttribute("title", "Open events and notifications");
  icon_el.disabled = !can_open_feed;
  render_icon(icon_el, level);
  if (can_open_feed) {
    icon_el.addEventListener("click", (event_obj) => {
      event_obj.preventDefault();
      event_obj.stopPropagation();
      open_notifications_feed(env, {
        on_open_feed,
        event_key,
        event
      });
    });
  }
  const body_el = document.createElement("div");
  body_el.className = "smart-env-default-notice__body";
  const eyebrow_el = document.createElement("div");
  eyebrow_el.className = "smart-env-default-notice__eyebrow";
  eyebrow_el.textContent = `${format_level_label(level)}`;
  const title_el = document.createElement("div");
  title_el.className = "smart-env-default-notice__title";
  title_el.textContent = title;
  body_el.appendChild(eyebrow_el);
  body_el.appendChild(title_el);
  if (details) {
    const details_el = document.createElement("div");
    details_el.className = "smart-env-default-notice__details";
    details_el.textContent = details;
    body_el.appendChild(details_el);
  }
  const should_render_actions = Boolean(btn_text && btn_callback) || Boolean(help_link) || Boolean(event_key);
  if (should_render_actions) {
    const actions_el = document.createElement("div");
    actions_el.className = "smart-env-default-notice__actions";
    if (btn_text && btn_callback) {
      const button_el = document.createElement("button");
      button_el.type = "button";
      button_el.className = "smart-env-default-notice__button mod-cta";
      button_el.textContent = btn_text;
      button_el.setAttribute("aria-label", btn_text);
      button_el.addEventListener("click", () => {
        run_action(btn_callback);
      });
      actions_el.appendChild(button_el);
    }
    if (can_view_more) {
      const view_more_btn_el = document.createElement("button");
      view_more_btn_el.type = "button";
      view_more_btn_el.className = "smart-env-default-notice__button";
      view_more_btn_el.textContent = "View more";
      view_more_btn_el.setAttribute("aria-label", "Open this event in the notifications feed");
      view_more_btn_el.addEventListener("click", () => {
        open_notifications_feed(env, {
          on_open_feed,
          event_key,
          event
        });
      });
      actions_el.appendChild(view_more_btn_el);
    }
    if (help_link) {
      const link_el = document.createElement("a");
      link_el.className = "smart-env-default-notice__link";
      link_el.href = help_link;
      link_el.textContent = "Learn more";
      link_el.target = "_external";
      link_el.rel = "noopener noreferrer";
      link_el.setAttribute("aria-label", "Learn more about this notification");
      actions_el.appendChild(link_el);
    }
    if (event_key) {
      const mute_btn_el = document.createElement("button");
      mute_btn_el.type = "button";
      mute_btn_el.className = "smart-env-default-notice__mute";
      mute_btn_el.textContent = "Mute";
      (0, import_obsidian13.setIcon)(mute_btn_el, "bell-off");
      mute_btn_el.setAttribute("aria-label", "Mute future native notices for this event key");
      mute_btn_el.addEventListener("click", () => {
        const muted = run_mute();
        if (muted === false) return;
        wrapper.dataset.muted = "true";
        mute_btn_el.disabled = true;
        mute_btn_el.textContent = "Muted";
      });
      actions_el.appendChild(mute_btn_el);
    }
    body_el.appendChild(actions_el);
  }
  surface_el.appendChild(icon_el);
  surface_el.appendChild(body_el);
  wrapper.appendChild(surface_el);
  frag.appendChild(wrapper);
  return frag;
}

// node_modules/obsidian-smart-env/src/components/env_stats.js
async function build_html3(env, opts = {}) {
  const lines = [];
  lines.push(`<h2>Collections</h2>`);
  const collection_keys = Object.keys(env.collections).filter((key) => ["smart_sources", "smart_blocks"].includes(key)).sort((a, b) => {
    if (a === "smart_sources" || a === "smart_blocks") return -1;
    if (b === "smart_sources" || b === "smart_blocks") return 1;
    return a.localeCompare(b);
  });
  for (const collection_key of collection_keys) {
    const collection = env[collection_key];
    if (!collection || !collection.items) {
      lines.push(`
        <div class="sc-collection-stats">
          <h3>${format_collection_name(collection_key)}</h3>
          <p>No valid items.</p>
        </div>
      `);
      continue;
    }
    const snippet = generate_collection_stats(collection, collection_key);
    lines.push(snippet);
  }
  return `
    <div class="sc-env-stats-container" style="user-select: text;">
      ${lines.join("\n")}
    </div>
  `;
}
async function render4(env, opts = {}) {
  const html = await build_html3.call(this, env, opts);
  const frag = this.create_doc_fragment(html);
  return await post_process2.call(this, env, frag, opts);
}
async function post_process2(env, frag, opts = {}) {
  return frag;
}
function generate_collection_stats(collection, collectionKey) {
  const total_items = Object.values(collection.items).length;
  const niceName = format_collection_name(collectionKey);
  const state = collection.env.collections[collectionKey];
  if (state !== "loaded") {
    return `
      <div class="sc-collection-stats">
        <h3>${niceName}</h3>
        <p>Not loaded yet (${total_items} items known).</p>
      </div>
    `;
  }
  const load_time_html = collection.load_time_ms ? `<p>Load time: ${collection.load_time_ms}ms</p>` : "";
  const state_html = `<p>State: ${state}</p>`;
  let html = get_generic_collection_stats(collection, niceName, total_items);
  let embed_stats = "";
  if (typeof collection.process_embed_queue === "function") {
    embed_stats = calculate_embed_coverage(collection, total_items);
  }
  return `
    <div class="sc-collection-stats">
      <h3>${niceName}</h3>
      ${embed_stats}
      ${html}
      ${load_time_html}
      ${state_html}
    </div>
  `;
}
function get_generic_collection_stats(collection, niceName, total_items, load_time_html) {
  return `
      <p><strong>Total:</strong> ${total_items}</p>
  `;
}
function calculate_embed_coverage(collection, total_items) {
  const embedded_items = Object.values(collection.items).filter((item) => item.vec);
  if (!embedded_items.length) return "<p>No items embedded</p>";
  const stats = Object.values(collection.items).reduce((acc, i) => {
    if (i.should_embed) acc.should_embed += 1;
    else acc.should_not_embed += 1;
    if (i.vec) acc.embedded += 1;
    if (i.should_embed && !i.vec) acc.missing_embed += 1;
    if (!i.should_embed && i.vec) acc.extraneous_embed += 1;
    return acc;
  }, { should_embed: 0, embedded: 0, missing_embed: 0, extraneous_embed: 0, should_not_embed: 0 });
  const pct = stats.embedded / stats.should_embed * 100;
  const percent = Math.round(pct);
  return `<p><strong>Embedding coverage:</strong> ${percent}% (${stats.embedded} / ${stats.should_embed})</p>` + (stats.missing_embed ? `<p><strong>Missing embeddings:</strong> ${stats.missing_embed}</p>` : "") + (stats.extraneous_embed ? `<p><strong>Extraneous embeddings:</strong> ${stats.extraneous_embed}</p>` : "") + (stats.should_not_embed ? `<p><strong>Other items (e.g. less than minimum length to embed):</strong> ${stats.should_not_embed}</p>` : "");
}

// node_modules/obsidian-smart-env/src/components/env_status.css
var env_status_default = ".smart-env-status-view {\n  display: flex;\n  flex-direction: column;\n  gap: 0.85rem;\n  padding: var(--size-4-4);\n}\n\n.smart-env-status-view__header {\n  display: flex;\n  flex-direction: column;\n  gap: 0.2rem;\n}\n\n.smart-env-status-view__eyebrow {\n  font-size: var(--font-ui-smaller);\n  letter-spacing: 0.08em;\n  text-transform: uppercase;\n  color: var(--text-muted);\n}\n\n.smart-env-status-view__title {\n  margin: 0;\n  font-size: var(--h2-size);\n  line-height: 1.15;\n}\n\n.smart-env-status-view__status {\n  color: var(--text-muted);\n  font-size: var(--font-ui-small);\n  font-variant-numeric: tabular-nums;\n}\n\n.smart-env-status-view__summary {\n  color: var(--text-normal);\n  line-height: 1.4;\n}\n\n.smart-env-status-view__progress {\n  height: 8px;\n  border-radius: 999px;\n  overflow: hidden;\n  border: 1px solid var(--background-modifier-border);\n  background: var(--background-secondary);\n}\n\n.smart-env-status-view__progress-fill {\n  height: 100%;\n  width: var(--smart-env-status-view-pct, 0%);\n  background: var(--color-accent);\n}\n\n.smart-env-status-view__details {\n  display: flex;\n  flex-direction: column;\n  gap: 0.45rem;\n}\n\n.smart-env-status-view__detail {\n  padding: 0.55rem 0.7rem;\n  border-radius: var(--radius-m);\n  background: var(--background-secondary);\n  color: var(--text-muted);\n  font-size: var(--font-ui-small);\n  line-height: 1.35;\n}\n\n.smart-env-status-view__actions {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.5rem;\n}\n\n.smart-env-status-view__btn {\n  appearance: none;\n  border: 1px solid var(--background-modifier-border);\n  background: var(--background-modifier-form-field);\n  color: var(--text-normal);\n  border-radius: 10px;\n  padding: 0.5rem 0.8rem;\n  cursor: pointer;\n  transition: background 120ms ease, border-color 120ms ease, transform 120ms ease;\n}\n\n.smart-env-status-view__btn:hover {\n  background: var(--background-modifier-hover);\n}\n\n.smart-env-status-view__btn:active {\n  transform: translateY(1px);\n}\n\n.smart-env-status-view__btn:focus-visible {\n  outline: 2px solid var(--color-accent);\n  outline-offset: 2px;\n}\n\n.smart-env-status-view__btn--primary {\n  background: var(--color-accent);\n  border-color: transparent;\n  color: var(--text-on-accent, var(--text-normal));\n}\n\n.smart-env-status-view__btn--primary:hover {\n  background: var(--interactive-accent-hover);\n}\n";

// node_modules/obsidian-smart-env/src/utils/status_bar_state.js
var indicator_level_rank = {
  info: 1,
  milestone: 2,
  attention: 3,
  warning: 4,
  error: 5
};
function get_unseen_notification_entries(event_logs) {
  const session_events = event_logs?.session_events;
  if (!Array.isArray(session_events)) return [];
  return session_events.filter((entry) => entry?.unseen === true);
}
function get_notification_event_count(event_logs) {
  return get_unseen_notification_entries(event_logs).length;
}
function get_next_indicator_level(current_level, event_key = "", event = {}) {
  const next_level = get_event_level(event_key, event);
  if (!next_level) return current_level ?? null;
  const current_rank = indicator_level_rank[current_level] || 0;
  const next_rank = indicator_level_rank[next_level] || 0;
  if (next_rank > current_rank) return next_level;
  return current_level ?? next_level;
}
function get_notification_indicator_level(event_logs) {
  return get_unseen_notification_entries(event_logs).reduce((current_level, entry) => {
    return get_next_indicator_level(current_level, entry?.event_key, entry?.event);
  }, null);
}
function get_status_bar_notice_line(event_key = "", event = {}) {
  const message = get_native_notice_message(event_key, event);
  return String(message || "").split(/\r?\n/u)[0].trim();
}
function format_status_bar_notice_message(value = "") {
  const text = typeof value === "string" ? value.trim() : "";
  if (!text) return "";
  if (text.length <= 20) return text;
  return `${text.slice(0, 20)}\u2026`;
}
function get_status_bar_notice_preview(event_logs) {
  const session_events = event_logs?.session_events;
  if (!Array.isArray(session_events) || session_events.length === 0) return null;
  for (let i = session_events.length - 1; i >= 0; i -= 1) {
    const entry = session_events[i];
    const event_key = typeof entry?.event_key === "string" ? entry.event_key : "";
    const event = entry?.event && typeof entry.event === "object" ? entry.event : {};
    if (!get_event_level(event_key, event)) continue;
    if (entry?.native_notice_shown === true) return null;
    const timeout_ms = get_native_notice_timeout(event_key, event);
    const expires_at = get_entry_timestamp(entry) + timeout_ms;
    if (Date.now() > expires_at) return null;
    const title = get_status_bar_notice_line(event_key, event);
    const message = format_status_bar_notice_message(title);
    if (!message) return null;
    return { message, title };
  }
  return null;
}
function get_import_progress_state(env) {
  return env?.smart_sources?.get_import_progress_state?.() || null;
}
function get_embed_progress_state(env) {
  return env?.smart_sources?.entities_vector_adapter?.get_progress_state?.() || null;
}
function get_reimport_queue_count(env) {
  return Object.keys(env?.smart_sources?.sources_re_import_queue || {}).length;
}
function get_progress_pct(progress, total) {
  if (typeof progress !== "number" || typeof total !== "number") return null;
  if (total <= 0) return null;
  return Math.round(progress / total * 1e3) / 10;
}
function normalize_number(value) {
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
}
function to_non_empty_string(value) {
  return typeof value === "string" ? value.trim() : "";
}
function format_collection_label(collection_key = "") {
  return collection_key.split("_").filter(Boolean).map((part) => part.slice(0, 1).toUpperCase() + part.slice(1)).join(" ");
}
function get_collection_loading_state(env) {
  const collection_entries = Object.entries(env?.collections || {}).filter(([collection_key]) => Boolean(collection_key));
  const total = collection_entries.length;
  const loaded = collection_entries.filter(([, state]) => state === "loaded").length;
  const pending_entries = collection_entries.filter(([, state]) => state !== "loaded");
  const current_key = pending_entries[0]?.[0] || "";
  return {
    total,
    loaded,
    pending: Math.max(0, total - loaded),
    current_key,
    current_label: current_key ? format_collection_label(current_key) : ""
  };
}
function get_env_activity_state(env) {
  const import_progress = get_import_progress_state(env);
  const embed_progress = get_embed_progress_state(env);
  const reimport_queue_count = get_reimport_queue_count(env);
  const version4 = `v${env?.constructor?.version || ""}`;
  const default_message = `${env?.is_pro ? "Pro" : "Smart"} ${version4}`;
  if (import_progress?.active) {
    const progress = normalize_number(import_progress.progress);
    const total = normalize_number(import_progress.total);
    const stage = to_non_empty_string(import_progress.stage) || "importing";
    const is_reimporting = stage === "reimporting";
    return {
      kind: is_reimporting ? "reimporting" : "importing",
      message: `${is_reimporting ? "Re-importing" : "Importing"} ${progress}/${total}`,
      title: is_reimporting ? "Smart Environment is re-importing queued sources." : "Smart Environment is importing sources.",
      click_action: "noop",
      indicator_level: null,
      progress_value: progress,
      progress_total: total,
      progress_pct: get_progress_pct(progress, total),
      view_title: is_reimporting ? "Re-importing sources" : "Importing sources",
      view_status: `${progress}/${total}`,
      view_summary: is_reimporting ? "Refreshing queued source changes before embeddings continue." : "Discovering and importing sources into Smart Environment.",
      view_details: [
        reimport_queue_count > 0 ? `${reimport_queue_count} additional source${reimport_queue_count === 1 ? "" : "s"} queued.` : "",
        env?.state === "loading" ? "Smart Environment is still loading in the background." : ""
      ].filter(Boolean),
      view_actions: []
    };
  }
  if (embed_progress?.active) {
    const progress = normalize_number(embed_progress.progress);
    const total = normalize_number(embed_progress.total);
    const paused = Boolean(embed_progress.paused);
    const tokens_per_second = normalize_number(embed_progress.tokens_per_second);
    const model_name = to_non_empty_string(embed_progress.model_name);
    const reason = to_non_empty_string(embed_progress.reason);
    return {
      kind: paused ? "embed_paused" : "embed_active",
      message: `${paused ? "Embedding paused" : "Embedding"} ${progress}/${total}`,
      title: paused ? "Click to resume embedding." : "Click to pause embedding.",
      click_action: paused ? "resume_embed" : "pause_embed",
      indicator_level: paused ? "attention" : "milestone",
      progress_value: progress,
      progress_total: total,
      progress_pct: get_progress_pct(progress, total),
      view_title: paused ? "Embedding paused" : "Embedding in progress",
      view_status: `${progress}/${total}`,
      view_summary: model_name ? `Using ${model_name} for embeddings.` : "Generating embeddings for imported content.",
      view_details: [
        tokens_per_second > 0 ? `${tokens_per_second} tokens/sec` : "",
        reason,
        reimport_queue_count > 0 ? `${reimport_queue_count} source${reimport_queue_count === 1 ? "" : "s"} still queued for re-import.` : ""
      ].filter(Boolean),
      view_actions: [paused ? "resume_embed" : "pause_embed"]
    };
  }
  if (env?.state === "loading") {
    const collection_loading = get_collection_loading_state(env);
    const collection_progress_value = collection_loading.total > 0 ? collection_loading.loaded : null;
    const collection_progress_total = collection_loading.total > 0 ? collection_loading.total : null;
    const current_collection_label = collection_loading.current_label;
    const collection_status = collection_loading.total > 0 ? `${collection_loading.loaded}/${collection_loading.total}` : "In progress";
    return {
      kind: "loading",
      message: current_collection_label ? `Loading ${current_collection_label}\u2026` : "Loading Smart Env\u2026",
      title: current_collection_label ? `Smart Environment is loading ${current_collection_label}.` : "Smart Environment is loading.",
      click_action: "noop",
      indicator_level: null,
      progress_value: collection_progress_value,
      progress_total: collection_progress_total,
      progress_pct: get_progress_pct(collection_progress_value, collection_progress_total),
      view_title: "Loading Smart Environment",
      view_status: current_collection_label ? `${current_collection_label} \u2022 ${collection_status}` : collection_status,
      view_summary: current_collection_label ? `Loading collection: ${current_collection_label}.` : "Preparing collections, sources, and shared plugin state.",
      view_details: [
        collection_loading.total > 0 ? `${collection_loading.loaded} of ${collection_loading.total} collection${collection_loading.total === 1 ? "" : "s"} loaded.` : "",
        collection_loading.pending > 0 ? `${collection_loading.pending} collection${collection_loading.pending === 1 ? "" : "s"} remaining.` : "",
        reimport_queue_count > 0 ? `${reimport_queue_count} source${reimport_queue_count === 1 ? "" : "s"} queued for re-import after load.` : ""
      ].filter(Boolean),
      view_actions: []
    };
  }
  if (reimport_queue_count > 0) {
    return {
      kind: "reimport_queued",
      message: `Re-import (${reimport_queue_count})`,
      title: "Click to re-import queued sources.",
      click_action: "run_reimport",
      indicator_level: "attention",
      progress_value: null,
      progress_total: null,
      progress_pct: null,
      view_title: "Queued re-import work",
      view_status: `${reimport_queue_count} queued`,
      view_summary: "Run re-import to refresh changed sources and resume downstream embedding work.",
      view_details: [],
      view_actions: ["run_reimport"]
    };
  }
  if (env?.state === "loaded") {
    return {
      kind: "ready",
      message: default_message,
      title: "Smart Environment status",
      click_action: "context_menu",
      indicator_level: null,
      progress_value: null,
      progress_total: null,
      progress_pct: null,
      view_title: "Smart Environment ready",
      view_status: "Ready",
      view_summary: "No active import or embedding work is running right now.",
      view_details: [],
      view_actions: ["open_notifications"]
    };
  }
  return {
    kind: "not_loaded",
    message: default_message,
    title: "Smart Environment status",
    click_action: "context_menu",
    indicator_level: null,
    progress_value: null,
    progress_total: null,
    progress_pct: null,
    view_title: "Smart Environment not loaded",
    view_status: "Idle",
    view_summary: "Load Smart Environment to enable Smart Plugins.",
    view_details: [],
    view_actions: ["load_env"]
  };
}
function should_poll_env_activity(env) {
  const activity_state = get_env_activity_state(env);
  if (get_status_bar_notice_preview(env?.event_logs)) return true;
  return [
    "embed_active",
    "embed_paused",
    "importing",
    "reimporting",
    "loading",
    "not_loaded"
  ].includes(activity_state.kind);
}
function get_status_bar_state(env) {
  const notification_count = get_notification_event_count(env?.event_logs);
  const notification_indicator_level = get_notification_indicator_level(env?.event_logs);
  const activity_state = get_env_activity_state(env);
  const embed_queue_count = get_reimport_queue_count(env);
  const status_bar_notice_preview = get_status_bar_notice_preview(env?.event_logs);
  const can_show_notice_preview = Boolean(status_bar_notice_preview) && !["embed_active", "embed_paused", "importing", "reimporting", "loading"].includes(activity_state.kind);
  let title = activity_state.title;
  let indicator_level = activity_state.indicator_level;
  if (!indicator_level && notification_count > 0) {
    indicator_level = notification_indicator_level;
  }
  if (activity_state.kind === "ready" && notification_count > 0) {
    title = `${notification_count} unseen notification${notification_count === 1 ? "" : "s"}`;
  }
  if (can_show_notice_preview && status_bar_notice_preview?.title) {
    title = status_bar_notice_preview.title;
  }
  return {
    message: can_show_notice_preview ? status_bar_notice_preview.message : activity_state.message,
    title,
    indicator_count: notification_count,
    indicator_level,
    embed_queue_count,
    click_action: activity_state.click_action
  };
}
function get_entry_timestamp(entry) {
  if (typeof entry?.event?.at === "number") return entry.event.at;
  if (typeof entry?.at === "number") return entry.at;
  return Date.now();
}

// node_modules/obsidian-smart-env/src/components/env_status.js
function build_html4() {
  return `<div class="smart-env-status-view">
    <div class="smart-env-status-view__header">
      <div class="smart-env-status-view__eyebrow">Smart Environment</div>
      <h1 class="smart-env-status-view__title"></h1>
      <div class="smart-env-status-view__status" aria-live="polite"></div>
    </div>
    <div class="smart-env-status-view__summary"></div>
    <div
      class="smart-env-status-view__progress"
      role="progressbar"
      aria-valuemin="0"
      aria-valuemax="100"
      hidden
    >
      <div class="smart-env-status-view__progress-fill"></div>
    </div>
    <div class="smart-env-status-view__details"></div>
    <div class="smart-env-status-view__actions"></div>
  </div>`;
}
async function render5(env, params = {}) {
  this.apply_style_sheet(env_status_default);
  const frag = this.create_doc_fragment(build_html4());
  const container = frag.firstElementChild;
  post_process3.call(this, env, container, params);
  return container;
}
async function post_process3(env, container, params = {}) {
  const {
    live_updates = true,
    event = null,
    event_key = ""
  } = params;
  if (event_key) {
    container.dataset.eventKey = event_key;
  }
  if (typeof event?.level === "string" && event.level.trim()) {
    container.dataset.eventLevel = event.level.trim();
  }
  const title_el = container.querySelector(".smart-env-status-view__title");
  const status_el = container.querySelector(".smart-env-status-view__status");
  const summary_el = container.querySelector(".smart-env-status-view__summary");
  const progress_el = container.querySelector(".smart-env-status-view__progress");
  const progress_fill_el = container.querySelector(".smart-env-status-view__progress-fill");
  const details_el = container.querySelector(".smart-env-status-view__details");
  const actions_el = container.querySelector(".smart-env-status-view__actions");
  const render_state = () => {
    const view_state = get_env_activity_state(env);
    set_text(title_el, view_state.view_title);
    set_text(status_el, view_state.view_status, { hide_empty: true });
    set_text(summary_el, view_state.view_summary, { hide_empty: true });
    render_progress(progress_el, progress_fill_el, view_state);
    render_details(details_el, view_state.view_details);
    render_actions(actions_el, env, view_state.view_actions, {
      event,
      event_key
    });
  };
  let polling_interval = null;
  const set_polling = (active) => {
    if (active) {
      if (polling_interval) return;
      polling_interval = setInterval(() => {
        render_state();
        set_polling(should_poll_env_activity(env));
      }, 1e3);
      return;
    }
    if (!polling_interval) return;
    clearInterval(polling_interval);
    polling_interval = null;
  };
  let debounce_timeout = null;
  const refresh = () => {
    if (debounce_timeout) clearTimeout(debounce_timeout);
    debounce_timeout = setTimeout(() => {
      debounce_timeout = null;
      render_state();
      set_polling(should_poll_env_activity(env));
    }, 100);
  };
  render_state();
  if (!live_updates) return container;
  set_polling(should_poll_env_activity(env));
  const disposers = [];
  if (typeof env?.events?.on === "function") {
    disposers.push(env.events.on("*", refresh));
  }
  disposers.push(() => {
    set_polling(false);
    if (debounce_timeout) clearTimeout(debounce_timeout);
  });
  this.attach_disposer(container, disposers);
  return container;
}
function set_text(element, value, params = {}) {
  if (!element) return;
  const { hide_empty = false } = params;
  const text = typeof value === "string" ? value : "";
  element.textContent = text;
  if (hide_empty) element.hidden = !text;
}
function render_progress(progress_el, progress_fill_el, view_state) {
  const progress_pct = typeof view_state.progress_pct === "number" ? Math.max(0, Math.min(100, view_state.progress_pct)) : null;
  if (progress_pct === null) {
    progress_el.hidden = true;
    progress_fill_el.style.width = "0%";
    progress_el.removeAttribute("aria-valuenow");
    progress_el.setAttribute("aria-valuemax", "100");
    return;
  }
  progress_el.hidden = false;
  progress_fill_el.style.width = `${progress_pct}%`;
  if (typeof view_state.progress_value === "number") {
    progress_el.setAttribute("aria-valuenow", String(view_state.progress_value));
  } else {
    progress_el.removeAttribute("aria-valuenow");
  }
  progress_el.setAttribute(
    "aria-valuemax",
    String(typeof view_state.progress_total === "number" && view_state.progress_total > 0 ? view_state.progress_total : 100)
  );
}
function render_details(details_el, details = []) {
  details_el.replaceChildren();
  details.filter(Boolean).forEach((detail) => {
    const detail_el = details_el.ownerDocument.createElement("div");
    detail_el.className = "smart-env-status-view__detail";
    detail_el.textContent = detail;
    details_el.appendChild(detail_el);
  });
  details_el.hidden = details_el.childElementCount === 0;
}
function render_actions(actions_el, env, action_keys = [], params = {}) {
  actions_el.replaceChildren();
  action_keys.forEach((action_key) => {
    const btn = actions_el.ownerDocument.createElement("button");
    btn.type = "button";
    btn.className = "smart-env-status-view__btn";
    bind_action_button(btn, env, action_key, params);
    actions_el.appendChild(btn);
  });
  actions_el.hidden = actions_el.childElementCount === 0;
}
function get_action_label(action_key) {
  switch (action_key) {
    case "load_env":
      return "Load Smart Environment";
    case "pause_embed":
      return "Pause embedding";
    case "resume_embed":
      return "Resume embedding";
    case "run_reimport":
      return "Run re-import";
    case "open_notifications":
      return "Open events feed";
    default:
      return action_key;
  }
}
function bind_action_button(btn, env, action_key, params = {}) {
  const label = get_action_label(action_key);
  btn.textContent = label;
  if (["load_env", "resume_embed", "run_reimport"].includes(action_key)) {
    btn.classList.add("smart-env-status-view__btn--primary");
  }
  btn.addEventListener("click", async () => {
    await run_action_key(env, action_key, params);
  });
}
async function run_action_key(env, action_key, params = {}) {
  switch (action_key) {
    case "load_env":
      if (typeof env?.start_mobile_env_load === "function") {
        await env.start_mobile_env_load({
          source: "env_status_component",
          open_progress_view: false,
          event: params.event,
          event_key: params.event_key
        });
        return;
      }
      await env?.load?.(true);
      return;
    case "pause_embed":
      env?.smart_sources?.entities_vector_adapter?.halt_embed_queue_processing?.();
      return;
    case "resume_embed":
      env?.smart_sources?.entities_vector_adapter?.resume_embed_queue_processing?.();
      return;
    case "run_reimport":
      await env?.run_re_import?.();
      return;
    case "open_notifications":
      env?.open_notifications_feed_modal?.();
      return;
    default:
      return;
  }
}

// node_modules/obsidian-smart-env/src/components/lean_coffee_callout.js
var import_obsidian14 = require("obsidian");
function build_html5(env, opts = {}) {
  return `<div class="wrapper">
    <div id="lean-coffee-callout" data-callout-metadata="" data-callout-fold="" data-callout="info" class="callout" style="mix-blend-mode: unset;">
      <div class="callout-title" style="align-items: center;">
        <div class="callout-icon">
          <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"
            viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
            stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-info">
            <circle cx="12" cy="12" r="10"></circle>
            <path d="M12 16v-4"></path>
            <path d="M12 8h.01"></path>
          </svg>
        </div>
        <div class="callout-title-inner">
          <strong>Community Lean Coffee</strong>
        </div>
      </div>
      <div class="callout-content">
        <p dir="auto">
          <span>Ask questions. Bring challenges. Request features. Show workflows. Be ready to share.</span>
          <br>
          <i>Join the next <a href="https://lu.ma/calendar/cal-ZJtdnzAdURyouM7" target="_external">Community Lean Coffee</a> meeting.</i> Unable to attend? Submit a question <a href="https://docs.google.com/forms/d/e/1FAIpQLSdqOtTjksMwg1BOuGNCncpMQ_QT-wcd-3AgZGIe3A_isut5aQ/viewform?usp=dialog" target="_external">here</a> \u{1F334}
        </p>
      </div>
    </div>
  </div>`;
}
function render6(env, opts = {}) {
  const html = build_html5.call(this, env, opts);
  const frag = this.create_doc_fragment(html);
  const callout = frag.querySelector("#lean-coffee-callout");
  const icon_container = callout.querySelector(".callout-icon");
  const icon = (0, import_obsidian14.getIcon)("smart-chat");
  if (icon) {
    this.empty(icon_container);
    icon_container.appendChild(icon);
  }
  post_process4.call(this, env, callout, opts);
  return callout;
}
function post_process4(env, callout) {
}

// node_modules/obsidian-smart-env/src/components/milestone_notification.js
var import_obsidian15 = require("obsidian");

// node_modules/obsidian-smart-env/src/components/milestone_notification.css
var milestone_notification_default = ".notice:has(.smart-env-milestone-notice) {\n  padding: 0;\n  border: none;\n  background: transparent;\n  box-shadow: none;\n  min-width: 0;\n  max-width: min(460px, calc(100vw - 2rem));\n}\n\n.notice-message:has(.smart-env-milestone-notice) {\n  padding: 0;\n  background: transparent;\n}\n\n.smart-env-milestone-notice {\n  width: 100%;\n  min-width: 0;\n  font-family: var(--font-interface);\n}\n\n.smart-env-milestone-notice__surface {\n  display: grid;\n  grid-template-columns: auto 1fr;\n  gap: var(--size-4-3);\n  align-items: start;\n\n  width: 100%;\n  box-sizing: border-box;\n  padding: var(--size-4-4);\n  border: 1px solid var(--background-modifier-border);\n  border-inline-start: 3px solid var(--color-accent);\n  border-radius: var(--radius-xl);\n  background: var(--background-secondary);\n}\n\n.smart-env-milestone-notice__icon {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  align-self: start;\n\n  width: calc(var(--icon-l) + var(--size-4-6));\n  height: calc(var(--icon-l) + var(--size-4-6));\n  padding: var(--size-2-2);\n\n  color: var(--color-accent);\n  background: var(--background-primary);\n  border: 1px solid var(--background-modifier-border);\n  border-radius: var(--radius-l);\n}\n\n.smart-env-milestone-notice__icon svg {\n  width: var(--icon-l);\n  height: var(--icon-l);\n}\n\n.smart-env-milestone-notice__body {\n  min-width: 0;\n}\n\n.smart-env-milestone-notice__eyebrow {\n  margin: 0 0 var(--size-2-1);\n  color: var(--text-muted);\n  font-size: var(--font-ui-small);\n  font-weight: var(--font-semibold);\n  letter-spacing: 0.08em;\n  line-height: 1.1;\n  text-transform: uppercase;\n}\n\n.smart-env-milestone-notice__title {\n  color: var(--text-normal);\n  font-size: var(--h3-size);\n  font-weight: var(--font-semibold);\n  line-height: 1.15;\n  white-space: pre-wrap;\n}\n\n.smart-env-milestone-notice__details {\n  margin-top: var(--size-2-3);\n  color: var(--text-muted);\n  font-size: var(--font-text-size);\n  line-height: 1.42;\n  white-space: pre-wrap;\n}\n\n.smart-env-milestone-notice__actions {\n  display: flex;\n  align-items: center;\n  gap: var(--size-4-2);\n  flex-wrap: wrap;\n  margin-top: var(--size-4-3);\n}\n\n.smart-env-milestone-notice__button {\n  margin: 0;\n}\n\n.smart-env-milestone-notice__link {\n  color: var(--text-accent, var(--color-accent));\n  font-size: var(--font-ui-small);\n  text-decoration: underline;\n  text-decoration-thickness: 1px;\n  text-underline-offset: 2px;\n}\n\n@media (max-width: 520px) {\n  .notice:has(.smart-env-milestone-notice) {\n    max-width: calc(100vw - 1.2rem);\n  }\n\n  .smart-env-milestone-notice__surface {\n    grid-template-columns: 1fr;\n    gap: var(--size-4-2);\n    padding: var(--size-4-3);\n  }\n}\n";

// node_modules/obsidian-smart-env/src/components/milestone_notification.js
function get_milestone_notice_title(event = {}) {
  if (typeof event?.message === "string" && event.message.trim()) return event.message.trim();
  return "You achieved a new Smart Milestone";
}
function get_milestone_notice_details(event = {}) {
  if (typeof event?.details === "string" && event.details.trim()) return event.details.trim();
  if (typeof event?.milestone === "string" && event.milestone.trim()) return event.milestone.trim();
  return "";
}
function get_milestone_notice_summary(event = {}) {
  const title = get_milestone_notice_title(event);
  const details = get_milestone_notice_details(event);
  if (!details) return title;
  return `${title}
${details}`;
}
function to_trimmed_string3(value) {
  return typeof value === "string" ? value.trim() : "";
}
function render_icon2(icon_el) {
  if (!icon_el) return;
  try {
    (0, import_obsidian15.setIcon)(icon_el, "sparkles");
  } catch (_error) {
  }
  if (!icon_el.querySelector("svg")) {
    icon_el.textContent = "\u2605";
  }
}
function open_notifications_feed2(env, params = {}) {
  const {
    on_open_feed = null,
    event_key = "",
    event = {}
  } = params;
  const open_params = {
    event_key,
    event
  };
  if (typeof on_open_feed === "function") {
    return on_open_feed(open_params) !== false;
  }
  if (typeof env?.open_notifications_feed_modal === "function") {
    env.open_notifications_feed_modal(open_params);
    return true;
  }
  return false;
}
function render7(env, params = {}) {
  const {
    event_key = "",
    event = {},
    on_action = null,
    on_open_feed = null
  } = params;
  const title = get_milestone_notice_title(event);
  const details = get_milestone_notice_details(event);
  const btn_text = to_trimmed_string3(event?.btn_text);
  const btn_callback = to_trimmed_string3(event?.btn_callback);
  const help_link = to_trimmed_string3(event?.link);
  if (typeof document === "undefined") {
    return get_milestone_notice_summary(event);
  }
  this.apply_style_sheet?.(milestone_notification_default);
  const run_action = typeof on_action === "function" ? on_action : (callback_key) => dispatch_notice_action(env, callback_key, {
    event_source: "milestone_notification",
    source_event_key: event_key,
    source_event: event
  });
  const can_open_feed = typeof on_open_feed === "function" || typeof env?.open_notifications_feed_modal === "function";
  const can_view_more = can_open_feed && Boolean(event_key);
  const frag = document.createDocumentFragment();
  const wrapper = document.createElement("div");
  wrapper.className = "smart-env-milestone-notice";
  const surface_el = document.createElement("div");
  surface_el.className = "smart-env-milestone-notice__surface";
  const icon_el = document.createElement("div");
  icon_el.className = "smart-env-milestone-notice__icon";
  render_icon2(icon_el);
  const body_el = document.createElement("div");
  body_el.className = "smart-env-milestone-notice__body";
  const eyebrow_el = document.createElement("div");
  eyebrow_el.className = "smart-env-milestone-notice__eyebrow";
  eyebrow_el.textContent = "Smart Milestone";
  const title_el = document.createElement("div");
  title_el.className = "smart-env-milestone-notice__title";
  title_el.textContent = title;
  body_el.appendChild(eyebrow_el);
  body_el.appendChild(title_el);
  if (details) {
    const details_el = document.createElement("div");
    details_el.className = "smart-env-milestone-notice__details";
    details_el.textContent = details;
    body_el.appendChild(details_el);
  }
  const should_render_actions = Boolean(btn_text && btn_callback) || Boolean(help_link) || can_view_more;
  if (should_render_actions) {
    const actions_el = document.createElement("div");
    actions_el.className = "smart-env-milestone-notice__actions";
    if (btn_text && btn_callback) {
      const button_el = document.createElement("button");
      button_el.type = "button";
      button_el.className = "smart-env-milestone-notice__button mod-cta";
      button_el.textContent = btn_text;
      button_el.setAttribute("aria-label", btn_text);
      button_el.addEventListener("click", () => {
        run_action(btn_callback);
      });
      actions_el.appendChild(button_el);
    }
    if (can_view_more) {
      const view_more_btn_el = document.createElement("button");
      view_more_btn_el.type = "button";
      view_more_btn_el.className = "smart-env-milestone-notice__button";
      view_more_btn_el.textContent = "View more";
      view_more_btn_el.setAttribute("aria-label", "Open this event in the notifications feed");
      view_more_btn_el.addEventListener("click", () => {
        open_notifications_feed2(env, {
          on_open_feed,
          event_key,
          event
        });
      });
      actions_el.appendChild(view_more_btn_el);
    }
    if (help_link) {
      const help_el = document.createElement("a");
      help_el.className = "smart-env-milestone-notice__link";
      help_el.href = help_link;
      help_el.textContent = "Learn more";
      help_el.target = "_external";
      help_el.rel = "noopener noreferrer";
      help_el.setAttribute("aria-label", "Learn more about this milestone");
      actions_el.appendChild(help_el);
    }
    body_el.appendChild(actions_el);
  }
  surface_el.appendChild(icon_el);
  surface_el.appendChild(body_el);
  wrapper.appendChild(surface_el);
  frag.appendChild(wrapper);
  return frag;
}

// node_modules/obsidian-smart-env/src/components/milestones.css
var milestones_default = `.sc-events-checklist {\r
  display: flex;\r
  flex-direction: column;\r
  gap: var(--size-4-3);\r
  padding: var(--size-4-2);\r
\r
  .sc-events-checklist__header {\r
    display: flex;\r
    align-items: baseline;\r
    justify-content: space-between;\r
    gap: var(--size-4-3);\r
  }\r
\r
  .sc-events-checklist__title {\r
    margin: 0;\r
    font-size: var(--h2-size);\r
  }\r
\r
  .sc-events-checklist__summary {\r
    font-size: var(--font-ui-small);\r
    color: var(--text-normal);\r
    font-variant-numeric: tabular-nums;\r
\r
    padding: 0.15em 0.6em;\r
    border-radius: 999px;\r
    background-color: var(--background-secondary);\r
    border: 1px solid var(--background-modifier-border);\r
    white-space: nowrap;\r
  }\r
\r
  .sc-events-checklist__hint {\r
    font-size: var(--font-ui-small);\r
    color: var(--text-muted);\r
    text-align: right;\r
  }\r
\r
  .sc-events-checklist__progress {\r
    height: 6px;\r
    border-radius: 999px;\r
    overflow: hidden;\r
    background-color: var(--background-secondary);\r
    border: 1px solid var(--background-modifier-border);\r
  }\r
\r
  .sc-events-checklist__progress-fill {\r
    height: 100%;\r
    width: var(--sc-events-checklist-progress, 0%);\r
    background-color: var(--color-green, var(--color-accent));\r
  }\r
\r
  .sc-events-checklist__group {\r
    border-top: 1px solid var(--background-modifier-border);\r
    padding-top: var(--size-4-3);\r
  }\r
\r
  .sc-events-checklist__group-title {\r
    margin: 0 0 var(--size-4-2) 0;\r
    font-size: var(--h3-size);\r
\r
    display: flex;\r
    align-items: baseline;\r
    justify-content: space-between;\r
    gap: var(--size-4-2);\r
  }\r
\r
  .sc-events-checklist__group-name {\r
    display: inline-flex;\r
    align-items: center;\r
    min-width: 0;\r
  }\r
\r
  .sc-events-checklist__group-count {\r
    font-size: var(--font-ui-small);\r
    color: var(--text-muted);\r
    white-space: nowrap;\r
    font-variant-numeric: tabular-nums;\r
    flex: 0 0 auto;\r
  }\r
\r
  /*\r
    Group completion badge:\r
    - shows only when every item in the group is checked\r
    - uses :has() (no JS needed)\r
    - avoids false positives on empty groups by requiring at least one item\r
  */\r
  .sc-events-checklist__group:has(.sc-events-checklist__item):not(:has(.sc-events-checklist__item[data-checked='false'])) {\r
    .sc-events-checklist__group-name::after {\r
      content: "DONE";\r
\r
      /* layout */\r
      display: inline-flex;\r
      align-items: center;\r
      justify-content: center;\r
      margin-left: 0.5em;\r
      padding: 0.08em 0.55em;\r
      border-radius: 999px;\r
      white-space: nowrap;\r
      vertical-align: middle;\r
\r
      /* typography */\r
      font-size: 0.65em;\r
      font-weight: 700;\r
      letter-spacing: 0.12em;\r
      text-transform: uppercase;\r
\r
      /* theme vars (with safe fallback) */\r
      color: var(--color-green, var(--text-accent));\r
      background-color: var(--background-secondary);\r
      border: 1px solid var(--background-modifier-border);\r
\r
      /* subtle depth, theme-aware */\r
      box-shadow:\r
        0 0 0 1px var(--background-primary),\r
        0 1px 3px rgba(0, 0, 0, 0.25);\r
      transform: translateY(-0.03em);\r
    }\r
  }\r
\r
  .sc-events-checklist__items {\r
    list-style: none;\r
    padding: 0;\r
    margin: 0;\r
    display: flex;\r
    flex-direction: column;\r
    gap: var(--size-2-2);\r
  }\r
\r
  .sc-events-checklist__item {\r
    display: flex;\r
    flex-direction: column;\r
    gap: 2px;\r
    padding: 6px 8px;\r
    border-radius: var(--radius-s);\r
\r
    cursor: pointer;\r
    border: 1px solid transparent;\r
\r
    transition:\r
      background-color 120ms ease,\r
      border-color 120ms ease,\r
      transform 120ms ease;\r
\r
    &:hover {\r
      background: var(--background-modifier-hover);\r
      border-color: var(--background-modifier-border);\r
    }\r
\r
    &:active {\r
      transform: translateY(1px);\r
    }\r
\r
    &:focus-visible {\r
      outline: 2px solid var(--color-accent);\r
      outline-offset: 2px;\r
      background: var(--background-modifier-hover);\r
      border-color: var(--color-accent);\r
    }\r
  }\r
\r
  .sc-events-checklist__label {\r
    display: flex;\r
    align-items: flex-start;\r
    gap: var(--size-2-2);\r
    cursor: pointer;\r
  }\r
\r
  .sc-events-checklist__icon {\r
    display: inline-flex;\r
    align-items: center;\r
    justify-content: center;\r
    margin-top: 2px;\r
    width: 18px;\r
    height: 18px;\r
    flex: 0 0 auto;\r
    color: var(--text-muted);\r
\r
    svg {\r
      width: 18px;\r
      height: 18px;\r
    }\r
  }\r
\r
  .sc-events-checklist__milestone {\r
    line-height: 1.3;\r
    user-select: text;\r
    cursor: text;\r
  }\r
\r
  .sc-events-checklist__item[data-checked='true'] {\r
    .sc-events-checklist__icon {\r
      color: var(--color-green, var(--text-accent));\r
    }\r
\r
    .sc-events-checklist__milestone {\r
      color: var(--text-normal);\r
    }\r
  }\r
}\r
\r
/* 1) Host elements that should get a PRO badge */\r
.sc-events-checklist__label.pro-milestone > .sc-events-checklist__milestone {\r
  position: relative; /* safe default, keeps ::after anchored */\r
}\r
\r
/* 2) The PRO badge itself */\r
.sc-events-checklist__label.pro-milestone > .sc-events-checklist__milestone::after {\r
  content: "PRO";\r
\r
  /* layout */\r
  display: inline-flex;\r
  align-items: center;\r
  justify-content: center;\r
  margin-left: 0.4em;\r
  padding: 0.08em 0.55em;\r
  border-radius: 999px;\r
  white-space: nowrap;\r
  vertical-align: middle;\r
\r
  /* typography */\r
  font-size: 0.7em;\r
  font-weight: 600;\r
  letter-spacing: 0.14em;\r
  text-transform: uppercase;\r
  line-height: 1;\r
\r
  /* color system: only Obsidian variables */\r
  background-color: var(--color-accent);\r
  background-image: linear-gradient(\r
    135deg,\r
    var(--color-accent),\r
    var(--interactive-accent-hover)\r
  );\r
  color: var(--text-on-accent, var(--background-primary));\r
  border: 1px solid var(--background-modifier-border);\r
\r
  /* subtle separation & depth, theme-aware */\r
  box-shadow:\r
    0 0 0 1px var(--background-primary),\r
    0 1px 3px rgba(0, 0, 0, 0.35);\r
  transform: translateY(-0.03em);\r
}\r
\r
/* 3) Interactive refinement: follow Obsidian's accent hover behavior */\r
.sc-events-checklist__label.pro-milestone > .sc-events-checklist__milestone:hover::after {\r
  background-color: var(--interactive-accent-hover);\r
  filter: brightness(1.05);\r
}\r
\r
/* Milestones modal: title row help icon */\r
.sc-milestones-modal__title {\r
  width: 100%;\r
}\r
\r
.sc-milestones-modal__title-row {\r
  display: flex;\r
  align-items: center;\r
  gap: var(--size-4-2);\r
  width: 100%;\r
}\r
\r
.sc-milestones-modal__title-text {\r
  min-width: 0;\r
}\r
\r
.sc-milestones-modal__help-btn {\r
  display: inline-flex;\r
  align-items: center;\r
  justify-content: center;\r
\r
  width: 28px;\r
  height: 28px;\r
  padding: 0;\r
  border-radius: var(--radius-s);\r
\r
  background: transparent;\r
  border: 1px solid transparent;\r
  color: var(--text-muted);\r
\r
  cursor: pointer;\r
}\r
\r
.sc-milestones-modal__help-btn svg {\r
  width: 18px;\r
  height: 18px;\r
}\r
\r
.sc-milestones-modal__help-btn:hover {\r
  background: var(--background-modifier-hover);\r
  border-color: var(--background-modifier-border);\r
  color: var(--text-normal);\r
}\r
\r
.sc-milestones-modal__help-btn:active {\r
  transform: translateY(1px);\r
}\r
\r
.sc-milestones-modal__help-btn:focus-visible {\r
  outline: 2px solid var(--color-accent);\r
  outline-offset: 2px;\r
  background: var(--background-modifier-hover);\r
  border-color: var(--color-accent);\r
}\r
`;

// node_modules/obsidian-smart-env/src/components/milestones.js
var import_obsidian16 = require("obsidian");

// node_modules/obsidian-smart-env/src/utils/onboarding_events_data.js
var PLUGIN_INSTALL_EVENT_CONFIG = {
  "connections:installed": {
    ids: ["smart-connections"]
  },
  "connections_pro:installed": {
    ids: ["smart-connections"],
    require_pro_name: true
  },
  "context:installed": {
    ids: ["smart-context"]
  },
  "context_pro:installed": {
    ids: ["smart-context"],
    require_pro_name: true
  },
  "chat:installed": {
    ids: ["smart-chatgpt", "smart-chat"]
  },
  "chat_pro:installed": {
    ids: ["smart-chat"],
    require_pro_name: true
  }
};
var EVENTS_CHECKLIST_ITEMS_BY_EVENT_KEY = {
  // Environment
  "sources:import_completed": {
    group: "Environment",
    milestone: "Initial vault import completed (all sources discovered).",
    link: "https://smartconnections.app/smart-environment/settings/?utm_source=milestones#sources"
  },
  "embedding:completed": {
    group: "Environment",
    milestone: "Initial embedding completed, you are ready to make connections!",
    link: "https://smartconnections.app/smart-environment/settings/?utm_source=milestones#embedding-models"
  },
  // Connections
  "connections:installed": {
    group: "Connections",
    milestone: "Installed Smart Connections (core plugin).",
    link: "https://smartconnections.app/smart-connections/list-feature/?utm_source=milestones"
  },
  "connections:opened": {
    group: "Connections",
    milestone: "Opened the connections view.",
    link: "https://smartconnections.app/smart-connections/list-feature/?utm_source=milestones#quick-start"
  },
  "connections:drag_result": {
    group: "Connections",
    milestone: "Dragged a Smart Connections result into a note to create a link.",
    link: "https://smartconnections.app/smart-connections/list-feature/?utm_source=milestones#drag-link"
  },
  "connections:open_result": {
    group: "Connections",
    milestone: "Opened a Smart Connections result from the UI (list item or inline popover).",
    link: "https://smartconnections.app/smart-connections/list-feature/?utm_source=milestones#core-interactions"
  },
  "connections:sent_to_context": {
    group: "Connections",
    milestone: "Sent Connections results to Smart Context (turn discovery into a context pack).",
    link: "https://smartconnections.app/smart-connections/list-feature/?utm_source=milestones#send-to-context"
  },
  "connections:copied_list": {
    group: "Connections",
    milestone: "Copied Connections results as a list of links.",
    link: "https://smartconnections.app/smart-connections/list-feature/?utm_source=milestones#copy-list"
  },
  "connections:hover_preview": {
    group: "Connections",
    milestone: "Previewed a connection by holding cmd/ctrl while hovering the result.",
    link: "https://smartconnections.app/smart-connections/list-feature/?utm_source=milestones#core-interactions"
  },
  "connections:open_random": {
    group: "Connections",
    milestone: "Opened a random connection from Smart Connections.",
    link: "https://smartconnections.app/smart-connections/getting-started/?utm_source=milestones#open-a-random-connection"
  },
  "connections:hidden_item": {
    group: "Connections",
    milestone: "Hidden a connection item from the list.",
    link: "https://smartconnections.app/smart-connections/list-feature/?utm_source=milestones#manage-noise"
  },
  "connections:pinned_item": {
    group: "Connections",
    milestone: "Pinned a connection item in the list.",
    link: "https://smartconnections.app/smart-connections/list-feature/?utm_source=milestones#manage-noise"
  },
  // Connections Pro
  "connections_pro:installed": {
    group: "Connections Pro",
    milestone: "Installed Smart Connections Pro.",
    link: "https://smartconnections.app/pro-plugins/?utm_source=milestones#connections-pro",
    is_pro: true
  },
  // Lookup
  "lookup:hover_preview": {
    group: "Lookup",
    milestone: "Previewed a Smart Lookup result by holding cmd/ctrl while hovering.",
    link: "https://smartconnections.app/smart-connections/lookup/?utm_source=milestones#understanding-results"
  },
  "lookup:get_results": {
    group: "Lookup",
    milestone: "Submitted a lookup query (started a semantic search).",
    link: "https://smartconnections.app/smart-connections/lookup/?utm_source=milestones"
  },
  "lookup:drag_result": {
    group: "Lookup",
    milestone: "Dragged a Smart Lookup result into a note to create a link.",
    link: "https://smartconnections.app/smart-connections/lookup/?utm_source=milestones#understanding-results"
  },
  "lookup:open_result": {
    group: "Lookup",
    milestone: "Opened a Lookup result.",
    link: "https://smartconnections.app/smart-connections/lookup/?utm_source=milestones#understanding-results"
  },
  // Context
  "context:created": {
    group: "Context",
    milestone: "First context created!",
    link: "https://smartconnections.app/smart-context/builder/?utm_source=milestones#quick-start"
  },
  "context:copied": {
    group: "Context",
    milestone: "Copied context to clipboard.",
    link: "https://smartconnections.app/smart-context/clipboard/?utm_source=milestones#copy-current"
  },
  "context:file_nav_copied": {
    group: "Context",
    milestone: "Copied context from the file navigator.",
    link: "https://smartconnections.app/smart-context/clipboard/?utm_source=milestones#copy-selected"
  },
  "context_selector:open": {
    group: "Context",
    milestone: "Opened the Context Builder selector modal.",
    link: "https://smartconnections.app/smart-context/builder/?utm_source=milestones#open-builder"
  },
  "context:named": {
    group: "Context",
    milestone: "Named a Smart Context (created a reusable saved context).",
    link: "https://smartconnections.app/smart-context/builder/?utm_source=milestones#save-reuse"
  },
  "context:renamed": {
    group: "Context",
    milestone: "Renamed a Smart Context (increased clarity).",
    link: "https://smartconnections.app/smart-context/builder/?utm_source=milestones#save-reuse"
  },
  "context:installed": {
    group: "Context",
    milestone: "Installed Smart Context (core plugin).",
    link: "https://smartconnections.app/smart-context/builder/?utm_source=milestones"
  },
  "context:copied_with_media": {
    group: "Context Pro",
    milestone: "Copied context with media (images/PDF pages) for multimodal workflows.",
    link: "https://smartconnections.app/smart-context/clipboard/?utm_source=milestones#copy-modes",
    is_pro: true
  },
  "context:custom_template_set": {
    group: "Context Pro",
    milestone: "Set a custom context template.",
    link: "https://smartconnections.app/smart-context/settings/?utm_source=milestones#context-templates",
    is_pro: true
  },
  "context_item:custom_template_set": {
    group: "Context Pro",
    milestone: "Set a custom context item template.",
    link: "https://smartconnections.app/smart-context/settings/?utm_source=milestones#item-templates",
    is_pro: true
  },
  // Context Pro
  "context_pro:installed": {
    group: "Context Pro",
    milestone: "Installed Smart Context Pro.",
    link: "https://smartconnections.app/pro-plugins/?utm_source=milestones#context-pro",
    is_pro: true
  },
  // Chat
  "chat:installed": {
    group: "Chat",
    milestone: "Installed Smart ChatGPT.",
    link: "https://smartconnections.app/smart-chat/?utm_source=milestones"
  },
  "chat_codeblock:saved_thread": {
    group: "Chat",
    milestone: "Started a chat in a Smart Chat codeblock (opened the loop).",
    link: "https://smartconnections.app/smart-chat/codeblock/?utm_source=milestones#quick-start"
  },
  "chat_codeblock:marked_active": {
    group: "Chat",
    milestone: "Marked a chat thread as active from the Smart Chat inbox.",
    link: "https://smartconnections.app/smart-chat/codeblock/?utm_source=milestones#chat-inbox"
  },
  "chat_codeblock:marked_done": {
    group: "Chat",
    milestone: "Marked the chat thread as done (closed the loop).",
    link: "https://smartconnections.app/smart-chat/codeblock/?utm_source=milestones#chat-inbox"
  },
  // Chat Pro
  "chat_pro:installed": {
    group: "Chat Pro",
    milestone: "Installed Smart Chat Pro.",
    link: "https://smartconnections.app/pro-plugins/?utm_source=milestones#chat-pro",
    is_pro: true
  },
  "completion:completed": {
    group: "Chat Pro",
    milestone: "Received the first Smart Chat response (a completion finished).",
    link: "https://smartconnections.app/smart-chat/api-integration/?utm_source=milestones#quick-start",
    is_pro: true
  },
  // Connections Pro (Inline Connections)
  "inline_connections:show": {
    group: "Connections Pro",
    milestone: "Opened inline connections in-note (used the inline workflow).",
    link: "https://smartconnections.app/smart-connections/inline/?utm_source=milestones",
    is_pro: true
  },
  "inline_connections:open_result": {
    group: "Connections Pro",
    milestone: "Opened an inline connections result (navigated from discovery to source).",
    link: "https://smartconnections.app/smart-connections/inline/?utm_source=milestones",
    is_pro: true
  },
  "inline_connections:drag_result": {
    group: "Connections Pro",
    milestone: "Inserted an inline link from an inline connection (converted discovery into a durable link).",
    link: "https://smartconnections.app/smart-connections/inline/?utm_source=milestones",
    is_pro: true
  },
  // Connect Pro
  "connect_pro:ping": {
    group: "Connect Pro",
    milestone: "Connect Pro ping observed (local route hit or tunnel health check ran).",
    link: "https://smartconnections.app/connect-pro/?utm_source=milestones#health-check",
    is_pro: true
  },
  "connect_pro:request": {
    group: "Connect Pro",
    milestone: "Connect Pro request received (remote action hit /obsidian-cli).",
    link: "https://smartconnections.app/connect-pro/?utm_source=milestones#request-flow",
    is_pro: true
  },
  // Pro
  "smart_plugins_oauth_completed": {
    group: "Pro",
    milestone: "Connected account (enabled Pro plugins).",
    link: "https://smartconnections.app/pro-plugins/?utm_source=milestones"
  },
  "referrals:copied_link": {
    group: "Pro",
    milestone: "Copied your referral link to share Pro.",
    link: "https://smartconnections.app/pro-plugins/?utm_source=milestones#referrals",
    is_pro: true
  },
  "referrals:opened_dashboard": {
    group: "Pro",
    milestone: "Opened the referrals dashboard to view bonuses.",
    link: "https://smartconnections.app/my-referrals/?utm_source=milestones",
    is_pro: true
  }
};
var EVENTS_CHECKLIST_GROUP_ORDER = [
  "Environment",
  "Connections",
  "Lookup",
  "Context",
  "Chat",
  "Connections Pro",
  "Context Pro",
  "Chat Pro",
  "Connect Pro",
  "Pro"
];
function derive_events_checklist_groups(items_by_event_key) {
  const group_map = Object.entries(items_by_event_key || {}).reduce(
    (acc, [event_key, item]) => {
      const group = item?.group || "Other";
      if (!acc[group]) acc[group] = [];
      acc[group].push({ event_key, group, milestone: item?.milestone || "", ...item });
      return acc;
    },
    /** @type {Record<string, Array<{event_key: string, group: string, milestone: string, link: string, is_pro?: boolean}>>} */
    {}
  );
  const all_groups = Object.keys(group_map);
  const order_index = EVENTS_CHECKLIST_GROUP_ORDER.reduce(
    (acc, name, idx) => {
      acc[name] = idx;
      return acc;
    },
    /** @type {Record<string, number>} */
    {}
  );
  const sorted_groups = all_groups.sort((a, b) => {
    const a_has = Object.prototype.hasOwnProperty.call(order_index, a);
    const b_has = Object.prototype.hasOwnProperty.call(order_index, b);
    if (a_has && b_has) return order_index[a] - order_index[b];
    if (a_has) return -1;
    if (b_has) return 1;
    return a.localeCompare(b);
  });
  return sorted_groups.map((group) => {
    const items = (group_map[group] || []).slice().sort((a, b) => a.event_key.localeCompare(b.event_key));
    return { group, items };
  });
}
function check_if_event_emitted(env, event_key) {
  const plugin_event_state = resolve_plugin_install_event(env, event_key);
  if (plugin_event_state === true) return true;
  if (env?.event_logs?.items?.[event_key]) return true;
  if (plugin_event_state === false) return false;
  return false;
}
function resolve_plugin_install_event(env, event_key) {
  const config = PLUGIN_INSTALL_EVENT_CONFIG[event_key];
  if (!config) return null;
  const manifests = env?.plugin?.app?.plugins?.manifests || {};
  const plugin_ids = Array.isArray(config.ids) ? config.ids : [];
  for (const plugin_id of plugin_ids) {
    const manifest = manifests[plugin_id];
    if (!manifest) continue;
    if (config.require_pro_name && !is_pro_manifest(manifest)) continue;
    return true;
  }
  return false;
}
function is_pro_manifest(manifest) {
  const name = manifest?.name;
  if (typeof name !== "string") return false;
  return name.toLowerCase().includes("pro");
}

// node_modules/obsidian-smart-env/src/utils/onboarding_events.js
function is_valid_milestone_event(event_key, params = {}) {
  const { items_by_event_key = {} } = params;
  if (typeof event_key !== "string" || event_key.length === 0) return false;
  return Boolean(items_by_event_key && event_key in items_by_event_key);
}
function register_first_of_event_notifications(env) {
  if (typeof env?.events?.on !== "function") {
    return () => {
    };
  }
  if (!(env._milestone_first_event_keys instanceof Set)) {
    env._milestone_first_event_keys = /* @__PURE__ */ new Set();
  }
  const handle_first_event = (data) => {
    const event_key = data?.first_of_event_key;
    if (!is_valid_milestone_event(event_key, { items_by_event_key: EVENTS_CHECKLIST_ITEMS_BY_EVENT_KEY })) return;
    if (env._milestone_first_event_keys.has(event_key)) return;
    const item = EVENTS_CHECKLIST_ITEMS_BY_EVENT_KEY[event_key];
    if (!item) return;
    env._milestone_first_event_keys.add(event_key);
    env.events.emit("milestones:first_achieved", {
      level: "milestone",
      message: "You achieved a new Smart Milestone",
      details: item.milestone,
      milestone: item.milestone,
      link: item.link,
      first_of_event_key: event_key,
      event_source: "onboarding_events",
      btn_text: "View milestones",
      btn_callback: "milestones_modal:open"
    });
  };
  const unsubscribe = env.events.on("event_log:first", handle_first_event);
  return () => {
    unsubscribe?.();
  };
}

// node_modules/obsidian-smart-env/src/components/milestones.js
function build_html6(env, params = {}) {
  const groups = derive_events_checklist_groups(EVENTS_CHECKLIST_ITEMS_BY_EVENT_KEY);
  const checked_count = groups.reduce((acc, g) => {
    const c = g.items.reduce((inner, item) => {
      return inner + (check_if_event_emitted(env, item.event_key) ? 1 : 0);
    }, 0);
    return acc + c;
  }, 0);
  const total_count = groups.reduce((acc, g) => acc + g.items.length, 0);
  const progress_pct = total_count > 0 ? Math.round(checked_count / total_count * 100) : 0;
  const groups_html = groups.map((group) => {
    const group_checked_count = group.items.reduce((acc, item) => {
      return acc + (check_if_event_emitted(env, item.event_key) ? 1 : 0);
    }, 0);
    const group_total_count = group.items.length;
    const items_html = group.items.map((item) => {
      const checked = check_if_event_emitted(env, item.event_key) === true;
      return build_item_html(item, { checked });
    }).join("\n");
    return `
      <section class="sc-events-checklist__group" data-group="${escape_html(group.group)}">
        <h3 class="sc-events-checklist__group-title">
          <span class="sc-events-checklist__group-name">${escape_html(group.group)}</span>
          <span class="sc-events-checklist__group-count" aria-label="Group completion">${group_checked_count.toString()} / ${group_total_count.toString()}</span>
        </h3>
        <ul class="sc-events-checklist__items">
          ${items_html}
        </ul>
      </section>
    `;
  }).join("\n");
  return `
    <div
      class="sc-events-checklist"
      data-component="events_checklist"
      style="--sc-events-checklist-progress: ${progress_pct.toString()}%;"
    >
      <div class="sc-events-checklist__header">
        <div class="sc-events-checklist__summary" aria-label="Checklist completion">
          ${checked_count.toString()} / ${total_count.toString()}
        </div>
        <div class="sc-events-checklist__hint" aria-hidden="true">
          Click a milestone to open docs
        </div>
      </div>

      <div
        class="sc-events-checklist__progress"
        role="progressbar"
        aria-label="Overall progress"
        aria-valuenow="${checked_count.toString()}"
        aria-valuemin="0"
        aria-valuemax="${total_count.toString()}"
        title="${escape_html(`${progress_pct.toString()}% complete`)}"
      >
        <div class="sc-events-checklist__progress-fill" aria-hidden="true"></div>
      </div>

      <div class="sc-events-checklist__body">
        ${groups_html}
      </div>
    </div>
  `;
}
async function render8(env, params = {}) {
  this.apply_style_sheet(milestones_default);
  const html = build_html6.call(this, env, params);
  const frag = this.create_doc_fragment(html);
  const container = frag.firstElementChild;
  post_process5.call(this, env, container, params);
  return container;
}
async function post_process5(env, container, params = {}) {
  attach_item_link_listeners(container);
  render_item_state_icons(container);
  return container;
}
function build_item_html(item, state) {
  const checked = state.checked === true;
  const checked_flag = checked ? "true" : "false";
  const link = typeof item.link === "string" ? item.link : "";
  const status_label = checked ? "Completed" : "Incomplete";
  const aria_label = `Open docs: ${item.milestone || item.event_key || "milestone"} (${status_label})`;
  return `
    <li
      class="sc-events-checklist__item"
      data-event-key="${escape_html(item.event_key)}"
      data-link="${escape_html(link)}"
      data-checked="${checked_flag}"
      tabindex="0"
      role="button"
      aria-label="${escape_html(aria_label)}"
    >
      <div class="sc-events-checklist__label${item.is_pro ? " pro-milestone" : ""}">
        <span class="sc-events-checklist__icon" aria-hidden="true"></span>
        <span class="sc-events-checklist__milestone">${escape_html(item.milestone)}</span>
      </div>
    </li>
  `;
}
function attach_item_link_listeners(container) {
  if (!container) return;
  if (container.getAttribute("data-links-enabled") === "true") return;
  container.setAttribute("data-links-enabled", "true");
  container.addEventListener("click", (evt) => {
    const item_el = get_item_el_from_event(container, evt);
    if (!item_el) return;
    const selection = window.getSelection();
    if (selection && selection.toString().length > 0) return;
    open_item_link(item_el);
  });
  container.addEventListener("keydown", (evt) => {
    const key = evt && /** @type {KeyboardEvent} */
    evt.key;
    if (key !== "Enter" && key !== " ") return;
    const item_el = get_item_el_from_event(container, evt);
    if (!item_el) return;
    evt.preventDefault();
    open_item_link(item_el);
  });
}
function open_item_link(item_el) {
  const link = get_item_link(item_el);
  if (typeof link === "string" && link.length > 0) {
    window.open(link, "_external");
  }
}
function render_item_state_icons(container) {
  if (!container) return;
  const item_els = Array.from(container.querySelectorAll(".sc-events-checklist__item"));
  item_els.forEach((item_el) => {
    const checked = item_el.getAttribute("data-checked") === "true";
    const icon_el = item_el.querySelector(".sc-events-checklist__icon");
    if (!icon_el) return;
    set_item_icon(
      /** @type {HTMLElement} */
      icon_el,
      checked
    );
  });
}
function set_item_icon(icon_el, checked) {
  const icon_ids = checked ? ["circle-check", "check-circle", "check"] : ["circle", "circle-dashed", "dot"];
  set_icon_with_fallback2(icon_el, icon_ids);
}
function set_icon_with_fallback2(icon_el, icon_ids) {
  if (!icon_el) return;
  const ids = Array.isArray(icon_ids) ? icon_ids : [];
  for (const icon_id of ids) {
    if (typeof icon_id !== "string" || icon_id.length === 0) continue;
    icon_el.textContent = "";
    try {
      (0, import_obsidian16.setIcon)(icon_el, icon_id);
    } catch (err) {
      continue;
    }
    if (icon_el.querySelector("svg")) return;
  }
}
function get_item_el_from_event(container, evt) {
  const target = evt && /** @type {any} */
  evt.target;
  if (!target || typeof target.closest !== "function") return null;
  const item_el = target.closest(".sc-events-checklist__item");
  if (!item_el) return null;
  if (!container.contains(item_el)) return null;
  return item_el;
}
function get_item_link(item_el) {
  const data_link = item_el.getAttribute("data-link");
  if (typeof data_link === "string" && data_link.length > 0) return data_link;
  const event_key = item_el.getAttribute("data-event-key");
  if (typeof event_key !== "string" || event_key.length === 0) return "";
  const item = EVENTS_CHECKLIST_ITEMS_BY_EVENT_KEY[event_key];
  if (!item || typeof item.link !== "string") return "";
  return item.link;
}

// node_modules/obsidian-smart-env/src/components/notification_feed.css
var notification_feed_default = `/* Smart Env notifications feed modal */

.smart-env-notifications-modal {
  width: min(900px, 92vw);
  max-height: min(820px, 88vh);
}

.smart-env-notifications-modal .modal-header {
  padding: var(--size-4-4) var(--size-4-4) var(--size-4-2);
}

.smart-env-notifications-modal .modal-title {
  font-weight: var(--font-semibold);
  letter-spacing: -0.01em;
}

.smart-env-notifications-modal .modal-content {
  padding: var(--size-4-3) var(--size-4-4) var(--size-4-4);
}

.smart-env-notifications {
  --smart-env-notifications-toolbar-radius: calc(var(--radius-l) + var(--size-2-2));
  --smart-env-notifications-filter-radius: 999px;
  --smart-env-notifications-card-radius: calc(var(--radius-m) + var(--size-2-3));
  --smart-env-notifications-code-radius: var(--radius-m);
  --smart-env-notifications-empty-radius: var(--radius-l);
  --smart-env-notifications-shadow: var(--shadow-s);
  --smart-env-notifications-shadow-hover: var(--shadow-l);
  --smart-env-notifications-surface: linear-gradient(
    180deg,
    color-mix(in srgb, var(--background-primary) 96%, var(--background-secondary)),
    color-mix(in srgb, var(--background-primary) 89%, var(--background-secondary))
  );
  --smart-env-notifications-muted-accent: var(--status-bar-text-color, var(--text-muted));

  display: flex;
  flex-direction: column;
  gap: var(--size-4-3);
  min-width: 0;
  font-family: var(--font-interface);
}

.smart-env-notifications__sticky {
  position: sticky;
  top: 0;
  z-index: 5;
  padding-top: var(--size-2-1);
  padding-bottom: var(--size-2-2);
  margin-top: calc(var(--size-2-1) * -1);
  background: linear-gradient(
    to bottom,
    var(--modal-background, var(--background-primary)) 76%,
    color-mix(in srgb, var(--modal-background, var(--background-primary)) 0%, transparent)
  );
}

.smart-env-notifications__toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--size-4-2) var(--size-4-3);

  padding: var(--size-4-2);
  border-radius: var(--smart-env-notifications-toolbar-radius);
  border: 1px solid var(--background-modifier-border);
  background: var(--smart-env-notifications-surface);
  box-shadow: var(--smart-env-notifications-shadow);
  backdrop-filter: var(--blur-l);
}

.smart-env-notifications__meta {
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--size-4-2);
  min-width: 0;
  margin-inline-start: auto;
}

.smart-env-notifications__summary {
  color: var(--text-muted);
  font-size: var(--font-ui-small);
  user-select: none;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  overflow: hidden;
  text-overflow: ellipsis;
}

.smart-env-notifications__actions {
  display: inline-flex;
  align-items: center;
  gap: var(--size-4-2);
}

.copy-all-notifications-btn {
  white-space: nowrap;
}

/* Buttons */
.smart-env-btn {
  appearance: none;
  box-sizing: border-box;
  border: 1px solid var(--background-modifier-border);
  background: var(--background-modifier-form-field);
  color: var(--text-normal);
  border-radius: var(--button-radius);
  padding: var(--size-4-2) calc(var(--size-4-3) + var(--size-2-1));
  font: inherit;
  font-size: var(--font-ui-small);
  line-height: 1.1;
  cursor: pointer;
  transition:
    background var(--anim-duration-fast) var(--anim-motion-smooth),
    border-color var(--anim-duration-fast) var(--anim-motion-smooth),
    transform var(--anim-duration-fast) var(--anim-motion-smooth),
    box-shadow var(--anim-duration-fast) var(--anim-motion-smooth),
    opacity var(--anim-duration-fast) var(--anim-motion-smooth);
}

.smart-env-btn:hover {
  background: var(--background-modifier-hover);
}

.smart-env-btn:active {
  transform: translateY(1px);
}

.smart-env-btn:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.smart-env-btn[disabled] {
  opacity: 0.55;
  cursor: default;
  transform: none;
}

.smart-env-btn--primary {
  background: var(--color-accent);
  border-color: transparent;
  color: var(--text-on-accent, var(--text-normal));
}

.smart-env-btn--primary:hover {
  background: var(--interactive-accent-hover);
}

.smart-env-btn--ghost {
  background: transparent;
}

.smart-env-btn--ghost:hover {
  background: var(--background-modifier-hover);
}

.smart-env-btn.is-copied {
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-accent) 35%, transparent);
}

/* Filter pills */
.smart-env-notifications-filter-controls {
  display: flex;
  flex: 1 1 420px;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--size-2-2);
  min-width: 0;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  -ms-overflow-style: none;
  padding: var(--size-2-2);
}

.smart-env-notifications-filter-controls::-webkit-scrollbar {
  display: none;
}

.smart-env-notifications-filter {
  all: unset;
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}

.smart-env-notifications-filter__content {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  gap: var(--size-2-2);
  padding: calc(var(--size-4-1) + 1px) var(--size-4-3);
  border-radius: var(--smart-env-notifications-filter-radius);
  border: 1px solid transparent;
  background: transparent;
  font-size: var(--font-ui-small);
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  transition:
    background var(--anim-duration-fast) var(--anim-motion-smooth),
    border-color var(--anim-duration-fast) var(--anim-motion-smooth),
    color var(--anim-duration-fast) var(--anim-motion-smooth),
    box-shadow var(--anim-duration-fast) var(--anim-motion-smooth),
    transform var(--anim-duration-fast) var(--anim-motion-smooth);
}

.smart-env-notifications-filter:hover .smart-env-notifications-filter__content {
  background: color-mix(in srgb, var(--background-modifier-hover) 70%, transparent);
  color: var(--text-normal);
}

.smart-env-notifications-filter.is-active:not(.smart-env-notifications-filter--all) .smart-env-notifications-filter__content {
  background: color-mix(in srgb, var(--smart-env-filter-accent) 14%, var(--background-primary));
  border-color: color-mix(in srgb, var(--smart-env-filter-accent) 36%, var(--background-modifier-border));
  box-shadow: var(--shadow-xs);
  color: var(--text-normal);
}

.smart-env-notifications-filter.is-active:not(.smart-env-notifications-filter--all) .smart-env-notifications-filter__count {
  background: transparent;
  border-color: transparent;
  color: inherit;
}

.smart-env-notifications-filter:focus-visible {
  outline: none;
}

.smart-env-notifications-filter:focus-visible .smart-env-notifications-filter__content {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.smart-env-notifications-filter__dot {
  width: var(--size-4-2);
  height: var(--size-4-2);
  border-radius: 999px;
  background: var(--smart-env-filter-accent);
  opacity: 0.95;
  flex: 0 0 auto;
}

.smart-env-notifications-filter--all .smart-env-notifications-filter__dot {
  width: calc(var(--size-4-2) - 1px);
  height: calc(var(--size-4-2) - 1px);
  background: transparent;
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--text-normal) 24%, var(--background-modifier-border));
}

.smart-env-notifications-filter__label {
  white-space: nowrap;
}

.smart-env-notifications-filter__count {
  margin-left: 1px;
  padding: 0 var(--size-2-2);
  border-radius: 999px;
  border: 1px solid var(--background-modifier-border);
  background: var(--background-primary);
  color: var(--text-muted);
  font-size: 0.78em;
  line-height: 1.4;
  font-variant-numeric: tabular-nums;
}

.smart-env-notifications-filter__count.is-zero {
  display: none;
}

.smart-env-notifications-filter--all.is-active .smart-env-notifications-filter__content {
  background: var(--background-primary);
  border-color: var(--background-modifier-border-hover, var(--background-modifier-border));
  box-shadow: var(--shadow-xs);
  color: var(--text-normal);
}

.smart-env-notifications-filter--all.is-active .smart-env-notifications-filter__count {
  background: color-mix(in srgb, var(--background-primary) 84%, transparent);
  border-color: color-mix(in srgb, var(--text-normal) 12%, var(--background-modifier-border));
  color: inherit;
}

.smart-env-notifications-filter[data-level='all'] {
  --smart-env-filter-accent: color-mix(in srgb, var(--text-normal) 30%, var(--text-muted));
}

.smart-env-notifications-filter[data-level='attention'] {
  --smart-env-filter-accent: var(--color-yellow);
}

.smart-env-notifications-filter[data-level='milestone'] {
  --smart-env-filter-accent: var(--color-accent);
}

.smart-env-notifications-filter[data-level='warning'] {
  --smart-env-filter-accent: var(--color-orange);
}

.smart-env-notifications-filter[data-level='error'] {
  --smart-env-filter-accent: var(--color-red);
}

.smart-env-notifications-filter[data-level='info'] {
  --smart-env-filter-accent: var(--status-bar-text-color, var(--text-muted));
}

.smart-env-notifications-filter[data-level='debug'] {
  --smart-env-filter-accent: color-mix(in srgb, var(--text-faint) 60%, var(--background-modifier-border-hover));
}

/* Feed */
.smart-env-notifications-feed {
  display: flex;
  flex-direction: column;
  padding: 1px 0 var(--size-2-3);
  gap: var(--size-4-2);
}

.smart-env-notification {
  background: var(--background-primary);
  border: 1px solid var(--background-modifier-border);
  border-radius: var(--smart-env-notifications-card-radius);
  overflow: hidden;
  transition:
    border-color var(--anim-duration-fast) var(--anim-motion-smooth),
    box-shadow var(--anim-duration-fast) var(--anim-motion-smooth),
    transform var(--anim-duration-fast) var(--anim-motion-smooth);
}

.smart-env-notification:hover {
  border-color: var(--background-modifier-border-hover, var(--background-modifier-border));
  box-shadow: var(--smart-env-notifications-shadow);
}

.smart-env-notification[data-muted='true'] {
  background: color-mix(in srgb, var(--background-primary) 88%, var(--background-secondary));
}

.smart-env-notification[data-level='attention'] {
  --smart-env-notification-accent: var(--color-yellow);
}

.smart-env-notification[data-level='milestone'] {
  --smart-env-notification-accent: var(--color-accent);
}

.smart-env-notification[data-level='warning'] {
  --smart-env-notification-accent: var(--color-orange);
}

.smart-env-notification[data-level='error'] {
  --smart-env-notification-accent: var(--color-red);
}

.smart-env-notification[data-level='info'] {
  --smart-env-notification-accent: var(--status-bar-text-color, var(--text-muted));
}

.smart-env-notification[data-level='debug'],
.smart-env-notification[data-level='event'] {
  --smart-env-notification-accent: color-mix(in srgb, var(--text-faint) 80%, var(--background-modifier-border-hover));
}

details.smart-env-notification {
  user-select: text;
}
details.smart-env-notification[open] {
  transform: translateY(-1px);
  border-color: color-mix(in srgb, var(--smart-env-notification-accent) 76%, var(--background-modifier-border));
  box-shadow: var(--smart-env-notifications-shadow-hover);
}

.smart-env-notification__summary {
  display: flex;
  align-items: flex-start;
  gap: var(--size-4-2);
  padding: calc(var(--size-4-2) + 1px) var(--size-4-3);
  list-style: none;
}

details.smart-env-notification > .smart-env-notification__summary {
  cursor: pointer;
}

details.smart-env-notification > .smart-env-notification__summary:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
  border-radius: var(--smart-env-notifications-card-radius);
}

.smart-env-notification__summary::-webkit-details-marker {
  display: none;
}

.smart-env-notification__accent {
  width: calc(var(--size-4-2) + 2px);
  height: calc(var(--size-4-2) + 2px);
  border-radius: 999px;
  margin-top: var(--size-2-2);
  flex: 0 0 auto;
  background: var(--smart-env-notification-accent, var(--smart-env-notifications-muted-accent));
  color: var(--smart-env-notification-accent, var(--smart-env-notifications-muted-accent));
  box-shadow: 0 0 0 4px color-mix(in srgb, currentColor 12%, transparent);
}

.smart-env-notification__summary-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--size-2-2);
}

.smart-env-notification__summary-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--size-4-3);
}

details[open] .smart-env-notification__event-key {
  white-space: normal;
}

.smart-env-notification__event-key {
  color: var(--text-normal);
  font-weight: var(--font-semibold);
  letter-spacing: -0.01em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.smart-env-notification__summary-action {
  margin-left: auto;
  font-size: var(--font-ui-smaller);
  min-height: var(--touch-size-xxs);
  padding: 0 var(--size-2-3);
}

.smart-env-notification__time {
  color: var(--text-muted);
  font-size: var(--font-ui-small);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  flex: 0 0 auto;
}

.smart-env-notification__summary-bottom {
  display: flex;
  align-items: center;
  gap: var(--size-2-2);
  row-gap: var(--size-2-1);
  flex-wrap: wrap;
}

.smart-env-notification__collection,
.smart-env-notification__level,
.smart-env-notification__feed-only,
.smart-env-notification__muted,
.smart-env-notification__event-count {
  display: inline-flex;
  align-items: center;
  padding: 0 var(--size-2-3);
  min-height: var(--touch-size-xxs);
  border-radius: 999px;
  border: 1px solid var(--background-modifier-border);
  background: transparent;
  color: var(--text-muted);
  font-size: var(--font-ui-small);
  line-height: 1.1;
}

.smart-env-notification__collection {
  background: var(--background-secondary);
}

.smart-env-notification__event-count {
  background: var(--background-primary);
}

.smart-env-notification__feed-only {
  color: var(--text-faint);
  border-style: dashed;
}

.smart-env-notification__level {
  border-color: color-mix(in srgb, var(--smart-env-notification-accent) 88%, var(--background-modifier-border));
  color: var(--smart-env-notification-accent, var(--text-muted));
}

.smart-env-notification[data-level='debug'] .smart-env-notification__level,
.smart-env-notification[data-level='event'] .smart-env-notification__level {
  border-style: dashed;
}

.smart-env-notification__chevron {
  width: var(--icon-m);
  height: var(--icon-m);
  margin-top: 1px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--text-faint);
  flex: 0 0 auto;
  transition:
    transform var(--anim-duration-fast) var(--anim-motion-smooth),
    color var(--anim-duration-fast) var(--anim-motion-smooth);
}

.smart-env-notification__chevron::before {
  content: ">";
  font-size: var(--font-ui-medium);
  line-height: 1;
}

details.smart-env-notification[open] .smart-env-notification__chevron {
  transform: rotate(90deg);
  color: var(--text-muted);
}

.smart-env-notification__expanded {
  display: flex;
  flex-direction: column;
  gap: var(--size-4-2);
  padding: var(--size-4-3);
  border-top: 1px solid var(--background-modifier-border);
  background: color-mix(in srgb, var(--background-secondary) 88%, var(--background-primary));
}

.smart-env-notification__expanded-meta {
  display: grid;
  gap: var(--size-2-2);
}

.smart-env-notification__expanded-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--size-4-3);
}

.smart-env-notification__expanded-label {
  color: var(--text-muted);
  font-size: var(--font-ui-small);
  white-space: nowrap;
}

.smart-env-notification__expanded-value {
  color: var(--text-normal);
  font-size: var(--font-ui-small);
  text-align: right;
  word-break: break-word;
}

.smart-env-notification__expanded-actions {
  display: flex;
  align-items: center;
  gap: var(--size-2-2);
  flex-wrap: wrap;
}

.smart-env-notification__message {
  margin: 0;
  padding: var(--size-4-3);
  border: 1px solid var(--background-modifier-border);
  border-radius: var(--smart-env-notifications-code-radius);
  background: var(--background-primary);
  font-family: var(--font-monospace);
  font-size: var(--font-smaller);
  font-variant-ligatures: none;
  white-space: pre-wrap;
  word-break: break-word;
}

/* Empty states */
.smart-env-notifications-empty-state {
  padding: var(--size-4-6) var(--size-4-4);
  border-radius: var(--smart-env-notifications-empty-radius);
  border: 1px dashed var(--background-modifier-border);
  background: color-mix(in srgb, var(--background-secondary) 65%, transparent);
  text-align: center;
}

.smart-env-notifications-empty-state__title {
  color: var(--text-normal);
  font-weight: var(--font-semibold);
  letter-spacing: -0.01em;
  margin-bottom: var(--size-2-2);
}

.smart-env-notifications-empty-state__detail {
  color: var(--text-muted);
  font-size: var(--font-ui-small);
  margin-bottom: var(--size-4-3);
}

/* Footer */
.smart-env-notifications__footer {
  display: flex;
  justify-content: center;
  padding-top: var(--size-2-2);
}

.smart-env-notifications__footer .smart-env-btn {
  width: 100%;
  max-width: 320px;
}

@media (max-width: 720px) {
  .smart-env-notifications__meta {
    width: 100%;
    margin-inline-start: 0;
    justify-content: flex-end;
  }
}

@media (max-width: 540px) {
  .smart-env-notifications-modal .modal-content {
    padding: var(--size-4-2) var(--size-4-2) var(--size-4-4);
  }

  .smart-env-notifications__toolbar {
    border-radius: var(--radius-l);
  }

  .smart-env-notifications-filter-controls {
    flex-basis: 100%;
  }

  .smart-env-notifications__meta {
    justify-content: space-between;
  }

  .smart-env-notifications__summary {
    flex: 1 1 auto;
    min-width: 0;
  }

  .smart-env-notifications__footer .smart-env-btn {
    max-width: none;
  }

  .smart-env-notification__expanded-row {
    flex-direction: column;
    gap: var(--size-2-1);
  }

  .smart-env-notification__expanded-value {
    text-align: left;
  }
}
`;

// node_modules/obsidian-smart-env/src/utils/notifications_feed_utils.js
var default_page_size = 100;
var load_more_step = 100;
var all_levels_filter_key = "all";
var debug_levels_filter_key = "debug";
var notification_filter_keys = Object.freeze([
  ...notification_levels,
  debug_levels_filter_key
]);
function normalize_filter_level(level) {
  const normalized_level = normalize_event_level(level);
  if (normalized_level) return normalized_level;
  const raw_level = typeof level === "string" ? level.trim().toLowerCase() : "";
  if (raw_level === debug_levels_filter_key) return debug_levels_filter_key;
  return null;
}
function create_all_levels_set() {
  return new Set(notification_filter_keys);
}
function are_all_levels_active(active_levels = /* @__PURE__ */ new Set()) {
  if (!(active_levels instanceof Set)) return false;
  if (active_levels.size !== notification_filter_keys.length) return false;
  return notification_filter_keys.every((level) => active_levels.has(level));
}
function get_next_active_levels(active_levels = /* @__PURE__ */ new Set(), params = {}) {
  const {
    level = null,
    select_all = false
  } = params;
  if (select_all) return create_all_levels_set();
  const normalized_level = normalize_filter_level(level);
  const next_active_levels = new Set(active_levels instanceof Set ? active_levels : []);
  if (!normalized_level) return next_active_levels;
  if (are_all_levels_active(next_active_levels)) {
    return /* @__PURE__ */ new Set([normalized_level]);
  }
  if (next_active_levels.has(normalized_level)) {
    next_active_levels.delete(normalized_level);
    if (next_active_levels.size === 0) {
      return create_all_levels_set();
    }
    return next_active_levels;
  }
  next_active_levels.add(normalized_level);
  return next_active_levels;
}
function get_canonical_entry_level(entry) {
  return get_event_level(entry?.event_key, entry?.event);
}
function get_entry_level(entry) {
  const canonical_level = get_canonical_entry_level(entry);
  if (canonical_level) return canonical_level;
  return get_event_level(entry?.event_key, entry?.event, {
    allow_display_fallback: true
  });
}
function is_canonical_notification_entry(entry) {
  return Boolean(get_canonical_entry_level(entry));
}
function is_debug_entry(entry) {
  if (!entry || typeof entry !== "object") return false;
  return !get_entry_level(entry);
}
function get_filtered_entries(entries, params = {}) {
  const { active_levels = create_all_levels_set() } = params;
  if (!(active_levels instanceof Set) || active_levels.size === 0) return [];
  const next_entries = Array.isArray(entries) ? [...entries] : [];
  if (are_all_levels_active(active_levels)) {
    return next_entries;
  }
  return next_entries.filter((entry) => {
    const level = get_entry_level(entry);
    if (level) return active_levels.has(level);
    return active_levels.has(debug_levels_filter_key);
  });
}
function get_visible_entries(entries, params = {}) {
  const { limit = default_page_size } = params;
  return entries.slice(-limit).reverse();
}
function get_visible_count(entries_length, params = {}) {
  const { page_size = default_page_size } = params;
  return Math.min(entries_length, page_size);
}
function get_next_visible_count(entries_length, params = {}) {
  const { current_count = 0, step_size = load_more_step } = params;
  return Math.min(entries_length, current_count + step_size);
}
function should_show_load_more(entries_length, visible_count) {
  return entries_length > visible_count;
}
function format_level_label2(level) {
  const normalized_level = normalize_filter_level(level);
  if (!normalized_level) return "";
  return normalized_level.slice(0, 1).toUpperCase() + normalized_level.slice(1);
}
function queue_live_update_entries(pending_entries, next_entries, params = {}) {
  const { existing_entries = [] } = params;
  const pending_list = Array.isArray(pending_entries) ? [...pending_entries] : [];
  const pending_entry_ids = new Set(pending_list.map((entry) => `${get_entry_event_key(entry)}::${get_entry_timestamp2(entry)}`));
  const existing_entry_ids = new Set((Array.isArray(existing_entries) ? existing_entries : []).map((entry) => `${get_entry_event_key(entry)}::${get_entry_timestamp2(entry)}`));
  (Array.isArray(next_entries) ? next_entries : []).forEach((entry) => {
    const entry_id = `${get_entry_event_key(entry)}::${get_entry_timestamp2(entry)}`;
    if (existing_entry_ids.has(entry_id)) return;
    if (pending_entry_ids.has(entry_id)) return;
    pending_list.push(entry);
    pending_entry_ids.add(entry_id);
  });
  return pending_list;
}
function get_level_counts(entries) {
  const counts = notification_filter_keys.reduce((acc, level) => {
    acc[level] = 0;
    return acc;
  }, {});
  entries.forEach((entry) => {
    if (!entry || typeof entry !== "object") return;
    const level = get_entry_level(entry);
    if (level) {
      counts[level] += 1;
      return;
    }
    counts[debug_levels_filter_key] += 1;
  });
  return counts;
}
function get_entry_timestamp2(entry) {
  if (typeof entry?.event?.at === "number") return entry.event.at;
  if (typeof entry?.at === "number") return entry.at;
  return Date.now();
}
function get_entry_event_key(entry) {
  return typeof entry?.event_key === "string" ? entry.event_key : "";
}
function get_entry_title(entry) {
  const event_obj = entry?.event && typeof entry.event === "object" ? entry.event : {};
  if (typeof event_obj.message === "string" && event_obj.message.trim()) return event_obj.message.trim();
  if (typeof event_obj.details === "string" && event_obj.details.trim()) return event_obj.details.trim();
  if (typeof event_obj.milestone === "string" && event_obj.milestone.trim()) return event_obj.milestone.trim();
  return get_entry_event_key(entry) || "event";
}
function get_entry_payload_text(entry) {
  const event_obj = entry?.event && typeof entry.event === "object" ? entry.event : {};
  return Object.entries(event_obj).filter(([key]) => !["at", "collection_key", "message", "level", "btn_text", "btn_callback", "timeout", "timeout_ms"].includes(key)).map(([key, value]) => `  ${key}: ${typeof value === "string" ? value : JSON.stringify(value)}`).join("\n");
}
function get_entry_summary_action(entry) {
  const event_obj = entry?.event && typeof entry.event === "object" ? entry.event : {};
  const btn_text = typeof event_obj.btn_text === "string" ? event_obj.btn_text.trim() : "";
  const btn_callback = typeof event_obj.btn_callback === "string" ? event_obj.btn_callback.trim() : "";
  if (!btn_text || !btn_callback) return null;
  return { btn_text, btn_callback };
}
function get_entry_meta_text(entry) {
  const collection_key = entry?.event?.collection_key ?? "";
  const event_key = get_entry_event_key(entry) || "event";
  const timestamp = get_entry_timestamp2(entry);
  return `${collection_key ? `${collection_key} - ` : ""}${event_key} - ${to_time_ago(timestamp)}`;
}
function entry_to_clipboard_text(entry) {
  const meta_text = get_entry_meta_text(entry);
  const payload_text = get_entry_payload_text(entry);
  if (!payload_text.trim().length) {
    return `${meta_text}

`;
  }
  return `${meta_text}
${payload_text}

`;
}
function entries_to_clipboard_text(entries = []) {
  return entries.map((entry) => entry_to_clipboard_text(entry)).join("");
}
function to_time_ago(ms) {
  const now_ms = Date.now();
  const seconds = Math.floor((now_ms - ms) / 1e3);
  if (seconds < 60) return `${Math.max(0, seconds)}s ago`;
  const minutes = Math.floor(seconds / 60);
  if (minutes < 60) return `${minutes}m ago`;
  const hours = Math.floor(minutes / 60);
  if (hours < 24) return `${hours}h ago`;
  const days = Math.floor(hours / 24);
  return `${days}d ago`;
}

// node_modules/obsidian-smart-env/src/components/notifications_feed.js
var feed_excluded_event_keys = /* @__PURE__ */ new Set([
  "collection:save_started",
  "collection:save_completed",
  "notifications:seen",
  "notifications:seen_all",
  "event_log:first"
]);
function build_html7() {
  return `<div class="smart-env-notifications">
    <div class="smart-env-notifications__sticky">
      <div class="smart-env-notifications__toolbar">
        <div class="smart-env-notifications-filter-controls" aria-label="Event level filters"></div>
        <div class="smart-env-notifications__meta">
          <div class="smart-env-notifications__summary" aria-live="polite"></div>
          <div class="smart-env-notifications__actions">
            <button class="smart-env-btn smart-env-btn--ghost copy-all-notifications-btn" type="button" title="Copy all filtered events to clipboard">Copy All</button>
          </div>
        </div>
      </div>
    </div>

    <div class="smart-env-notifications-live-updates"></div>
    <div class="smart-env-notifications-feed" role="list"></div>

    <div class="smart-env-notifications__footer">
      <button class="smart-env-btn smart-env-btn--primary load-more-notifications-btn" type="button">Load more</button>
    </div>
  </div>`;
}
async function render9(env, params = {}) {
  this.apply_style_sheet(notification_feed_default);
  const frag = this.create_doc_fragment(build_html7());
  const container = frag.firstElementChild;
  await post_process6.call(this, env, container, params);
  return container;
}
async function post_process6(env, container, params = {}) {
  const {
    live_updates = false,
    auto_mark_seen = false,
    state = {}
  } = params;
  const feed_container = container.querySelector(".smart-env-notifications-feed");
  const copy_btn = container.querySelector(".copy-all-notifications-btn");
  const load_more_btn = container.querySelector(".load-more-notifications-btn");
  const filter_controls = container.querySelector(".smart-env-notifications-filter-controls");
  const summary_el = container.querySelector(".smart-env-notifications__summary");
  const live_updates_el = container.querySelector(".smart-env-notifications-live-updates");
  const smart_env2 = this;
  const expanded_entry_keys = state.expanded_entry_keys instanceof Set ? state.expanded_entry_keys : /* @__PURE__ */ new Set();
  let target_entry_key = typeof state?.target_entry_key === "string" ? state.target_entry_key.trim() : "";
  let has_revealed_target_entry = false;
  this.empty(feed_container);
  const active_levels = state.active_levels instanceof Set ? state.active_levels : create_all_levels_set();
  let visible_count = typeof state.visible_count === "number" ? state.visible_count : null;
  let filtered_count = typeof state.filtered_count === "number" ? state.filtered_count : null;
  let pending_live_entries = Array.isArray(state.pending_live_entries) ? [...state.pending_live_entries] : [];
  let rendered_entries_cache = [];
  if (target_entry_key) {
    expanded_entry_keys.add(target_entry_key);
  }
  state.active_levels = active_levels;
  state.expanded_entry_keys = expanded_entry_keys;
  state.pending_live_entries = pending_live_entries;
  state.target_entry_key = target_entry_key;
  const get_entries = () => {
    const session_entries = Array.isArray(env?.event_logs?.session_events) ? [...env.event_logs.session_events] : [];
    return session_entries;
  };
  const should_refresh_for_event = (event_key) => {
    if (event_key === "event_logs:mute_changed") return true;
    if (feed_excluded_event_keys.has(event_key)) return false;
    return true;
  };
  const capture_expanded_entry_keys = () => {
    expanded_entry_keys.clear();
    feed_container.querySelectorAll("details.smart-env-notification[open]").forEach((details_el) => {
      const entry_key = details_el.dataset.entryKey;
      if (!entry_key) return;
      expanded_entry_keys.add(entry_key);
    });
    if (target_entry_key) {
      expanded_entry_keys.add(target_entry_key);
    }
  };
  const set_active_levels = (next_active_levels) => {
    active_levels.clear();
    next_active_levels.forEach((level) => active_levels.add(level));
  };
  const maybe_mark_entries_seen = () => {
    if (!auto_mark_seen) return;
    env?.event_logs?.mark_all_notification_entries_seen?.();
  };
  const render_filters = (entries) => {
    render_filter_controls(filter_controls, {
      active_levels,
      total_count: entries.length,
      level_counts: get_level_counts(entries),
      on_change: handle_filters_changed,
      on_select_all: () => {
        set_active_levels(create_all_levels_set());
      },
      on_select_level: (level) => {
        set_active_levels(get_next_active_levels(active_levels, { level }));
      }
    });
  };
  const render_entries = (entries) => {
    smart_env2.empty(feed_container);
    const filtered_entries = get_filtered_entries(entries, { active_levels });
    const previous_filtered_count = typeof filtered_count === "number" ? filtered_count : filtered_entries.length;
    if (typeof visible_count !== "number") {
      visible_count = get_visible_count(filtered_entries.length);
    } else if (visible_count >= previous_filtered_count) {
      visible_count = filtered_entries.length;
    } else {
      visible_count = Math.min(visible_count, filtered_entries.length);
    }
    const target_visible_count = get_required_visible_count_for_entry(filtered_entries, target_entry_key);
    if (typeof target_visible_count === "number" && target_visible_count > visible_count) {
      visible_count = target_visible_count;
    }
    filtered_count = filtered_entries.length;
    state.visible_count = visible_count;
    state.filtered_count = filtered_count;
    const shown_count = Math.min(visible_count, filtered_entries.length);
    update_summary(summary_el, {
      total_count: entries.length,
      filtered_count: filtered_entries.length,
      visible_count: shown_count
    });
    set_btn_disabled(copy_btn, filtered_entries.length === 0);
    if (filtered_entries.length === 0) {
      render_empty_state(feed_container, {
        title: "No events match your filters.",
        detail: "Try enabling more levels or switch back to All.",
        action_text: "Reset filters",
        on_action: reset_filters
      });
      if (load_more_btn) load_more_btn.style.display = "none";
      return;
    }
    get_visible_entries(filtered_entries, { limit: visible_count }).forEach((entry) => {
      append_entry(feed_container, entry, {
        env,
        expanded_entry_keys,
        on_entry_toggle: handle_entry_toggle,
        on_toggle_mute: handle_toggle_mute
      });
    });
    update_load_more_button(load_more_btn, {
      entries_length: filtered_entries.length,
      visible_count
    });
    if (!has_revealed_target_entry && target_entry_key) {
      has_revealed_target_entry = reveal_target_entry(feed_container, target_entry_key);
      if (has_revealed_target_entry) {
        state.target_entry_key = "";
        target_entry_key = "";
      }
    }
  };
  const render_feed = (opts = {}) => {
    const {
      preserve_expanded = false,
      reset_visible_count = false
    } = opts;
    if (preserve_expanded) capture_expanded_entry_keys();
    const entries = get_entries();
    rendered_entries_cache = entries;
    if (reset_visible_count) {
      const filtered_entries = get_filtered_entries(entries, { active_levels });
      visible_count = get_visible_count(filtered_entries.length);
      state.visible_count = visible_count;
      state.filtered_count = filtered_entries.length;
    }
    if (!entries.length) {
      smart_env2.empty(feed_container);
      filter_controls?.replaceChildren?.();
      render_empty_state(feed_container, {
        title: "No Smart Env events yet.",
        detail: "When Smart Env emits events, they will appear here."
      });
      set_btn_disabled(copy_btn, true);
      if (load_more_btn) load_more_btn.style.display = "none";
      update_summary(summary_el, { total_count: 0, filtered_count: 0, visible_count: 0 });
      render_live_updates_action(live_updates_el, {
        pending_count: pending_live_entries.length,
        on_click: handle_show_live_updates
      });
      maybe_mark_entries_seen();
      return;
    }
    render_filters(entries);
    render_entries(entries);
    render_live_updates_action(live_updates_el, {
      pending_count: pending_live_entries.length,
      on_click: handle_show_live_updates
    });
    maybe_mark_entries_seen();
  };
  const handle_filters_changed = () => {
    render_feed({ preserve_expanded: true, reset_visible_count: true });
  };
  const handle_toggle_mute = (entry) => {
    const event_key = entry?.event_key;
    if (!event_key || typeof env?.event_logs?.toggle_event_key_muted !== "function") return;
    if (!is_canonical_notification_entry(entry)) return;
    env.event_logs.toggle_event_key_muted(event_key);
    render_feed({ preserve_expanded: true });
  };
  const handle_entry_toggle = (entry_key, is_open) => {
    if (is_open) {
      expanded_entry_keys.add(entry_key);
      return;
    }
    expanded_entry_keys.delete(entry_key);
  };
  const handle_show_live_updates = () => {
    if (pending_live_entries.length === 0) return;
    pending_live_entries = [];
    state.pending_live_entries = pending_live_entries;
    render_feed({ preserve_expanded: true });
  };
  const reset_filters = () => {
    set_active_levels(create_all_levels_set());
    render_feed({ preserve_expanded: true, reset_visible_count: true });
  };
  render_feed({ reset_visible_count: true });
  if (copy_btn) {
    copy_btn.addEventListener("click", async () => {
      if (copy_btn.disabled) return;
      const filtered_entries = get_filtered_entries(get_entries(), { active_levels });
      const newest_first = get_visible_entries(filtered_entries, { limit: filtered_entries.length });
      const all_text = entries_to_clipboard_text(newest_first);
      const copied = await write_text_to_clipboard(all_text);
      set_btn_copied_state(copy_btn, {
        idle_text: "Copy All",
        copied_text: copied ? "Copied" : "Copy failed"
      });
    });
  }
  if (load_more_btn) {
    load_more_btn.addEventListener("click", () => {
      const filtered_entries = get_filtered_entries(get_entries(), { active_levels });
      visible_count = get_next_visible_count(filtered_entries.length, {
        current_count: visible_count
      });
      state.visible_count = visible_count;
      render_feed({ preserve_expanded: true });
    });
  }
  if (live_updates && typeof env?.events?.on === "function") {
    let debounce_timeout = null;
    const live_update_off = env.events.on("*", (_event, event_key) => {
      if (!should_refresh_for_event(event_key)) return;
      if (debounce_timeout) clearTimeout(debounce_timeout);
      debounce_timeout = setTimeout(() => {
        debounce_timeout = null;
        const next_entries = get_entries();
        pending_live_entries = queue_live_update_entries(pending_live_entries, next_entries, {
          existing_entries: rendered_entries_cache
        });
        state.pending_live_entries = pending_live_entries;
        if (pending_live_entries.length > 0) {
          render_live_updates_action(live_updates_el, {
            pending_count: pending_live_entries.length,
            on_click: handle_show_live_updates
          });
          return;
        }
        render_feed({ preserve_expanded: true });
      }, 100);
    });
    this.attach_disposer(container, [
      live_update_off,
      () => {
        if (!debounce_timeout) return;
        clearTimeout(debounce_timeout);
        debounce_timeout = null;
      }
    ]);
  }
}
function render_filter_controls(container, params = {}) {
  if (!container) return;
  const {
    active_levels = /* @__PURE__ */ new Set(),
    total_count = 0,
    level_counts = {},
    on_change = () => {
    },
    on_select_all = () => {
    },
    on_select_level = () => {
    }
  } = params;
  container.replaceChildren();
  const all_is_active = are_all_levels_active(active_levels);
  append_filter_button(container, {
    level: all_levels_filter_key,
    label_text: "All",
    count_total: total_count,
    is_active: all_is_active,
    modifier_class: "smart-env-notifications-filter--all",
    on_click: () => {
      if (all_is_active) return;
      on_select_all();
      on_change();
    }
  });
  notification_levels.forEach((level) => {
    append_filter_button(container, {
      level,
      label_text: format_level_label2(level),
      count_total: typeof level_counts[level] === "number" ? level_counts[level] : 0,
      is_active: !all_is_active && active_levels.has(level),
      on_click: () => {
        on_select_level(level);
        on_change();
      }
    });
  });
  append_filter_button(container, {
    level: debug_levels_filter_key,
    label_text: "Debug",
    count_total: typeof level_counts[debug_levels_filter_key] === "number" ? level_counts[debug_levels_filter_key] : 0,
    is_active: !all_is_active && active_levels.has(debug_levels_filter_key),
    on_click: () => {
      on_select_level(debug_levels_filter_key);
      on_change();
    }
  });
}
function append_filter_button(container, params) {
  const {
    level,
    label_text,
    count_total,
    is_active,
    on_click,
    modifier_class = ""
  } = params;
  const button = container.ownerDocument.createElement("button");
  button.type = "button";
  button.className = `smart-env-notifications-filter${modifier_class ? ` ${modifier_class}` : ""}`;
  button.dataset.level = level;
  button.setAttribute("aria-pressed", String(Boolean(is_active)));
  if (is_active) button.classList.add("is-active");
  button.addEventListener("click", on_click);
  const content = container.ownerDocument.createElement("span");
  content.className = "smart-env-notifications-filter__content";
  const dot = container.ownerDocument.createElement("span");
  dot.className = "smart-env-notifications-filter__dot";
  dot.setAttribute("aria-hidden", "true");
  const text = container.ownerDocument.createElement("span");
  text.className = "smart-env-notifications-filter__label";
  text.textContent = label_text;
  const count = container.ownerDocument.createElement("span");
  count.className = "smart-env-notifications-filter__count";
  count.textContent = count_total > 0 ? count_total.toLocaleString() : "";
  if (count_total <= 0) count.classList.add("is-zero");
  content.appendChild(dot);
  content.appendChild(text);
  content.appendChild(count);
  button.appendChild(content);
  container.appendChild(button);
}
function update_load_more_button(button, params = {}) {
  if (!button) return;
  const { entries_length = 0, visible_count = 0 } = params;
  const is_visible2 = should_show_load_more(entries_length, visible_count);
  button.style.display = is_visible2 ? "block" : "none";
  if (is_visible2) {
    const remaining_count = entries_length - visible_count;
    const next_step = Math.min(load_more_step, remaining_count);
    button.textContent = `Load ${next_step.toLocaleString()} more`;
  }
}
function render_live_updates_action(container, params = {}) {
  if (!container) return;
  const {
    pending_count = 0,
    on_click = () => {
    }
  } = params;
  container.replaceChildren();
  if (pending_count <= 0) return;
  const button = container.ownerDocument.createElement("button");
  button.type = "button";
  button.className = "smart-env-btn smart-env-btn--ghost smart-env-notifications-live-updates__btn";
  button.textContent = `Show ${pending_count.toLocaleString()} new event${pending_count === 1 ? "" : "s"}`;
  button.addEventListener("click", on_click);
  container.appendChild(button);
}
function append_entry(feed_container, entry, params = {}) {
  const {
    env = null,
    expanded_entry_keys = /* @__PURE__ */ new Set(),
    on_entry_toggle = () => {
    },
    on_toggle_mute = () => {
    }
  } = params;
  const level = get_entry_level(entry);
  const is_debug = is_debug_entry(entry);
  const title = get_entry_title(entry);
  const timestamp = get_entry_timestamp2(entry);
  const collection_key = entry?.event?.collection_key ?? "";
  const event_key = get_entry_event_key(entry) || "event";
  const payload_text = get_entry_payload_text(entry);
  const summary_action = get_entry_summary_action(entry);
  const is_canonical = is_canonical_notification_entry(entry);
  const is_muted = is_canonical && Boolean(env?.event_logs?.is_event_key_muted?.(event_key));
  const is_feed_only = Boolean(level) && !is_canonical;
  const entry_row_key = get_entry_row_key(entry);
  const total_count = get_event_key_total_count(env, event_key);
  const row_level = level || (is_debug ? debug_levels_filter_key : "event");
  const row = feed_container.ownerDocument.createElement("details");
  row.className = "smart-env-notification";
  row.dataset.entryKey = entry_row_key;
  row.dataset.level = row_level;
  row.setAttribute("role", "listitem");
  if (is_muted) row.dataset.muted = "true";
  if (expanded_entry_keys.has(entry_row_key)) row.open = true;
  const summary = feed_container.ownerDocument.createElement("summary");
  summary.className = "smart-env-notification__summary";
  const accent = feed_container.ownerDocument.createElement("span");
  accent.className = "smart-env-notification__accent";
  accent.setAttribute("aria-hidden", "true");
  const body = feed_container.ownerDocument.createElement("div");
  body.className = "smart-env-notification__summary-body";
  const top = feed_container.ownerDocument.createElement("div");
  top.className = "smart-env-notification__summary-top";
  const event_el = feed_container.ownerDocument.createElement("div");
  event_el.className = "smart-env-notification__event-key";
  event_el.textContent = title;
  const time_el = feed_container.ownerDocument.createElement("div");
  time_el.className = "smart-env-notification__time";
  time_el.textContent = to_time_ago(timestamp);
  time_el.title = format_timestamp(timestamp);
  top.appendChild(event_el);
  if (summary_action) {
    const cta_btn = feed_container.ownerDocument.createElement("button");
    cta_btn.className = "smart-env-btn smart-env-btn--ghost smart-env-notification__summary-action";
    cta_btn.type = "button";
    cta_btn.textContent = summary_action.btn_text;
    cta_btn.setAttribute("aria-label", summary_action.btn_text);
    cta_btn.addEventListener("click", (event) => {
      event.preventDefault();
      event.stopPropagation();
      dispatch_notice_action(env, summary_action.btn_callback, {
        event_key,
        event: entry?.event,
        event_source: "notifications_feed"
      });
    });
    top.appendChild(cta_btn);
  }
  top.appendChild(time_el);
  const bottom = feed_container.ownerDocument.createElement("div");
  bottom.className = "smart-env-notification__summary-bottom";
  if (collection_key) {
    bottom.appendChild(create_tag(feed_container, "smart-env-notification__collection", collection_key));
  }
  if (title !== event_key) {
    bottom.appendChild(create_tag(feed_container, "smart-env-notification__collection", event_key));
  }
  bottom.appendChild(create_tag(
    feed_container,
    "smart-env-notification__level",
    level ? format_level_label2(level) : is_debug ? "Debug" : "Event"
  ));
  if (is_feed_only) {
    bottom.appendChild(create_tag(feed_container, "smart-env-notification__feed-only", "Feed only"));
  }
  if (is_muted) {
    bottom.appendChild(create_tag(feed_container, "smart-env-notification__muted", "Muted"));
  }
  body.appendChild(top);
  body.appendChild(bottom);
  const chevron = feed_container.ownerDocument.createElement("span");
  chevron.className = "smart-env-notification__chevron";
  chevron.setAttribute("aria-hidden", "true");
  summary.appendChild(accent);
  summary.appendChild(body);
  summary.appendChild(chevron);
  const expanded = feed_container.ownerDocument.createElement("div");
  expanded.className = "smart-env-notification__expanded";
  const meta = feed_container.ownerDocument.createElement("div");
  meta.className = "smart-env-notification__expanded-meta";
  append_meta_row(feed_container, meta, "Occurred", format_timestamp(timestamp));
  append_meta_row(feed_container, meta, "Event key", event_key);
  if (collection_key) {
    append_meta_row(feed_container, meta, "Collection", collection_key);
  }
  expanded.appendChild(meta);
  const actions = feed_container.ownerDocument.createElement("div");
  actions.className = "smart-env-notification__expanded-actions";
  actions.appendChild(create_tag(
    feed_container,
    "smart-env-notification__event-count",
    `${Math.max(1, total_count).toLocaleString()} total`
  ));
  if (is_canonical) {
    const mute_btn = feed_container.ownerDocument.createElement("button");
    mute_btn.className = "smart-env-btn smart-env-btn--ghost smart-env-notification__mute";
    mute_btn.type = "button";
    mute_btn.textContent = is_muted ? "Unmute native notices" : "Mute native notices";
    mute_btn.setAttribute(
      "aria-label",
      is_muted ? "Allow native notices for this event key" : "Mute native notices for this event key"
    );
    mute_btn.addEventListener("click", (event) => {
      event.preventDefault();
      event.stopPropagation();
      on_toggle_mute(entry);
    });
    actions.appendChild(mute_btn);
  } else if (is_feed_only) {
    actions.appendChild(create_tag(feed_container, "smart-env-notification__feed-only", "Display fallback only"));
  }
  expanded.appendChild(actions);
  if (payload_text.trim().length) {
    const message = feed_container.ownerDocument.createElement("pre");
    message.className = "smart-env-notification__message";
    message.textContent = payload_text;
    expanded.appendChild(message);
  }
  row.appendChild(summary);
  row.appendChild(expanded);
  row.addEventListener("toggle", () => {
    on_entry_toggle(entry_row_key, row.open === true);
  });
  feed_container.appendChild(row);
}
function update_summary(summary_el, params = {}) {
  if (!summary_el) return;
  const {
    total_count = 0,
    filtered_count = 0,
    visible_count = 0
  } = params;
  if (total_count <= 0) {
    summary_el.textContent = "";
    return;
  }
  const shown_count = Math.min(visible_count, filtered_count);
  const shown_label = shown_count.toLocaleString();
  const filtered_label = filtered_count.toLocaleString();
  const total_label = total_count.toLocaleString();
  let text = shown_count === filtered_count ? `${shown_label} shown` : `${shown_label} of ${filtered_label} shown`;
  if (filtered_count !== total_count) {
    text += ` (${total_label} total)`;
  }
  summary_el.textContent = text;
}
function render_empty_state(feed_container, params = {}) {
  const {
    title = "Nothing here yet.",
    detail = "",
    action_text = "",
    on_action = null
  } = params;
  const wrap = feed_container.ownerDocument.createElement("div");
  wrap.className = "smart-env-notifications-empty-state";
  const heading = feed_container.ownerDocument.createElement("div");
  heading.className = "smart-env-notifications-empty-state__title";
  heading.textContent = title;
  wrap.appendChild(heading);
  if (detail) {
    const detail_el = feed_container.ownerDocument.createElement("div");
    detail_el.className = "smart-env-notifications-empty-state__detail";
    detail_el.textContent = detail;
    wrap.appendChild(detail_el);
  }
  if (action_text && typeof on_action === "function") {
    const button = feed_container.ownerDocument.createElement("button");
    button.className = "smart-env-btn smart-env-btn--ghost";
    button.type = "button";
    button.textContent = action_text;
    button.addEventListener("click", () => on_action());
    wrap.appendChild(button);
  }
  feed_container.appendChild(wrap);
}
function set_btn_disabled(btn, is_disabled) {
  if (!btn) return;
  btn.disabled = Boolean(is_disabled);
}
function set_btn_copied_state(btn, params = {}) {
  if (!btn) return;
  const {
    idle_text = "Copy",
    copied_text = "Copied"
  } = params;
  btn.textContent = copied_text;
  btn.classList.add("is-copied");
  setTimeout(() => {
    btn.textContent = idle_text;
    btn.classList.remove("is-copied");
  }, 1400);
}
async function write_text_to_clipboard(text = "") {
  if (!text) return false;
  try {
    if (navigator?.clipboard?.writeText) {
      await navigator.clipboard.writeText(text);
      return true;
    }
  } catch (_err) {
  }
  try {
    if (typeof window !== "undefined" && typeof window.require === "function") {
      const { clipboard } = window.require("electron");
      if (clipboard?.writeText) {
        clipboard.writeText(text);
        return true;
      }
    }
  } catch (_err) {
  }
  try {
    if (typeof document === "undefined") return false;
    const textarea = document.createElement("textarea");
    textarea.value = text;
    textarea.style.position = "fixed";
    textarea.style.left = "-9999px";
    textarea.style.top = "0";
    document.body.appendChild(textarea);
    textarea.focus();
    textarea.select();
    const copied = document.execCommand("copy");
    document.body.removeChild(textarea);
    return Boolean(copied);
  } catch (_err) {
    return false;
  }
}
function get_entry_row_key(entry) {
  return `${get_entry_event_key(entry)}:${get_entry_timestamp2(entry)}`;
}
function get_required_visible_count_for_entry(entries = [], target_entry_key = "") {
  if (!Array.isArray(entries) || !target_entry_key) return null;
  const target_index = entries.findIndex((entry) => get_entry_row_key(entry) === target_entry_key);
  if (target_index === -1) return null;
  return entries.length - target_index;
}
function reveal_target_entry(feed_container, target_entry_key = "") {
  if (!feed_container || !target_entry_key) return false;
  const target_entry_el = Array.from(feed_container.querySelectorAll(".smart-env-notification")).find((details_el) => details_el.dataset.entryKey === target_entry_key);
  if (!target_entry_el) return false;
  target_entry_el.open = true;
  target_entry_el.scrollIntoView?.({ block: "center", behavior: "smooth" });
  return true;
}
function get_event_key_total_count(env, event_key) {
  if (!event_key) return 0;
  return env?.event_logs?.get?.(event_key)?.data?.ct || 0;
}
function create_tag(feed_container, class_name, text) {
  const el = feed_container.ownerDocument.createElement("span");
  el.className = class_name;
  el.textContent = text;
  return el;
}
function append_meta_row(feed_container, container, label, value) {
  const row = feed_container.ownerDocument.createElement("div");
  row.className = "smart-env-notification__expanded-row";
  const label_el = feed_container.ownerDocument.createElement("span");
  label_el.className = "smart-env-notification__expanded-label";
  label_el.textContent = label;
  const value_el = feed_container.ownerDocument.createElement("span");
  value_el.className = "smart-env-notification__expanded-value";
  value_el.textContent = value;
  row.appendChild(label_el);
  row.appendChild(value_el);
  container.appendChild(row);
}
function format_timestamp(timestamp) {
  try {
    return new Date(timestamp).toLocaleString();
  } catch (_error) {
    return String(timestamp);
  }
}

// node_modules/obsidian-smart-env/src/modals/smart_model_modal.js
var import_obsidian18 = require("obsidian");

// node_modules/obsidian-smart-env/src/modals/smart_model_modal.css
var smart_model_modal_default = ".modal-content.smart-model-modal .setting-component:has(.dropdown-no-options) {\r\n  display: block;\r\n}";

// node_modules/obsidian-smart-env/src/utils/render_settings_config.js
var import_obsidian17 = require("obsidian");

// node_modules/obsidian-smart-env/src/utils/settings_config_utils.js
function ensure_settings_config(settings_config13, scope) {
  try {
    if (typeof settings_config13 === "function") {
      settings_config13 = settings_config13(scope);
    }
  } catch (e) {
    console.error("Error evaluating settings_config function:", e);
    settings_config13 = { error: { name: "Error", description: `Failed to load settings. ${e.message} (logged to console)` } };
  }
  return settings_config13;
}
function build_settings_group_map(settings_config13, scope, default_group_name) {
  const resolved_settings_config = ensure_settings_config(settings_config13, scope);
  return Object.entries(resolved_settings_config || {}).reduce((acc, [key, config]) => {
    const group = config.group || default_group_name;
    if (!acc[group]) acc[group] = {};
    acc[group][key] = config;
    return acc;
  }, { [default_group_name]: {} });
}
function resolve_group_settings_config(settings_config13, scope, group_name, default_group_name) {
  const group_map = build_settings_group_map(settings_config13, scope, default_group_name);
  return group_map[group_name] || {};
}

// node_modules/obsidian-smart-env/src/utils/render_settings_config.js
var SettingGroupPolyfill = class {
  constructor(container) {
    this.components = [];
    this.groupEl = container.createDiv("setting-group");
    this.headerEl = this.groupEl.createDiv("setting-item setting-item-heading");
    this.headerInnerEl = this.headerEl.createDiv("setting-item-name");
    this.controlEl = this.headerEl.createDiv("setting-item-control");
    this.listEl = this.groupEl.createDiv("setting-items");
  }
  setHeading(heading) {
    this.headerInnerEl.setText(heading);
  }
  addSetting(callback) {
    const setting = new import_obsidian17.Setting(this.listEl);
    this.components.push(setting);
    callback(setting);
    return setting;
  }
  addClass(class_name) {
    this.groupEl.addClass(class_name);
  }
};
function render_settings_config(settings_config13, scope, container, params = {}) {
  const {
    default_group_name = "Settings"
  } = params;
  const settings_config_source = settings_config13;
  const group_map = build_settings_group_map(settings_config13, scope, default_group_name);
  const group_params = params.group_params || {};
  const settings_groups = Object.entries(group_map).sort(([a], [b]) => {
    const a_order = group_params[a]?.order ?? Number.POSITIVE_INFINITY;
    const b_order = group_params[b]?.order ?? Number.POSITIVE_INFINITY;
    if (a_order !== b_order) {
      return a_order - b_order;
    }
    return a === default_group_name ? -1 : b === default_group_name ? 1 : 0;
  }).filter(([, group_config]) => Object.keys(group_config).length > 0).map(([group_name, group_config]) => {
    const group_container = container.createDiv();
    const group_params2 = {
      ...params,
      ...params.group_params?.[group_name] || {},
      settings_config_source
    };
    return render_settings_group(
      group_name,
      scope,
      group_config,
      group_container,
      group_params2
    );
  });
  return settings_groups;
}
function render_settings_group(group_name, scope, settings_config13, container, params = {}) {
  const settings_config_source = params.settings_config_source || settings_config13;
  const settings_config_group = params.settings_config_source ? resolve_group_settings_config(
    settings_config_source,
    scope,
    group_name,
    params.default_group_name || "Settings"
  ) : settings_config13;
  let SettingGroup;
  try {
    const obsidian_module = require("obsidian");
    if (obsidian_module.SettingGroup) {
      SettingGroup = obsidian_module.SettingGroup;
    } else {
      SettingGroup = SettingGroupPolyfill;
    }
  } catch (e) {
    SettingGroup = SettingGroupPolyfill;
  }
  settings_config13 = settings_config_group;
  const {
    heading_btn = null
  } = params;
  const render_group = params.settings_config_source ? (group_name2, scope2, settings_config14, container2, group_params) => {
    const group_config = resolve_group_settings_config(
      settings_config14,
      scope2,
      group_name2,
      group_params.default_group_name || "Settings"
    );
    return render_settings_group(group_name2, scope2, group_config, container2, group_params);
  } : render_settings_group;
  const rerender_settings_group = create_settings_group_rerender(scope, {
    container,
    group_name,
    settings_config: settings_config_source,
    group_params: params,
    render_group
  });
  let setting_group = new SettingGroup(container);
  if (heading_btn && typeof heading_btn === "object") {
    if (Array.isArray(heading_btn)) {
      for (const btn_config of heading_btn) {
        render_heading_button(setting_group, scope, btn_config);
      }
    } else {
      render_heading_button(setting_group, scope, heading_btn);
    }
  }
  setting_group.setHeading(group_name);
  for (const [setting_path, setting_config] of Object.entries(settings_config13)) {
    if (!setting_config || typeof setting_config !== "object") {
      console.warn(`Invalid setting config for ${setting_path}:`, setting_config);
      continue;
    }
    const settng_is_pro = setting_config.scope_class === "pro-setting";
    const env_is_pro = !!scope.env?.is_pro || !!scope.is_pro;
    setting_group.addSetting((setting) => {
      if (setting_config.name) setting.setName(setting_config.name);
      setting.setClass(setting_path.replace(/[^a-zA-Z0-9]/g, "-"));
      if (setting_config.type) setting.setClass(`setting-type-${setting_config.type}`);
      if (setting_config.description) {
        setting.setDesc(setting_config.description);
      }
      switch (setting_config.type) {
        case "button":
          setting.addButton((btn) => {
            btn.setButtonText(setting_config.name || "Run");
            btn.onClick(async (event) => {
              if (typeof setting_config.callback === "function") {
                await handle_config_callback(setting, event, setting_config.callback, { scope });
              }
            });
          });
          break;
        case "toggle":
          setting.addToggle((toggle) => {
            toggle.setValue(get_by_path(scope.settings, setting_path) || false);
            toggle.onChange((value) => {
              if (settng_is_pro && !env_is_pro) {
                scope?.env?.events?.emit?.("settings:pro_feature_blocked", {
                  level: "warning",
                  message: "Nice try! This is a PRO feature. Please upgrade to access this setting.",
                  event_source: "render_settings_config.toggle"
                });
                return;
              }
              set_by_path(scope.settings, setting_path, value);
              if (typeof setting_config.callback === "function") {
                handle_config_callback(setting, value, setting_config.callback, { scope });
              }
            });
          });
          break;
        case "text":
          setting.addText((text) => {
            text.setValue(String(get_by_path(scope.settings, setting_path) || ""));
            text.onChange((value) => {
              set_by_path(scope.settings, setting_path, value);
            });
          });
          break;
        case "password":
          setting.addText((text) => {
            text.setValue(String(get_by_path(scope.settings, setting_path) || ""));
            text.inputEl.setAttribute("type", "password");
            text.onChange((value) => {
              set_by_path(scope.settings, setting_path, value);
            });
          });
          break;
        case "number":
          setting.addText((text) => {
            text.setValue(String(get_by_path(scope.settings, setting_path) ?? "0"));
            text.inputEl.setAttribute("type", "number");
            text.onChange((value) => {
              const num_value = Number(value);
              if (!isNaN(num_value)) {
                set_by_path(scope.settings, setting_path, num_value);
              }
              if (typeof setting_config.callback === "function") {
                handle_config_callback(setting, num_value, setting_config.callback, { scope });
              }
            });
          });
          break;
        case "dropdown":
          setting.addDropdown(async (dropdown) => {
            const options_callback = setting_config.options_callback;
            if (typeof options_callback === "function") {
              const options = await options_callback.call(scope, scope);
              options.forEach((opt) => {
                const label = opt.label || opt.name || opt.value;
                dropdown.addOption(opt.value, label);
              });
            }
            dropdown.setValue(get_by_path(scope.settings, setting_path) || "");
            dropdown.onChange((value) => {
              set_by_path(scope.settings, setting_path, value);
              if (typeof setting_config.callback === "function") {
                handle_config_callback(setting, value, setting_config.callback, { scope });
              }
              rerender_settings_group();
            });
          });
          break;
        case "textarea":
          setting.addTextArea((text) => {
            text.setValue(String(get_by_path(scope.settings, setting_path) || ""));
            text.onChange((value) => {
              if (settng_is_pro && !env_is_pro) {
                scope?.env?.events?.emit?.("settings:pro_feature_blocked", {
                  level: "warning",
                  message: "Nice try! This is a PRO feature. Please upgrade to access this setting.",
                  event_source: "render_settings_config.textarea"
                });
                return;
              }
              set_by_path(scope.settings, setting_path, value);
            });
            if (settng_is_pro && !env_is_pro) {
              text.setDisabled(true);
            }
          });
          break;
        case "slider":
          setting.addSlider((slider) => {
            const min = setting_config.min || 0;
            const max = setting_config.max || 100;
            const step = setting_config.step || 1;
            slider.setLimits(min, max, step);
            slider.setValue(get_by_path(scope.settings, setting_path) || min);
            slider.setDynamicTooltip();
            slider.onChange((value) => {
              set_by_path(scope.settings, setting_path, value);
              if (typeof setting_config.callback === "function") {
                handle_config_callback(setting, value, setting_config.callback, { scope });
              }
            });
          });
          break;
        case "heading":
          setting.setHeading();
          break;
        case "html":
          if (setting_config.value) {
            setting.descEl.replaceChildren(
              document.createRange().createContextualFragment(setting_config.value)
            );
          }
          break;
        default:
          console.warn(`Unsupported setting type for ${setting_path}:`, setting_config.type);
          break;
      }
      if (setting_config.scope_class) {
        setting.settingEl.addClass(setting_config.scope_class);
      }
      if (settng_is_pro && !env_is_pro) {
        setting.setDisabled(true);
      }
    });
  }
  return setting_group;
}
function render_heading_button(setting_group, scope, heading_btn) {
  const btn_el = setting_group.controlEl.createEl("button", { cls: "" });
  if (heading_btn.btn_icon) {
    (0, import_obsidian17.setIcon)(btn_el, heading_btn.btn_icon);
  }
  if (heading_btn.btn_text) {
    btn_el.setText(heading_btn.btn_text);
  }
  if (heading_btn.label) {
    btn_el.setAttr("aria-label", heading_btn.label);
  }
  btn_el.addEventListener("click", async (event) => {
    if (typeof heading_btn.callback === "function") {
      await handle_config_callback(null, event, heading_btn.callback, { scope });
    } else {
      console.warn("No callback defined for heading button");
    }
  });
  setting_group.controlEl.appendChild(btn_el);
}
async function handle_config_callback(setting, event_or_value, cb, params = {}) {
  const {
    scope = null
  } = params;
  if (scope) {
    return await cb.call(scope, event_or_value, setting);
  } else {
    return await cb(event_or_value, setting);
  }
}
function create_settings_group_rerender(scope, params = {}) {
  const {
    container,
    group_name,
    settings_config: settings_config13,
    group_params = {},
    render_group
  } = params;
  return () => {
    if (!container || typeof render_group !== "function") return null;
    container.replaceChildren();
    return render_group(group_name, scope, settings_config13, container, group_params);
  };
}

// node_modules/obsidian-smart-env/src/modals/smart_model_modal.js
var SmartModelModal = class extends import_obsidian18.Modal {
  /**
   * @param {App} app
   * @param {EditModelModalOpts} opts
   */
  constructor(model, params = {}) {
    const app2 = model.env.plugin.app || window.app;
    super(app2);
    this.model = model;
    this.collection = this.model.collection;
    this.env = this.model.env;
    this.params = params;
  }
  onOpen() {
    this.titleEl.setText("Edit model");
    this.contentEl.addClass("smart-model-modal");
    this.render_form();
  }
  onClose() {
    this.contentEl.empty();
    if (typeof this.params.on_close === "function") {
      this.params.on_close();
    }
  }
  async render_form() {
    const container = this.contentEl;
    container.empty();
    const model = this.model;
    const model_actions_bar = await this.env.smart_components.render_component("settings_model_actions", model, {
      // these callbacks should probably be handled via events instead
      on_before_new: async () => {
        this.close();
      },
      on_after_delete: async () => {
        this.close();
      }
    });
    container.appendChild(model_actions_bar);
    const settings = model.settings_config;
    this.env.smart_view.apply_style_sheet(smart_model_modal_default);
    const form_container = container.createDiv({ cls: "smart-model-settings-form" });
    render_settings_config(settings, model, form_container, {
      default_group_name: "Model settings"
    });
    const test_btn = container.createEl("button", { text: "Test model" });
    const test_results_el = container.createDiv({ cls: "model-test-container" });
    test_btn.addEventListener("click", async () => {
      await this.run_test(test_results_el, model);
    });
    if (this.params.test_on_open) {
      await this.run_test(test_results_el, model);
    }
  }
  async run_test(test_results_el, model) {
    test_results_el.empty();
    const test_result_el = test_results_el.createEl("pre", { cls: "model-test-result", text: "Testing..." });
    test_results_el.appendChild(test_result_el);
    const test_result = await model.test_model();
    test_result_el.textContent = JSON.stringify(test_result, null, 2);
  }
};

// node_modules/obsidian-smart-env/src/components/settings/env_model.css
var env_model_default = '.model-settings .model-info {\r\n  border-radius: var(--radius-m);\r\n  padding: 1rem;\r\n  margin-bottom: 1rem;\r\n  background-color: var(--background-secondary);\r\n  pre {\r\n    margin: 0;\r\n    font-size: 0.9rem;\r\n  }\r\n  .test-result-icon {\r\n    vertical-align: middle;\r\n    margin-left: 0.5rem;\r\n  }\r\n  .test-result-icon[data-icon="square-check-big"]{\r\n    color: var(--color-green);\r\n  }\r\n  .test-result-icon[data-icon="circle-x"]{\r\n    color: var(--color-red);\r\n  }\r\n}\r\n\r\n.smart-model-modal{\r\n   pre, .model-note {\r\n    user-select: text;\r\n  }\r\n}';

// node_modules/obsidian-smart-env/src/components/settings/env_model.js
var import_obsidian19 = require("obsidian");
function build_html8(model, params) {
  const details = [
    `Provider: ${model.data.provider_key}`,
    `Model: ${model.data.model_key || "**MISSING - EDIT & SELECT MODEL**"}`
  ];
  return `<div class="model-info">
    <div class="smart-env-settings-header">
      <b>Current: ${model.display_name} <span class="test-result-icon" data-icon="${get_test_result_icon_name(model)}"></span></b>
      <div>
        <button class="edit-model">Edit</button>
        <button class="test-model">Test</button>
      </div>
    </div>
    <pre>${details.join("\n")}</pre>
  </div>`;
}
async function render10(model, params) {
  this.apply_style_sheet(env_model_default);
  const frag = this.create_doc_fragment(build_html8.call(this, model, params));
  const container = frag.firstElementChild;
  post_process7.call(this, model, container, params);
  return container;
}
async function post_process7(model, container, params) {
  const edit_btn = container.querySelector(".edit-model");
  const test_btn = container.querySelector(".test-model");
  const icon_el = container.querySelector(".test-result-icon");
  (0, import_obsidian19.setIcon)(icon_el, get_test_result_icon_name(model));
  edit_btn.addEventListener("click", () => {
    new SmartModelModal(model).open();
  });
  test_btn.addEventListener("click", () => {
    new SmartModelModal(model, { test_on_open: true }).open();
  });
  return container;
}
function get_test_result_icon_name(model) {
  switch (model.data.test_passed) {
    case true:
      return "square-check-big";
    case false:
      return "circle-x";
    default:
      return "square";
  }
}

// node_modules/obsidian-smart-env/src/utils/smart-models/show_new_model_menu.js
var import_obsidian20 = require("obsidian");

// node_modules/obsidian-smart-env/src/utils/smart-models/provider_options.js
var provider_options = {
  chat_completion_models: [
    {
      label: "Open Router (cloud)",
      value: "open_router"
    },
    {
      label: "PRO: LM Studio (local, requires LM Studio app)",
      value: "lm_studio",
      disabled: true
    },
    {
      label: "PRO: Ollama (local, requires Ollama app)",
      value: "ollama",
      disabled: true
    },
    {
      label: "PRO: OpenAI (cloud)",
      value: "openai",
      disabled: true
    },
    {
      label: "PRO: Google Gemini (cloud)",
      value: "google",
      disabled: true
    },
    {
      label: "PRO: Cohere (cloud)",
      value: "cohere",
      disabled: true
    },
    {
      label: "PRO: xAI Grok (cloud)",
      value: "xai",
      disabled: true
    },
    {
      label: "PRO: Anthropic Claude (cloud)",
      value: "anthropic",
      disabled: true
    },
    {
      label: "PRO: Deepseek (cloud)",
      value: "deepseek",
      disabled: true
    },
    {
      label: "PRO: Azure OpenAI (cloud)",
      value: "azure",
      disabled: true
    },
    {
      label: "Experimental: Lite LLM (self-hosted proxy)",
      value: "litellm",
      disabled: true
    }
  ],
  embedding_models: [
    {
      label: "Transformers (easy, local, built-in)",
      value: "transformers"
    },
    {
      label: "PRO: LM Studio (local, requires LM Studio app)",
      value: "lm_studio",
      disabled: true
    },
    {
      label: "PRO: Ollama (local, requires Ollama app)",
      value: "ollama",
      disabled: true
    },
    {
      label: "PRO: OpenAI (cloud)",
      value: "openai",
      disabled: true
    },
    {
      label: "PRO: Google Gemini (cloud)",
      value: "gemini",
      disabled: true
    },
    {
      label: "PRO: Open Router (cloud)",
      value: "open_router",
      disabled: true
    }
  ],
  ranking_models: [
    {
      label: "PRO: Cohere (cloud)",
      value: "cohere",
      disabled: true
    }
  ]
};

// node_modules/obsidian-smart-env/src/utils/smart-models/show_new_model_menu.js
function show_new_model_menu(models_collection, event, params = {}) {
  const providers = (provider_options[models_collection.collection_key] || []).map((p) => ({ ...p, disabled: !models_collection.env_config.providers[p.value] }));
  console.log("show_new_model_menu providers", providers);
  if (providers.length === 0) {
    if (event.target.tagName.toLowerCase() === "button") {
      event.target.disabled = true;
      event.title = "No providers available to create new models.";
    }
  } else {
    const menu = new import_obsidian20.Menu();
    providers.forEach((provider) => {
      menu.addItem((item) => {
        item.setTitle(provider.label);
        if (provider.disabled) {
          item.setDisabled(true);
        }
        item.onClick(async () => {
          if (typeof params.on_before_new === "function") {
            await params.on_before_new();
          }
          const model = models_collection.new_model({ provider_key: provider.value });
          const on_new_close = async () => {
          };
          new SmartModelModal(model, { on_close: on_new_close }).open();
        });
      });
    });
    menu.showAtMouseEvent(event);
  }
}

// node_modules/obsidian-smart-env/src/components/settings/env_model_type.js
function build_html9(models_collection, params) {
  return `<div class="model-settings" data-model-type="${models_collection.collection_key}">
    <div class="global-settings"></div>
  </div>`;
}
async function render11(models_collection, params) {
  const frag = this.create_doc_fragment(build_html9.call(this, models_collection, params));
  const container = frag.firstElementChild;
  post_process8.call(this, models_collection, container, params);
  return container;
}
async function post_process8(models_collection, container, params) {
  const disposers = [];
  const render_current_model_info = async (current_model) => {
    this.empty(container);
    const [settings_group] = render_settings_config(
      models_collection.env_config.settings_config,
      models_collection,
      container,
      {
        default_group_name: `${models_collection.model_type} models`,
        heading_btn: {
          btn_text: "+ New",
          callback: (event, setting) => {
            show_new_model_menu(models_collection, event);
          }
        }
      }
    );
    models_collection.env.smart_components.render_component("settings_env_model", current_model, {}).then((model_info_el) => {
      settings_group.listEl.appendChild(model_info_el);
    });
  };
  render_current_model_info(models_collection.default);
  disposers.push(models_collection.on_event("settings:changed", async (payload) => {
    const default_setting_path = `${models_collection.collection_key}.default_model_key`;
    if (payload.path_string === default_setting_path) {
      await render_current_model_info(models_collection.default);
    }
  }));
  disposers.push(models_collection.on_event("model:changed", async () => {
    await render_current_model_info(models_collection.default);
  }));
  this.attach_disposer(container, disposers);
}

// node_modules/obsidian-smart-env/src/components/settings/env_models.js
function build_html10(env, params) {
  const models_collections = [
    env.embedding_models,
    env.chat_completion_models,
    env.ranking_models
  ].filter(Boolean);
  const type_containers = models_collections.map((models_collection) => {
    return `<div data-collection-key="${models_collection.collection_key}"></div>`;
  }).join("\n");
  return `<div class="env-model-types">
    ${type_containers}
  </div>`;
}
async function render12(env, params) {
  const frag = this.create_doc_fragment(build_html10(env, params));
  const container = frag.firstElementChild;
  post_process9.call(this, env, container, params);
  return container;
}
async function post_process9(env, container, params) {
  const collection_containers = container.querySelectorAll("div[data-collection-key]");
  for (const collection_container of collection_containers) {
    const collection_key = collection_container.getAttribute("data-collection-key");
    const models_collection = env[collection_key];
    env.smart_components.render_component("settings_env_model_type", models_collection).then((model_type_el) => {
      this.empty(collection_container);
      collection_container.appendChild(model_type_el);
    });
  }
  return container;
}

// node_modules/obsidian-smart-env/src/modals/exclude_folders_fuzzy.js
var import_obsidian21 = require("obsidian");

// node_modules/obsidian-smart-env/src/utils/exclusions.js
function ensure_smart_sources_settings(env) {
  if (!env.settings) env.settings = {};
  if (!env.settings.smart_sources) env.settings.smart_sources = {};
  const smart_sources_settings = env.settings.smart_sources;
  if (!smart_sources_settings.folder_exclusions) smart_sources_settings.folder_exclusions = "";
  if (!smart_sources_settings.file_exclusions) smart_sources_settings.file_exclusions = "";
  return smart_sources_settings;
}
function parse_exclusions_csv(exclusions = "") {
  return exclusions.split(",").map((value) => value.trim()).filter(Boolean);
}
function add_exclusion(exclusions, value) {
  const trimmed = (value ?? "").trim();
  if (!trimmed) return exclusions || "";
  const current = parse_exclusions_csv(exclusions);
  if (!current.includes(trimmed)) current.push(trimmed);
  return current.join(",");
}
function remove_exclusion(exclusions, value) {
  const trimmed = (value ?? "").trim();
  if (!trimmed) return exclusions?.trim() || "";
  const filtered = parse_exclusions_csv(exclusions).filter((entry) => entry !== trimmed);
  return filtered.join(",");
}

// node_modules/obsidian-smart-env/src/modals/exclude_folders_fuzzy.js
var ExcludedFoldersFuzzy = class extends import_obsidian21.FuzzySuggestModal {
  /**
   * @param {App} app - The Obsidian app
   * @param {Object} env - An environment-like object, must have .settings and .fs.folder_paths
   */
  constructor(app2, env) {
    super(app2);
    this.env = env;
    this.setPlaceholder("Select a folder to exclude...");
  }
  /**
   * Open the modal with an optional callback invoked after an item is chosen.
   * The current exclusion list is rendered at the top of the modal.
   * @param {Function} [selection_callback]
   */
  open(selection_callback) {
    this.callback = selection_callback;
    super.open();
    this.render_excluded_list();
  }
  /**
   * Return candidate folder paths that are not already excluded.
   * @returns {string[]}
   */
  getItems() {
    const smart_sources_settings = ensure_smart_sources_settings(this.env);
    const folder_exclusions2 = parse_exclusions_csv(smart_sources_settings.folder_exclusions);
    const candidates = (this.env.smart_sources?.fs?.folder_paths || []).filter((path) => !folder_exclusions2.includes(path));
    return candidates;
  }
  getItemText(item) {
    return item;
  }
  /**
   * Handle selecting a folder to exclude.
   * @param {string} item
   */
  onChooseItem(item) {
    this.prevent_close = true;
    if (!item) return;
    const smart_sources_settings = ensure_smart_sources_settings(this.env);
    smart_sources_settings.folder_exclusions = add_exclusion(smart_sources_settings.folder_exclusions, item);
    this.render_excluded_list();
    this.updateSuggestions();
    this.callback?.();
  }
  /**
   * Render the current list of excluded folders at the top of the modal,
   * with inline remove buttons.
   */
  render_excluded_list() {
    if (!this.modalEl) return;
    const smart_sources_settings = ensure_smart_sources_settings(this.env);
    const excluded_folders = parse_exclusions_csv(smart_sources_settings.folder_exclusions);
    let header = this.modalEl.querySelector(".sc-excluded-folders-header");
    if (!header) {
      header = this.modalEl.createEl("div", { cls: "sc-excluded-folders-header" });
      this.modalEl.prepend(header);
    }
    header.empty();
    const title_el = header.createEl("h3");
    title_el.setText("Excluded folders");
    if (!excluded_folders.length) {
      const empty_el = header.createEl("p");
      empty_el.setText("No folders excluded yet.");
      return;
    }
    const list_el = header.createEl("ul");
    excluded_folders.forEach((folder_path) => {
      const li = list_el.createEl("li", { cls: "excluded-folder-item" });
      li.setText(folder_path + "  ");
      const remove_btn = li.createEl("button", {
        text: "(x)",
        cls: "remove-excluded-folder-btn"
      });
      remove_btn.addEventListener("click", () => {
        smart_sources_settings.folder_exclusions = remove_exclusion(
          smart_sources_settings.folder_exclusions,
          folder_path
        );
        this.env.update_exclusions?.();
        this.render_excluded_list();
        this.updateSuggestions();
      });
    });
  }
  close() {
    setTimeout(() => {
      if (!this.prevent_close) super.close();
      this.prevent_close = false;
    }, 10);
  }
};

// node_modules/obsidian-smart-env/src/modals/excluded_sources.js
var import_obsidian22 = require("obsidian");
var ExcludedSourcesModal = class extends import_obsidian22.Modal {
  /**
   * @param {Object} app - Obsidian app
   * @param {Object} env - The environment instance
   */
  constructor(app2, env) {
    super(app2);
    this.env = env;
  }
  async onOpen() {
    this.titleEl.setText("Excluded Sources");
    this.contentEl.addClass("excluded-sources-modal");
    this.render_excluded_list();
  }
  async render_excluded_list() {
    this.contentEl.empty();
    const list_el = this.contentEl.createEl("ul");
    const excluded_file_paths = this.env.smart_sources.excluded_file_paths;
    const too_long_files = this.app.vault.getMarkdownFiles().filter((file) => file.path.length > 200).map((file) => file.path);
    for (const file_path of excluded_file_paths) {
      const li = list_el.createEl("li");
      li.setText(file_path);
    }
    this.contentEl.createEl("hr");
    this.contentEl.createEl("h3", { text: "Paths too long to import into Smart Environment" });
    const too_long_list_ul = this.contentEl.createEl("ul", { cls: "too-long-exclusions" });
    for (const file_path of too_long_files) {
      const li = too_long_list_ul.createEl("li");
      li.setText(file_path);
    }
  }
};

// node_modules/obsidian-smart-env/src/components/settings/env_sources.js
async function build_html11(env, opts = {}) {
  return `
    <div class="sources-settings">
    </div>
  `;
}
async function render13(env, opts = {}) {
  const html = await build_html11.call(this, env, opts);
  const frag = this.create_doc_fragment(html);
  const container = frag.firstElementChild;
  post_process10.call(this, env, container, opts);
  return container;
}
async function post_process10(env, container, opts = {}) {
  const settings_config13 = {
    folder_exclusions,
    view_exclusions,
    // reset_env_settings_btn, // TODO: manually tested before implementing reset button
    re_import_sources
  };
  render_settings_config(settings_config13, env, container, {
    default_group_name: "Sources",
    heading_btn: {
      btn_icon: "help-circle",
      callback: () => {
        window.open("https://smartconnections.app/smart-environment/settings/?utm_source=source-settings", "_external");
      }
    }
  });
  const disposers = [];
  disposers.push(env.events?.on("model:changed", highlight_reset_data(env, container)));
  this.attach_disposer(container, disposers);
  return container;
}
function highlight_reset_data(env, container) {
  return async (payload) => {
    if (payload.collection_key !== "embedding_models") return;
    const re_import_setting = container.querySelector(".re-import-sources");
    re_import_setting.classList.add("env-setting-highlight");
    const notice = re_import_setting.querySelector(".reimport-notice") ? re_import_setting.querySelector(".reimport-notice") : re_import_setting.createEl("div", { cls: "reimport-notice env-setting-note" });
    notice.textContent = "Embedding model changed. Please re-import your sources to update their embeddings.";
    re_import_setting.appendChild(notice);
    env.events.once("sources:reimported", () => {
      re_import_setting.classList.remove("env-setting-highlight");
      notice.remove();
    });
  };
}
var folder_exclusions = {
  type: "button",
  name: "Manage excluded folders",
  description: "Manage the list of folders excluded from processing.",
  btn_text: "Manage folders",
  callback: async function() {
    const env = this;
    const fuzzy = new ExcludedFoldersFuzzy(env.main.app, env);
    const selection_callback = () => {
      env.update_exclusions();
    };
    fuzzy.open(selection_callback);
  }
};
var view_exclusions = {
  type: "button",
  name: "View all exclusions",
  description: "View all excluded sources.",
  btn_text: "Show",
  callback: async function() {
    const env = this;
    const modal = new ExcludedSourcesModal(env.main.app, env);
    modal.open();
  }
};
var re_import_sources = {
  type: "button",
  name: "Reset data",
  description: "Clear sources data and re-import.",
  btn_text: "Re-import sources",
  callback: async function(value, setting) {
    const env = this;
    const container = setting.controlEl;
    const confirm_row = container.createEl("div", { cls: "sc-inline-confirm-row" });
    const reimport_btn = container.querySelector("button");
    reimport_btn.style.display = "none";
    confirm_row.setText("Are you sure you want to clear all sources data? This cannot be undone.");
    let confirm_cancel = confirm_row.createEl("button", { text: "Cancel" });
    let confirm_yes = confirm_row.createEl("button", { text: "Re-import", cls: "mod-warning" });
    confirm_yes.addEventListener("click", async (event) => {
      confirm_cancel.style.display = "none";
      confirm_yes.textContent = "Re-importing...";
      confirm_yes.disabled = true;
      const row = event.target.closest(".sc-inline-confirm-row");
      await env.smart_sources.run_clear_all();
      const start = Date.now();
      env.smart_sources.unload();
      env.smart_blocks.unload();
      await env.init_collections();
      await env.load_collections();
      await env.smart_sources.process_embed_queue();
      const end = Date.now();
      env.events?.emit("sources:reimported", {
        time_ms: end - start,
        message: format_reimport_message(end - start),
        event_source: "settings_env_sources.re_import_sources"
      });
      row.style.display = "none";
      reimport_btn.style.display = "inline-block";
      confirm_yes.textContent = "Yes";
      confirm_yes.disabled = false;
    });
    confirm_cancel.addEventListener("click", () => {
      confirm_row.style.display = "none";
      reimport_btn.style.display = "inline-block";
    }, { once: true });
  }
};
function format_reimport_message(time_ms) {
  const seconds = Math.max(0, Math.round(time_ms / 100) / 10);
  return `Sources re-imported in ${seconds}s.`;
}

// node_modules/obsidian-smart-env/src/components/settings/model_actions.js
function build_html12(model, params = {}) {
  return `<div class="smart-model-modal-actions">
    <button class="new-model-btn">New</button>
    <button class="delete-model-btn">Delete</button>
    <div class="confirm-delete-container" style="display:none;">
      <span>Are you sure?</span>
      <button class="confirm-delete-yes-btn">Yes</button>
      <button class="confirm-delete-no-btn">No</button>
    </div>
  </div>`;
}
async function render14(model, params = {}) {
  const frag = this.create_doc_fragment(build_html12(model, params));
  const container = frag.firstElementChild;
  post_process11.call(this, model, container, params);
  return container;
}
async function post_process11(model, container, params = {}) {
  const new_model_btn = container.querySelector(".new-model-btn");
  new_model_btn.addEventListener("click", async (event) => {
    const on_before_new = params.on_before_new;
    const opts = {};
    if (typeof on_before_new === "function") {
      opts.on_before_new = on_before_new;
    }
    show_new_model_menu(model.collection, event, opts);
  });
  const delete_model_btn = container.querySelector(".delete-model-btn");
  const confirm_delete_container = container.querySelector(".confirm-delete-container");
  const confirm_delete_yes_btn = container.querySelector(".confirm-delete-yes-btn");
  const confirm_delete_no_btn = container.querySelector(".confirm-delete-no-btn");
  delete_model_btn.addEventListener("click", async () => {
    confirm_delete_container.style.display = "";
  });
  confirm_delete_no_btn.addEventListener("click", async () => {
    confirm_delete_container.style.display = "none";
  });
  confirm_delete_yes_btn.addEventListener("click", async () => {
    await model.delete_model();
    if (typeof params.on_after_delete === "function") {
      params.on_after_delete();
    }
  });
  return container;
}

// node_modules/obsidian-smart-env/src/components/settings/style.css
var style_default = ".sc-env-settings-container {\n  margin: 1rem 0;\n}\n\n.smart-env-settings-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 0.5rem;\n}\n\n.toggle-env-settings-btn {\n  cursor: pointer;\n}\n\n\n.setting-group .setting-items .setting-item.env-setting-highlight {\n  border: 1px solid var(--color-accent);\n  background-color: var(--interactive-hover);\n  padding: 0.5rem;\n  margin: 0.5rem 0;\n}\n\n.settings-group {\n  .setting-item {\n    border-top: none;\n  }\n}\n\n.sc-inline-confirm-row {\n  display: flex;\n  align-items: center;\n  gap: 0.5rem;\n  margin-top: 0.5rem;\n}\n";

// node_modules/obsidian-smart-env/src/components/settings/smart_env.js
async function build_html13(env, params = {}) {
  return `<div class="smart-env-settings-container">
    <div class="sources-container">
      <h1>Sources</h1>
    </div>
    <div class="models-container">
      <h1>Models</h1>
    </div>
    <div class="notifications-container">
      <h1>Notifications</h1>
    </div>
  </div>`;
}
async function render15(env, params = {}) {
  this.apply_style_sheet(style_default);
  const html = await build_html13.call(this, env, params);
  const frag = this.create_doc_fragment(html);
  const container = frag.firstElementChild;
  post_process12.call(this, env, container, params);
  return container;
}
async function post_process12(env, container, opts = {}) {
  const models_container = container.querySelector(".models-container");
  const sources_container = container.querySelector(".sources-container");
  const notifications_container = container.querySelector(".notifications-container");
  render_if_available.call(this, "settings_env_sources", env, sources_container);
  render_if_available.call(this, "settings_env_models", env, models_container);
  render_settings_config(
    env.event_logs.settings_config,
    env.event_logs,
    notifications_container,
    {
      default_group_name: "Notifications"
    }
  );
  return container;
}
function render_if_available(component_key, env, container) {
  if (env.config.components[component_key]) {
    const placeholder = this.create_doc_fragment(`<div data-component="${component_key}"></div>`).firstElementChild;
    container.appendChild(placeholder);
    env.smart_components.render_component(component_key, env).then((comp_el) => {
      this.empty(placeholder);
      placeholder.appendChild(comp_el);
    });
  }
}

// node_modules/obsidian-smart-env/src/utils/smart-context/copy_actions.js
var import_obsidian23 = require("obsidian");

// node_modules/obsidian-smart-env/src/utils/copy_to_clipboard.js
function emit_clipboard_event(env, event_key, payload = {}) {
  if (!event_key) return;
  env?.events?.emit?.(event_key, payload);
}
async function write_with_navigator_clipboard(text) {
  if (typeof navigator === "undefined") return false;
  if (!navigator.clipboard || typeof navigator.clipboard.writeText !== "function") {
    return false;
  }
  await navigator.clipboard.writeText(text);
  return true;
}
function write_with_electron_clipboard(text) {
  if (typeof window === "undefined" || typeof window.require !== "function") {
    return false;
  }
  const electron = window.require("electron");
  if (!electron?.clipboard || typeof electron.clipboard.writeText !== "function") {
    return false;
  }
  electron.clipboard.writeText(text);
  return true;
}
function write_with_exec_command(text) {
  if (typeof document === "undefined") return false;
  if (typeof document.execCommand !== "function") return false;
  if (!document.body?.appendChild) return false;
  const textarea = document.createElement("textarea");
  textarea.value = text;
  textarea.setAttribute("readonly", "true");
  textarea.style.position = "fixed";
  textarea.style.opacity = "0";
  textarea.style.pointerEvents = "none";
  document.body.appendChild(textarea);
  textarea.focus();
  textarea.select();
  let copied = false;
  try {
    copied = Boolean(document.execCommand("copy"));
  } finally {
    textarea.remove();
  }
  return copied;
}
async function copy_to_clipboard(text = "", params = {}) {
  const value = typeof text === "string" ? text : String(text ?? "");
  const env = params.env || null;
  const event_source = params.event_source || "copy_to_clipboard";
  const success_event_key = params.success_event_key || "clipboard:copied";
  const error_event_key = params.error_event_key || "clipboard:copy_failed";
  const unavailable_event_key = params.unavailable_event_key || "clipboard:copy_unavailable";
  let last_error = null;
  try {
    if (await write_with_navigator_clipboard(value)) {
      emit_clipboard_event(env, success_event_key, {
        message: "Text copied to clipboard.",
        event_source
      });
      return true;
    }
  } catch (error) {
    last_error = error;
  }
  try {
    if (write_with_electron_clipboard(value)) {
      emit_clipboard_event(env, success_event_key, {
        message: "Text copied to clipboard.",
        event_source
      });
      return true;
    }
  } catch (error) {
    last_error = error;
  }
  try {
    if (write_with_exec_command(value)) {
      emit_clipboard_event(env, success_event_key, {
        message: "Text copied to clipboard.",
        event_source
      });
      return true;
    }
  } catch (error) {
    last_error = error;
  }
  if (last_error) {
    emit_clipboard_event(env, error_event_key, {
      level: "error",
      message: "Failed to copy text to clipboard.",
      details: last_error?.message || "",
      event_source
    });
    return false;
  }
  emit_clipboard_event(env, unavailable_event_key, {
    level: "warning",
    message: "Clipboard copy is unavailable in this environment.",
    event_source
  });
  return false;
}

// node_modules/obsidian-smart-env/src/utils/smart-context/to_md_tree.js
function normalize_context_item_key(key = "") {
  if (typeof key !== "string") return "";
  return key.trim().replace(/^external:/, "").replace(/^selection:/, "").replace(/\\+/g, "/").replace(/\/+/g, "/").replace(/^\.\//, "");
}
function parse_context_item_key(key = "") {
  const raw_key = typeof key === "string" ? key.trim() : "";
  const is_external = raw_key.startsWith("external:");
  const is_selection = raw_key.startsWith("selection:");
  const normalized_key = normalize_context_item_key(raw_key);
  let display_key = normalized_key;
  if (is_external && display_key.startsWith("../")) {
    display_key = display_key.slice(3);
  }
  return {
    raw_key,
    normalized_key,
    display_key,
    is_external,
    is_selection
  };
}
function split_path_segments(normalized_key = "") {
  if (typeof normalized_key !== "string" || normalized_key.length === 0) return [];
  return normalized_key.split("/").map((segment) => segment.trim()).filter(Boolean);
}
function format_wikilink_target(file_segment = "") {
  if (typeof file_segment !== "string") return "";
  const trimmed = file_segment.trim();
  if (!trimmed) return "";
  const hash_index = trimmed.indexOf("#");
  if (hash_index === -1) {
    return trimmed.replace(/\.md$/i, "");
  }
  const source_name = trimmed.slice(0, hash_index).replace(/\.md$/i, "");
  const fragment = trimmed.slice(hash_index);
  return `${source_name}${fragment}`;
}
function escape_markdown_link_text(text = "") {
  return String(text).replace(/\\/g, "\\\\").replace(/\]/g, "\\]");
}
function create_tree_node() {
  return {
    children: [],
    dir_nodes: /* @__PURE__ */ new Map(),
    file_keys: /* @__PURE__ */ new Set()
  };
}
function ensure_dir_node(node, dir_name) {
  const existing = node.dir_nodes.get(dir_name);
  if (existing) return existing;
  const dir_node = create_tree_node();
  node.dir_nodes.set(dir_name, dir_node);
  node.children.push({
    type: "dir",
    name: dir_name,
    node: dir_node
  });
  return dir_node;
}
function ensure_path(root_node, dir_segments = []) {
  let node = root_node;
  for (let i = 0; i < dir_segments.length; i += 1) {
    node = ensure_dir_node(node, dir_segments[i]);
  }
  return node;
}
function get_vault_adapter(smart_context) {
  const app2 = smart_context?.env?.plugin?.app || smart_context?.env?.app || smart_context?.app || globalThis.app || null;
  return app2?.vault?.adapter || null;
}
function get_vault_base_path(smart_context) {
  const adapter = get_vault_adapter(smart_context);
  if (!adapter) return "";
  if (typeof adapter.getBasePath === "function") {
    const base_path = adapter.getBasePath();
    if (typeof base_path === "string" && base_path.trim()) {
      return base_path.trim();
    }
  }
  if (typeof adapter.getFullPath === "function") {
    try {
      const full_path = adapter.getFullPath("");
      if (typeof full_path === "string" && full_path.trim()) {
        return full_path.trim();
      }
    } catch (err) {
    }
  }
  if (typeof adapter.basePath === "string" && adapter.basePath.trim()) {
    return adapter.basePath.trim();
  }
  return "";
}
function encode_file_path_segments(normalized_path = "") {
  return normalized_path.split("/").map((segment, index) => {
    if (index === 0 && /^[a-zA-Z]:$/.test(segment)) {
      return segment;
    }
    return encodeURIComponent(segment);
  }).join("/");
}
function absolute_path_to_file_href(absolute_path = "") {
  const trimmed_path = typeof absolute_path === "string" ? absolute_path.trim() : "";
  if (!trimmed_path) return "";
  if (/^[a-zA-Z][a-zA-Z\d+.-]*:\/\//.test(trimmed_path)) {
    return trimmed_path;
  }
  const normalized_path = trimmed_path.replace(/\\+/g, "/");
  const encoded_path = encode_file_path_segments(normalized_path);
  if (/^[a-zA-Z]:\//.test(normalized_path)) {
    return `file:///${encoded_path}`;
  }
  if (normalized_path.startsWith("//")) {
    return `file:${encoded_path}`;
  }
  if (normalized_path.startsWith("/")) {
    return `file://${encoded_path}`;
  }
  return "";
}
function resolve_file_href_from_base_path(vault_base_path, normalized_path) {
  const base_href = absolute_path_to_file_href(vault_base_path);
  if (!base_href) return "";
  const directory_href = base_href.endsWith("/") ? base_href : `${base_href}/`;
  const encoded_relative_path = encode_file_path_segments(normalized_path);
  try {
    return new URL(encoded_relative_path, directory_href).href;
  } catch (err) {
    return "";
  }
}
function resolve_external_href(smart_context, key = "") {
  const parsed = parse_context_item_key(key);
  if (!parsed.is_external) return "";
  if (!parsed.normalized_key) return "";
  if (/^[a-zA-Z][a-zA-Z\d+.-]*:\/\//.test(parsed.normalized_key)) {
    return parsed.normalized_key;
  }
  const absolute_key_href = absolute_path_to_file_href(parsed.normalized_key);
  if (absolute_key_href) {
    return absolute_key_href;
  }
  const adapter = get_vault_adapter(smart_context);
  if (adapter && typeof adapter.getFilePath === "function") {
    try {
      const file_href = adapter.getFilePath(parsed.normalized_key);
      if (typeof file_href === "string" && file_href.trim()) {
        return file_href.trim();
      }
    } catch (err) {
    }
  }
  if (adapter && typeof adapter.getFullPath === "function") {
    try {
      const full_path = adapter.getFullPath(parsed.normalized_key);
      const full_path_href = absolute_path_to_file_href(full_path);
      if (full_path_href) {
        return full_path_href;
      }
    } catch (err) {
    }
  }
  const vault_base_path = get_vault_base_path(smart_context);
  if (!vault_base_path) {
    return parsed.normalized_key;
  }
  return resolve_file_href_from_base_path(vault_base_path, parsed.normalized_key) || parsed.normalized_key;
}
function render_tree_lines(node, depth = 0, lines = []) {
  for (let i = 0; i < node.children.length; i += 1) {
    const child = node.children[i];
    if (!child) continue;
    if (child.type === "dir") {
      lines.push(`${"	".repeat(depth)}- ${child.name}`);
      render_tree_lines(child.node, depth + 1, lines);
      continue;
    }
    if (child.type === "external_file") {
      const safe_label = escape_markdown_link_text(child.label);
      lines.push(`${"	".repeat(depth)}- [${safe_label}](${child.href})`);
      continue;
    }
    if (child.type === "file") {
      lines.push(`${"	".repeat(depth)}- [[${child.target}]]`);
    }
  }
  return lines;
}
function list_context_items(smart_context) {
  const collection = smart_context?.context_items;
  if (collection && typeof collection.filter === "function") {
    return collection.filter((item) => {
      if (!item?.key) return false;
      if (item?.data?.exclude) return false;
      return true;
    });
  }
  const raw_items = smart_context?.data?.context_items;
  if (!raw_items || typeof raw_items !== "object") return [];
  return Object.entries(raw_items).filter(([key, item_data]) => {
    if (!key) return false;
    if (item_data?.exclude) return false;
    return true;
  }).map(([key, item_data]) => ({ key, data: item_data || {} }));
}
function context_to_md_tree(smart_context) {
  const items = list_context_items(smart_context);
  if (!items.length) return "";
  const root_node = create_tree_node();
  const seen_keys = /* @__PURE__ */ new Set();
  for (let i = 0; i < items.length; i += 1) {
    const item = items[i];
    const parsed_key = parse_context_item_key(item?.key);
    if (!parsed_key.raw_key || seen_keys.has(parsed_key.raw_key)) continue;
    seen_keys.add(parsed_key.raw_key);
    const raw_folder_value = item?.data?.folder;
    const normalized_folder_value = typeof raw_folder_value === "string" ? normalize_context_item_key(raw_folder_value) : "";
    const is_folder = raw_folder_value === true || parsed_key.display_key.endsWith("/") || parsed_key.normalized_key.endsWith("/") || normalized_folder_value.length > 0 && normalized_folder_value === parsed_key.normalized_key;
    const path_segments = split_path_segments(parsed_key.display_key);
    if (!path_segments.length) continue;
    if (is_folder) {
      ensure_path(root_node, path_segments);
      continue;
    }
    const file_segment = path_segments.pop();
    const parent_node = ensure_path(root_node, path_segments);
    if (parsed_key.is_external) {
      const href = resolve_external_href(smart_context, parsed_key.raw_key);
      if (!href) continue;
      const external_key = `external:${href}`;
      if (parent_node.file_keys.has(external_key)) continue;
      parent_node.file_keys.add(external_key);
      parent_node.children.push({
        type: "external_file",
        label: file_segment,
        href
      });
      continue;
    }
    const wikilink_target = format_wikilink_target(file_segment);
    if (!wikilink_target) continue;
    const internal_key = `internal:${wikilink_target}`;
    if (parent_node.file_keys.has(internal_key)) continue;
    parent_node.file_keys.add(internal_key);
    parent_node.children.push({
      type: "file",
      target: wikilink_target
    });
  }
  return render_tree_lines(root_node).join("\n");
}

// node_modules/obsidian-smart-env/src/utils/smart-context/copy_actions.js
function has_active_context_items(ctx) {
  return Number(ctx?.item_count || 0) > 0;
}
function has_any_context_state(ctx) {
  return has_active_context_items(ctx) || Number(ctx?.excluded_item_count || 0) > 0;
}
function show_menu_at_button(button, event, menu) {
  if (typeof MouseEvent !== "undefined" && event instanceof MouseEvent) {
    menu.showAtMouseEvent(event);
    return;
  }
  const rect = button.getBoundingClientRect();
  if (typeof menu.showAtPosition === "function") {
    menu.showAtPosition({ x: rect.left, y: rect.bottom });
    return;
  }
  menu.showAtMouseEvent(new MouseEvent("contextmenu", {
    bubbles: true,
    cancelable: true,
    clientX: rect.left,
    clientY: rect.bottom
  }));
}
function create_button(container, params = {}) {
  const button = container.createEl("button", {
    text: params.icon_only ? "" : params.text || ""
  });
  if (params.icon) {
    button.classList.add("clickable-icon");
    (0, import_obsidian23.setIcon)(button, params.icon);
  }
  if (params.aria_label || params.text) {
    button.setAttribute("aria-label", params.aria_label || params.text);
    if (!params.icon_only && !params.text) {
      button.textContent = params.aria_label;
    }
  }
  return button;
}
function get_help_url(ctx) {
  const ctx_key = String(ctx?.key || "");
  if (ctx_key.endsWith("#codeblock")) {
    return "https://smartconnections.app/smart-context/codeblock/?utm_source=context-actions";
  }
  return "https://smartconnections.app/smart-context/builder/?utm_source=context-actions";
}
async function copy_link_tree(ctx) {
  const md_tree = context_to_md_tree(ctx).trim();
  if (!md_tree) {
    ctx.emit_event("context:copy_empty", {
      level: "warning",
      message: "No context items to copy.",
      event_source: "smart_context.copy_link_tree"
    });
    return false;
  }
  const copied = await copy_to_clipboard(md_tree, {
    env: ctx.env,
    event_source: "smart_context.copy_link_tree",
    success_event_key: "context:clipboard_raw_copied",
    error_event_key: "context:clipboard_raw_copy_failed",
    unavailable_event_key: "context:clipboard_copy_unavailable"
  });
  if (!copied) return false;
  ctx.emit_event("context:link_tree_copied", {
    level: "info",
    message: "Copied link tree to clipboard.",
    event_source: "smart_context.copy_link_tree"
  });
  return true;
}
function register_copy_menu_actions(env) {
  env.register_menu_action("smart_context:copy_menu", (menu, ctx) => {
    if (!menu || !ctx || !has_active_context_items(ctx)) return;
    const descriptors = [
      {
        key: "copy_text",
        title: "Copy text",
        icon: "copy",
        run: async () => {
          return await ctx.actions.context_copy_to_clipboard({ with_media: false });
        }
      },
      {
        key: "copy_link_tree",
        title: "Copy link tree",
        icon: "list-tree",
        order: 3,
        run: async () => {
          return await copy_link_tree(ctx);
        }
      }
    ];
    descriptors.forEach((descriptor) => {
      menu_add_from_desc(menu, descriptor);
    });
  });
}
function register_context_menu_actions(env) {
  env.register_menu_action("smart_context:actions_menu", (menu, ctx) => {
    if (!menu || !ctx || !has_any_context_state(ctx)) return;
    const descriptors = [
      {
        key: "final_separator",
        separator: true,
        order: 998
      },
      {
        key: "clear_context",
        title: "Clear this context",
        icon: "rotate-ccw",
        order: 999,
        run: async () => {
          ctx.clear_all?.();
          return true;
        }
      }
    ];
    descriptors.forEach((descriptor) => {
      menu_add_from_desc(menu, descriptor);
    });
  });
}
function menu_add_from_desc(menu, descriptor) {
  if (descriptor.separator) {
    menu.addSeparator();
    menu.items[menu.items.length - 1]._order = descriptor.order || 0;
    return;
  }
  menu.addItem((mi) => {
    mi.setTitle(descriptor.title).setIcon(descriptor.icon).onClick(async () => {
      await descriptor.run();
    });
    mi._order = descriptor.order || 0;
  });
}
function build_context_actions_menu(ctx, menu, params = {}) {
  if (!ctx || !menu) return menu;
  ctx?.env?.build_menu?.("smart_context:copy_menu", menu, ctx);
  ctx?.env?.build_menu?.("smart_context:actions_menu", menu, ctx);
  if (menu.items?.sort) {
    menu.items.sort((a, b) => {
      const order_a = a._order || 0;
      const order_b = b._order || 0;
      return order_a - order_b;
    });
  }
  return menu;
}
function render_btn_quick_copy(ctx, container, params = {}) {
  if (!has_active_context_items(ctx)) return null;
  const button = create_button(container, {
    icon: "smart-copy-note",
    icon_only: true,
    aria_label: "Smart Copy"
  });
  button.addEventListener("click", async () => {
    await ctx.actions.context_copy_to_clipboard();
  });
  return button;
}
function render_btn_copy_menu(ctx, container, params = {}) {
  if (!has_active_context_items(ctx)) return null;
  const app2 = ctx?.env?.plugin?.app || ctx?.env?.obsidian_app || window?.app || globalThis?.app || null;
  if (!app2) return null;
  const button = create_button(container, {
    icon: "chevron-down",
    text: "Copy",
    aria_label: "Open copy menu"
  });
  const open_menu = (event) => {
    const menu = new import_obsidian23.Menu(app2);
    build_context_actions_menu(ctx, menu, params);
    show_menu_at_button(button, event, menu);
  };
  button.addEventListener("click", (event) => {
    event.preventDefault();
    open_menu(event);
  });
  button.addEventListener("keydown", (event) => {
    if (event.key !== "Enter" && event.key !== " ") return;
    event.preventDefault();
    open_menu(event);
  });
  return button;
}
function render_btn_clear_context(ctx, container) {
  if (!has_any_context_state(ctx)) return null;
  const button = create_button(container, {
    text: "Clear",
    aria_label: "Clear context",
    icon: "rotate-ccw",
    icon_only: true
  });
  button.addEventListener("click", () => {
    if (!button.dataset.confirmed) {
      button.dataset.confirmed = "true";
      button.style.backgroundColor = "var(--color-red)";
      return;
    }
    ctx.clear_all?.();
    button.dataset.confirmed = "";
    button.style.backgroundColor = "";
  });
  return button;
}
function render_btn_help(ctx, container) {
  const button = create_button(container, {
    icon: "help-circle",
    aria_label: "Help",
    icon_only: true
  });
  button.addEventListener("click", () => {
    const url = get_help_url(ctx);
    if (typeof globalThis.open === "function") {
      globalThis.open(url, "_external");
    }
  });
  return button;
}

// node_modules/obsidian-smart-env/src/components/smart-context/actions.js
function build_html14() {
  return `
    <div class="sc-context-actions">
      <div class="sc-context-actions-left">
      </div>
      <div class="sc-context-actions-right">
      </div>
    </div>
  `;
}
async function render16(ctx, opts = {}) {
  const html = build_html14();
  const frag = this.create_doc_fragment(html);
  const container = frag.firstElementChild;
  post_process13.call(this, ctx, container, opts);
  return container;
}
async function post_process13(ctx, container, opts = {}) {
  const render_ctx_actions = () => {
    const actions_left = container.querySelector(".sc-context-actions-left");
    this.empty(actions_left);
    ctx.env.smart_components.render_component("smart_context_meta", ctx, opts).then((meta) => {
      if (meta) actions_left.appendChild(meta);
    });
    const actions_right = container.querySelector(".sc-context-actions-right");
    this.empty(actions_right);
    render_btn_quick_copy(ctx, actions_right);
    render_btn_copy_menu(ctx, actions_right, opts);
    render_btn_clear_context(ctx, actions_right);
    render_btn_help(ctx, actions_right);
  };
  render_ctx_actions();
  const disposers = [];
  disposers.push(ctx.on_event("context:updated", render_ctx_actions));
  this.attach_disposer(container, disposers);
  return container;
}

// node_modules/obsidian-smart-env/src/components/smart-context/styles.css
var styles_default = "/* Modal view adjustments */\r\n.modal-container .sc-context-view {\r\n  max-height: 42vh;\r\n  display: flex;\r\n  flex-direction: column;\r\n\r\n  .sc-context-view-body {\r\n    overflow: auto;\r\n    display: flex;\r\n    flex-direction: column;\r\n    gap: var(--size-4-2);\r\n  }\r\n\r\n  .sc-context-view-header {\r\n    padding: var(--size-4-2);\r\n  }\r\n\r\n  .sc-context-actions {\r\n    display: flex;\r\n    justify-content: space-between;\r\n  }\r\n\r\n  .sc-context-actions-left,\r\n  .sc-context-actions-right {\r\n    display: flex;\r\n    gap: var(--size-4-2);\r\n    align-items: center;\r\n  }\r\n\r\n  .sc-context-view-body {\r\n    padding: var(--size-4-2);\r\n  }\r\n\r\n  .sc-context-view-footer {\r\n    padding: var(--size-4-2);\r\n  }\r\n}\r\n\r\n/* make hover popover work in builder modal */\r\n.hover-popover {\r\n  z-index: 100;\r\n}\r\n";

// node_modules/obsidian-smart-env/src/components/smart-context/item.js
var import_obsidian24 = require("obsidian");

// node_modules/obsidian-smart-env/src/utils/render_utils.js
function create_render_scheduler(render_fn) {
  let handle = null;
  let pending = false;
  let last_args = [];
  let last_result = null;
  const clear_handle = () => {
    if (handle === null) return;
    if (typeof cancelAnimationFrame === "function") {
      cancelAnimationFrame(handle);
    } else {
      clearTimeout(handle);
    }
    handle = null;
  };
  const run = async () => {
    pending = false;
    handle = null;
    last_result = await render_fn(...last_args);
    return last_result;
  };
  const schedule = (...args) => {
    last_args = args;
    if (pending) return;
    pending = true;
    if (typeof requestAnimationFrame === "function") {
      handle = requestAnimationFrame(() => {
        void run();
      });
      return;
    }
    handle = setTimeout(() => {
      void run();
    }, 0);
  };
  schedule.cancel = () => {
    pending = false;
    clear_handle();
  };
  schedule.flush = async (...args) => {
    if (args.length) {
      last_args = args;
    }
    if (!pending) {
      last_result = await render_fn(...last_args);
      return last_result;
    }
    schedule.cancel();
    last_result = await render_fn(...last_args);
    return last_result;
  };
  return schedule;
}

// node_modules/obsidian-smart-env/src/components/smart-context/item.js
function build_html15(ctx, opts = {}) {
  return `<div>
    <div class="sc-context-view" data-context-key="${ctx.data.key}">
      <div class="sc-context-view-header">
        <div class="sc-context-actions"></div>
      </div>
      <div class="sc-context-view-body">
        <div class="sc-context-tree"></div>
      </div>
      <div class="sc-context-view-footer">
        <div class="sc-context-exclusions"></div>
      </div>
    </div>
  </div>`;
}
async function render17(ctx, opts = {}) {
  const html = build_html15(ctx, opts);
  this.apply_style_sheet(styles_default);
  const frag = this.create_doc_fragment(html);
  const container = frag.querySelector(".sc-context-view");
  post_process14.call(this, ctx, container, opts);
  return container;
}
async function post_process14(ctx, container, opts = {}) {
  const disposers = [];
  const render_children = async () => {
    const component_renderers = [
      ctx.env.smart_components.render_component("smart_context_actions", ctx, opts),
      ctx.env.smart_components.render_component("smart_context_tree", ctx, opts)
    ];
    if (ctx.env._config.components.smart_context_exclusions_list) {
      component_renderers.push(ctx.env.smart_components.render_component("smart_context_exclusions_list", ctx, opts));
    }
    const [actions, tree, exclusions] = await Promise.all(component_renderers);
    const header = container.querySelector(".sc-context-view-header");
    this.empty(header);
    if (actions) header.appendChild(actions);
    const body = container.querySelector(".sc-context-view-body");
    this.empty(body);
    if (tree) body.appendChild(tree);
    const footer = container.querySelector(".sc-context-view-footer");
    this.empty(footer);
    if (exclusions) footer.appendChild(exclusions);
  };
  const schedule_render_children = create_render_scheduler(render_children);
  const plugin = ctx.env.plugin;
  const app2 = plugin?.app || window.app;
  const register = plugin?.registerDomEvent?.bind(plugin) || ((el, evt, cb) => el.addEventListener(evt, cb));
  register(container, "contextmenu", (ev) => {
    ev.preventDefault();
    ev.stopPropagation();
    if (!app2) return;
    const menu = new import_obsidian24.Menu(app2);
    build_context_actions_menu(ctx, menu, opts);
    menu.showAtMouseEvent(ev);
  });
  await render_children();
  disposers.push(ctx.on_event("context:updated", schedule_render_children));
  this.attach_disposer(container, disposers);
  return container;
}

// node_modules/obsidian-smart-env/src/components/smart-context/meta.js
function estimate_tokens(char_count) {
  return Math.ceil((char_count || 0) / 4);
}
function build_html16() {
  return `
    <div class="sc-context-meta" aria-live="polite"></div>
  `;
}
async function render18(ctx, params = {}) {
  const html = build_html16();
  const frag = this.create_doc_fragment(html);
  const container = frag.firstElementChild;
  post_process15.call(this, ctx, container, params);
  return container;
}
async function post_process15(ctx, container, params = {}) {
  const render_meta = () => {
    if (ctx?.has_context_items) {
      const chars = ctx.size || 0;
      const tokens = estimate_tokens(chars);
      container.textContent = `\u2248 ${chars.toLocaleString()} chars \xB7 ${tokens.toLocaleString()} tokens`;
    } else {
      container.textContent = "No context items selected";
    }
  };
  render_meta();
  const disposers = [];
  disposers.push(ctx.on_event("context:updated", render_meta));
  this.attach_disposer(container, disposers);
  return container;
}

// node_modules/obsidian-smart-env/src/utils/smart-context/build_path_tree.js
function build_path_tree(selected_items = []) {
  const root = create_tree_node2();
  const selected_folders = selected_items.map(get_item_key).filter((item_key) => item_key && is_folder_item_key(item_key));
  for (const item of selected_items) {
    const item_key = get_item_key(item);
    if (!item_key) continue;
    if (is_redundant_path(item_key, selected_folders)) continue;
    insert_item_path(root, {
      item_key,
      exists: item?.exists
    });
  }
  return root;
}
function get_item_key(item) {
  return item?.key || item?.path || "";
}
function create_tree_node2() {
  return { name: "", children: {}, selected: false };
}
function is_redundant_path(item_key, selected_folders) {
  return selected_folders.some((folder_key) => {
    if (folder_key === item_key) return false;
    return item_key.startsWith(`${folder_key}/`);
  });
}
function is_folder_item_key(item_key) {
  const block_idx = find_first_block_separator(item_key);
  const source_path = block_idx === -1 ? item_key : item_key.slice(0, block_idx);
  return !source_path.match(/\.[a-zA-Z0-9]+$/u);
}
function find_first_block_separator(value = "") {
  let in_wikilink = false;
  for (let i = 0; i < value.length; i++) {
    if (!in_wikilink && value.slice(i, i + 2) === "[[") {
      in_wikilink = true;
      i++;
      continue;
    }
    if (in_wikilink && value.slice(i, i + 2) === "]]") {
      in_wikilink = false;
      i++;
      continue;
    }
    if (!in_wikilink && value[i] === "#") return i;
  }
  return -1;
}
function split_source_path_segments(source_path = "") {
  const segments = [];
  let segment = "";
  let in_wikilink = false;
  for (let i = 0; i < source_path.length; i++) {
    if (!in_wikilink && source_path.slice(i, i + 2) === "[[") {
      in_wikilink = true;
      segment += "[[";
      i++;
      continue;
    }
    if (in_wikilink && source_path.slice(i, i + 2) === "]]") {
      in_wikilink = false;
      segment += "]]";
      i++;
      continue;
    }
    if (!in_wikilink && source_path[i] === "/") {
      if (segment) segments.push(segment);
      segment = "";
      continue;
    }
    segment += source_path[i];
  }
  if (segment) segments.push(segment);
  return segments;
}
function split_block_path_segments(block_path = "") {
  const segments = [];
  let segment = "";
  let in_wikilink = false;
  for (let i = 0; i < block_path.length; i++) {
    if (!in_wikilink && block_path.slice(i, i + 2) === "[[") {
      in_wikilink = true;
      segment += "[[";
      i++;
      continue;
    }
    if (in_wikilink && block_path.slice(i, i + 2) === "]]") {
      in_wikilink = false;
      segment += "]]";
      i++;
      continue;
    }
    if (!in_wikilink && block_path[i] === "#") {
      if (segment) {
        segments.push(segment);
        segment = "";
      }
      if (block_path[i + 1] === "#") {
        segment = "#";
        while (block_path[i + 1] === "#") {
          i++;
          segment += "#";
        }
      } else if (block_path[i + 1] === "{") {
        segment = "#";
      }
      continue;
    }
    segment += block_path[i];
  }
  if (segment) segments.push(segment);
  return segments;
}
function split_path_segments2(item_path) {
  const block_idx = find_first_block_separator(item_path);
  const has_block = block_idx !== -1;
  const source_path = has_block ? item_path.slice(0, block_idx) : item_path;
  const block_path = has_block ? item_path.slice(block_idx) : "";
  const source_segments = split_source_path_segments(source_path);
  const segments = [...source_segments];
  if (block_path) {
    segments.push(...split_block_path_segments(block_path));
  }
  return {
    segments,
    has_block,
    source_segments_count: source_segments.length
  };
}
function insert_item_path(root, params = {}) {
  const { item_key, exists } = params;
  const { segments, has_block, source_segments_count } = split_path_segments2(item_key);
  let node = root;
  let running = "";
  segments.forEach((segment, index) => {
    running = get_running_path(running, segment, {
      has_block,
      index,
      source_segments_count
    });
    if (segment.startsWith("external:..")) return;
    const is_last = index === segments.length - 1;
    const is_block_leaf = is_last && has_block;
    const is_source_file = has_block && index === source_segments_count - 1;
    if (!node.children[segment]) {
      node.children[segment] = {
        name: segment,
        path: is_block_leaf ? item_key : running,
        // For blocks we store an empty *array* so AVA can assert `children.length === 0`.
        children: is_block_leaf ? [] : {},
        selected: false,
        is_file: is_block_leaf || is_source_file || is_last && segment.includes(".")
      };
    }
    node = node.children[segment];
    if (is_last) {
      node.selected = true;
      node.exists = exists;
    }
  });
}
function get_running_path(running, segment, params = {}) {
  if (!running) return segment;
  if (!params.has_block || params.index < params.source_segments_count) {
    return `${running}/${segment}`;
  }
  return segment.startsWith("#") ? `${running}${segment}` : `${running}#${segment}`;
}

// node_modules/obsidian-smart-env/src/components/smart-context/tree.css
var tree_default = ".sc-context-tree {\n  ul {\n    padding-inline-start: 1.7rem;\n  }\n  li:has(> .sc-context-item-remove),\n  li:has(> .sc-context-item-leaf > .sc-context-item-remove) {\n    list-style-type: none;\n  }\n  .sc-context-item-remove:hover {\n    font-weight: bold;\n    filter: brightness(1.8);\n  }\n  .sc-context-item-remove {\n    padding: 0 0.2rem;\n    margin-left: -1.4rem;\n  }\n  .sc-context-item-remove.is-disabled {\n    color: var(--text-faint, var(--text-muted));\n    cursor: not-allowed;\n    filter: none;\n    opacity: 0.65;\n  }\n  .sc-context-item-remove.is-disabled:hover {\n    font-weight: normal;\n    filter: none;\n  }\n  .sc-context-item-remove.is-removing {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    width: 1.2em;\n    height: 1.2em;\n    padding: 0;\n    color: var(--text-muted);\n    cursor: default;\n    pointer-events: none;\n    filter: none;\n  }\n  .sc-context-item-remove.is-removing:hover {\n    font-weight: normal;\n    filter: none;\n  }\n  .sc-context-item-remove.is-removing::after {\n    content: '';\n    width: 0.75em;\n    height: 0.75em;\n    border: 2px solid currentColor;\n    border-top-color: transparent;\n    border-radius: 999px;\n    animation: sc-context-remove-spin 0.8s linear infinite;\n  }\n}\n.sc-context-item-leaf, .sc-context-item-remove {\n  cursor: pointer;\n}\n.sc-context-item-score,\n.sc-context-item-size,\n.sc-context-item-origin-badge {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  min-width: 4.5ch;\n  height: 1.7em;\n  line-height: 1.7em;\n  text-align: center;\n  font-weight: 600 !important;\n  font-size: 0.8em !important;\n  color: var(--nav-item-color) !important;\n  background: var(--background-modifier-hover);\n  border-radius: 6px;\n  padding: 0 0.4em;\n  margin-right: 0.35em;\n  vertical-align: middle;\n}\n.sc-context-item-size,\n.sc-context-item-origin-badge {\n  min-width: 0;\n}\n.sc-context-item-type-icon {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  width: 1em;\n  height: 1em;\n  margin-right: 0.25em;\n  color: var(--text-muted);\n  vertical-align: middle;\n}\n.sc-context-item-origin-badges {\n  display: inline-flex;\n  align-items: center;\n  flex-wrap: wrap;\n  gap: 0.25em;\n  margin-left: 0.35em;\n  vertical-align: middle;\n}\n.sc-context-item-origin-badge {\n  gap: 0.25em;\n  color: var(--text-muted) !important;\n}\n.sc-context-item-badge-icon svg,\n.sc-context-item-type-icon svg {\n  width: 0.9em;\n  height: 0.9em;\n}\n.sc-context-item-badge-label {\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n  max-width: 18ch;\n}\n@keyframes sc-context-remove-spin {\n  to {\n    transform: rotate(360deg);\n  }\n}\n\n";

// node_modules/obsidian-smart-env/src/components/smart-context/tree.js
var remove_debounce_ms = 1500;
function build_html17(ctx, params = {}) {
  return `
    <div class="sc-context-tree" data-context-key="${ctx.data.key}"></div>
  `;
}
async function render19(ctx, params = {}) {
  this.apply_style_sheet(tree_default);
  const html = build_html17(ctx, params);
  const frag = this.create_doc_fragment(html);
  const container = frag.firstElementChild;
  post_process16.call(this, ctx, container, params);
  return container;
}
async function post_process16(ctx, container, params = {}) {
  let render_token = 0;
  const pending_removals = get_pending_removals(ctx);
  const is_pending_removal = (item_key) => {
    for (const target_path of pending_removals.keys()) {
      if (item_matches_remove_path2(item_key, target_path)) return true;
    }
    return false;
  };
  const render_tree_leaves = () => {
    render_token += 1;
    const token = render_token;
    const env = ctx.env;
    const items = ctx.context_items.filter(params.filter);
    const included_items = items.filter((item) => !item?.data?.exclude).filter((item) => !is_pending_removal(get_item_key2(item)));
    const context_size = get_total_context_size(included_items);
    const tree_root = build_path_tree(included_items);
    const context_item_by_key = included_items.reduce((acc, item) => {
      const item_key = get_item_key2(item);
      if (item_key) acc.set(item_key, item);
      return acc;
    }, /* @__PURE__ */ new Map());
    const list_el = render_tree_list.call(this, tree_root, {
      ctx,
      env,
      context_item_by_key,
      context_size,
      render_token: token,
      get_render_token: () => render_token,
      on_remove_path: (target_path, remove_params, tree_item_el) => {
        remove_tree_item_from_ui(tree_item_el);
        queue_remove_by_path(target_path, remove_params);
      }
    });
    this.empty(container);
    if (list_el) container.appendChild(list_el);
  };
  const schedule_render_tree_leaves = create_render_scheduler(render_tree_leaves);
  const flush_pending_removals = () => {
    if (ctx._remove_by_path_timer) {
      clearTimeout(ctx._remove_by_path_timer);
      ctx._remove_by_path_timer = null;
    }
    const removals = Array.from(pending_removals.entries()).map(([target_path, remove_params]) => ({
      path: target_path,
      ...remove_params?.folder ? { folder: true } : {}
    }));
    pending_removals.clear();
    if (!removals.length) return;
    try {
      ctx.remove_by_paths(removals);
    } catch (error) {
      console.warn("Smart Context: Failed to remove queued paths", error);
      schedule_render_tree_leaves();
    }
  };
  const queue_remove_by_path = (target_path, remove_params = {}) => {
    if (!target_path) return;
    for (const pending_path of Array.from(pending_removals.keys())) {
      if (item_matches_remove_path2(target_path, pending_path)) return;
      if (item_matches_remove_path2(pending_path, target_path)) {
        pending_removals.delete(pending_path);
      }
    }
    pending_removals.set(target_path, remove_params);
    if (ctx._remove_by_path_timer) clearTimeout(ctx._remove_by_path_timer);
    ctx._remove_by_path_timer = setTimeout(flush_pending_removals, remove_debounce_ms);
  };
  render_tree_leaves();
  const on_tree_remove_click = (event) => {
    const target = event.target?.closest?.(".sc-context-item-remove");
    if (!target) return;
    if (target.closest(".sc-context-item-leaf")) return;
    event.preventDefault();
    event.stopPropagation();
    const target_path = target.getAttribute("data-path");
    if (!target_path) return;
    if (target.classList.contains("is-disabled")) {
      emit_named_context_remove_blocked_notice(ctx, target.getAttribute("data-named-context") || "");
      return;
    }
    const tree_item_el = target.closest(".sc-tree-item");
    const is_folder = tree_item_el?.classList.contains("dir");
    remove_tree_item_from_ui(tree_item_el);
    queue_remove_by_path(target_path, {
      ...is_folder ? { folder: true } : {}
    });
  };
  const on_named_context_badge_click = (event) => {
    const badge = event.target?.closest?.(".sc-context-item-origin-named-context");
    if (!badge) return;
    const ctx_name = badge.getAttribute("data-named-context");
    if (!ctx_name) return;
    event.preventDefault();
    event.stopPropagation();
    const named_ctx = ctx.env.smart_contexts.get_named_context(ctx_name);
    if (!named_ctx) return console.warn(`Smart Context: Failed to find named context with name: ${ctx_name}`);
    named_ctx.emit_event("context_selector:open");
  };
  const disposers = [];
  container.addEventListener("click", on_tree_remove_click);
  container.addEventListener("click", on_named_context_badge_click);
  disposers.push(() => container.removeEventListener("click", on_tree_remove_click));
  disposers.push(() => container.removeEventListener("click", on_named_context_badge_click));
  disposers.push(ctx.on_event("context:updated", schedule_render_tree_leaves));
  ctx.named_contexts.forEach((named_ctx) => {
    disposers.push(named_ctx.on_event("context:updated", schedule_render_tree_leaves));
  });
  this.attach_disposer(container, disposers);
  return container;
}
function get_item_key2(item) {
  return item?.key || item?.path || "";
}
function get_context_item_named_context(item) {
  const named_context = item?.data?.from_named_context;
  return typeof named_context === "string" ? named_context.trim() : "";
}
function get_pending_removals(ctx) {
  if (!(ctx._pending_remove_by_path instanceof Map)) {
    ctx._pending_remove_by_path = /* @__PURE__ */ new Map();
  }
  return ctx._pending_remove_by_path;
}
function normalize_remove_path2(path = "") {
  return String(path || "").replace(/\/+$/g, "");
}
function item_matches_remove_path2(item_key = "", target_path = "") {
  const normalized_item_key = normalize_remove_path2(item_key);
  const normalized_target_path = normalize_remove_path2(target_path);
  if (!normalized_item_key || !normalized_target_path) return false;
  return normalized_item_key === normalized_target_path || normalized_item_key.startsWith(normalized_target_path + "/") || normalized_item_key.startsWith(normalized_target_path + "#") || normalized_item_key.startsWith(normalized_target_path + "{");
}
function is_core_context(ctx) {
  return ctx?.env?.is_pro !== true;
}
function get_tree_item_named_contexts(tree_item, context_item_by_key, named_contexts = /* @__PURE__ */ new Set()) {
  const context_item = context_item_by_key.get(get_item_key2(tree_item));
  const named_context = get_context_item_named_context(context_item);
  if (named_context) named_contexts.add(named_context);
  get_child_nodes(tree_item).forEach((child) => {
    get_tree_item_named_contexts(child, context_item_by_key, named_contexts);
  });
  return named_contexts;
}
function emit_named_context_remove_blocked_notice(ctx, named_context_name = "") {
  const context_name = String(named_context_name || "").trim();
  const named_ctx = context_name ? ctx?.env?.smart_contexts?.get_named_context?.(context_name) : null;
  const message = context_name ? `This item is included from named context "${context_name}". Open that named context to remove it there.` : "This item is included from a named context. Open the named context to remove it there.";
  ctx?.emit_event?.("context:named_context_remove_blocked", {
    level: "warning",
    message,
    event_source: "smart_context_tree.remove_named_context_child",
    named_context_name: context_name,
    ...named_ctx ? {
      btn_text: "Open named context",
      btn_callback: "context_selector:open",
      btn_event_key: "context_selector:open",
      btn_event_payload: {
        collection_key: "smart_contexts",
        item_key: named_ctx.key
      }
    } : {}
  });
}
function get_item_size(item) {
  const size = Number(item?.size ?? item?.data?.size);
  if (!Number.isFinite(size) || size < 0) return 0;
  return size;
}
function get_total_context_size(items = []) {
  return items.reduce((sum, item) => sum + get_item_size(item), 0);
}
function get_child_nodes(node) {
  if (!node?.children || Array.isArray(node.children)) return [];
  return Object.values(node.children);
}
function sort_tree_items(left, right) {
  if (left.is_file !== right.is_file) return left.is_file ? 1 : -1;
  return left.name.localeCompare(right.name);
}
function render_tree_list(node, params = {}) {
  const child_nodes = get_child_nodes(node);
  if (!child_nodes.length) return null;
  const list_el = document.createElement("ul");
  child_nodes.sort(sort_tree_items).forEach((child) => {
    list_el.appendChild(render_tree_item.call(this, child, params));
  });
  return list_el;
}
function render_tree_item(tree_item, params = {}) {
  const key = get_item_key2(tree_item);
  const li = document.createElement("li");
  li.dataset.path = key;
  li.classList.add("sc-tree-item", tree_item.is_file ? "file" : "dir");
  if (key.startsWith("external:")) li.classList.add("sc-external");
  const child_list = render_tree_list.call(this, tree_item, params);
  const context_item = params.context_item_by_key.get(key);
  render_tree_item_shell(tree_item, li, {
    ...params,
    child_list,
    is_context_item: Boolean(context_item)
  });
  if (context_item) {
    const named_context = get_context_item_named_context(context_item);
    const remove_disabled = is_core_context(params.ctx) && Boolean(named_context);
    params.env.smart_components.render_component("context_item_leaf", context_item, {
      context_size: params.context_size,
      remove_disabled,
      on_remove_disabled: () => {
        emit_named_context_remove_blocked_notice(params.ctx, named_context);
      },
      on_remove: (_event, item = context_item) => {
        const item_key = get_item_key2(item);
        if (!item_key) return;
        params.on_remove_path?.(
          item_key,
          {
            ...tree_item.is_file ? {} : { folder: true }
          },
          li
        );
      }
    }).then((leaf) => {
      if (params.get_render_token() !== params.render_token) return;
      if (!leaf) return;
      this.empty(li);
      li.appendChild(leaf);
      if (child_list) li.appendChild(child_list);
    }).catch((error) => {
      console.warn(`Smart Context: Failed to render tree leaf for path: ${key}`, error);
    });
  }
  return li;
}
function render_tree_item_shell(tree_item, li, params = {}) {
  const key = get_item_key2(tree_item);
  const has_children = Boolean(params.child_list);
  if (!params.is_context_item && has_children) {
    let named_context = "";
    let remove_disabled = false;
    if (is_core_context(params.ctx)) {
      const named_contexts = get_tree_item_named_contexts(tree_item, params.context_item_by_key);
      remove_disabled = named_contexts.size > 0;
      if (named_contexts.size === 1) named_context = Array.from(named_contexts)[0];
    }
    const remove_btn = document.createElement("span");
    remove_btn.classList.add("sc-context-item-remove");
    if (remove_disabled) remove_btn.classList.add("is-disabled");
    remove_btn.dataset.path = key;
    if (named_context) remove_btn.dataset.namedContext = named_context;
    remove_btn.textContent = "\xD7";
    remove_btn.setAttribute(
      "aria-label",
      remove_disabled ? "Open named context to edit included items" : "Remove folder from context"
    );
    if (remove_disabled) {
      remove_btn.setAttribute("aria-disabled", "true");
      remove_btn.setAttribute("title", "Open the named context to remove included items.");
    }
    li.appendChild(remove_btn);
  }
  const label = document.createElement("span");
  label.classList.add("sc-tree-label", "sc-context-item-name");
  if (params.is_context_item) label.classList.add("sc-context-item-placeholder");
  if (tree_item.exists === false) label.classList.add("missing");
  label.textContent = tree_item.name || key;
  li.appendChild(label);
  if (params.child_list) li.appendChild(params.child_list);
}
function remove_tree_item_from_ui(tree_item_el) {
  let current = tree_item_el;
  while (current) {
    const parent_list = current.parentElement;
    const parent_item = parent_list?.closest?.(".sc-tree-item");
    current.remove();
    if (!parent_list) return;
    if (parent_list.children.length === 0) parent_list.remove();
    if (!parent_item) return;
    if (parent_item.querySelector(":scope > .sc-context-item-leaf")) return;
    const child_list = parent_item.querySelector(":scope > ul");
    if (child_list && child_list.children.length > 0) return;
    current = parent_item;
  }
}

// node_modules/obsidian-smart-env/src/components/smart-plugins/list.js
var import_obsidian26 = require("obsidian");

// node_modules/obsidian-smart-env/src/utils/smart_plugins.js
var import_obsidian25 = require("obsidian");
function get_smart_server_url() {
  if (typeof window !== "undefined" && window.SMART_SERVER_URL_OVERRIDE) {
    return window.SMART_SERVER_URL_OVERRIDE;
  }
  return "https://connect.smartconnections.app";
}
var install_file_names = ["manifest.json", "main.js", "styles.css"];
async function write_files_with_adapter(adapter, baseFolder, files) {
  const hasWriteBinary = typeof adapter.writeBinary === "function";
  if (!await adapter.exists(baseFolder)) {
    await adapter.mkdir(baseFolder);
  }
  for (const { fileName, data, accessed_at } of files) {
    const fullPath = baseFolder + "/" + fileName;
    if (hasWriteBinary) {
      await adapter.writeBinary(fullPath, data, { ctime: accessed_at, mtime: accessed_at });
    } else {
      const text = new TextDecoder("utf-8").decode(data);
      await adapter.write(fullPath, text, { ctime: accessed_at, mtime: accessed_at });
    }
  }
}
async function authenticated_smart_plugins_request(params = {}) {
  const request_path = String(params.path || "").trim();
  if (!request_path) {
    throw new Error("path required");
  }
  const token = String(params.token || "").trim();
  const headers = {
    ...params.headers || {}
  };
  if (token) {
    headers.Authorization = `Bearer ${token}`;
  }
  const url = request_path.startsWith("http") ? request_path : `${get_smart_server_url()}${request_path}`;
  return await (0, import_obsidian25.requestUrl)({
    url,
    method: params.method || "POST",
    headers,
    body: params.body,
    contentType: params.contentType,
    throw: false
  });
}
async function fetch_plugin_file(repo_name, token, params = {}) {
  const file = String(params.file || "").trim();
  if (!file) {
    throw new Error("file required");
  }
  const body = {
    repo: repo_name,
    file
  };
  const version4 = String(params.version || "").trim();
  if (version4) {
    body.version = version4;
  }
  const resp = await authenticated_smart_plugins_request({
    token,
    path: "/plugin_download",
    method: "POST",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify(body)
  });
  if (resp.status !== 200) {
    throw new Error(
      get_response_message(resp) || `plugin_download error ${resp.status}`
    );
  }
  return resp;
}
function get_response_header_value(response, header_name) {
  const normalized_header_name = String(header_name || "").trim().toLowerCase();
  if (!normalized_header_name) return "";
  const headers = response?.headers;
  if (!headers) return "";
  if (typeof headers.get === "function") {
    return String(
      headers.get(header_name) || headers.get(normalized_header_name) || ""
    ).trim();
  }
  if (Array.isArray(headers)) {
    const match = headers.find(([key]) => {
      return String(key || "").trim().toLowerCase() === normalized_header_name;
    });
    return String(match?.[1] || "").trim();
  }
  if (typeof headers === "object") {
    const key = Object.keys(headers).find((candidate_key) => {
      return String(candidate_key || "").trim().toLowerCase() === normalized_header_name;
    });
    return String(key && headers[key] || "").trim();
  }
  return "";
}
function normalize_positive_epoch_ms(value) {
  const numeric_value = Number(value);
  if (!Number.isFinite(numeric_value) || numeric_value <= 0) {
    return null;
  }
  return Math.round(numeric_value);
}
function get_plugin_file_text(response, file_name) {
  if (typeof response?.text === "string" && response.text.length) {
    return response.text;
  }
  if (response?.arrayBuffer instanceof ArrayBuffer) {
    return new TextDecoder("utf-8").decode(response.arrayBuffer);
  }
  if (file_name === "manifest.json" && response?.json) {
    return JSON.stringify(response.json, null, 2);
  }
  return "";
}
function build_plugin_file_record(file_name, response) {
  return {
    fileName: file_name,
    data: new TextEncoder().encode(get_plugin_file_text(response, file_name)),
    accessed_at: normalize_positive_epoch_ms(
      get_response_header_value(response, "accessed_at")
    ) || Date.now()
  };
}
async function enable_plugin(app2, plugin_id) {
  await app2.plugins.enablePlugin(plugin_id);
  app2.plugins.enabledPlugins.add(plugin_id);
  app2.plugins.requestSaveConfig();
  app2.plugins.loadManifests();
}
function get_oauth_storage_prefix(app2) {
  const vault_name = app2?.vault?.getName?.() || "";
  const safe = vault_name.toLowerCase().replace(/[^a-z0-9]/g, "_");
  return `${safe}_smart_plugins_oauth_`;
}
function get_response_json(response) {
  return response?.json && typeof response.json === "object" ? response.json : {};
}
function get_response_message(response) {
  return String(
    get_response_json(response)?.message || get_response_json(response)?.error || response?.text || ""
  ).trim();
}
async function fetch_server_plugin_list(token) {
  const resp = await authenticated_smart_plugins_request({
    token,
    path: "/plugin_list",
    method: "POST",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify({})
  });
  if (resp.status !== 200) {
    return {
      list: [],
      sub_exp: null,
      status: resp.status,
      message: get_response_message(resp)
    };
  }
  const response_json = get_response_json(resp);
  return {
    ...response_json,
    status: resp.status,
    message: String(response_json?.message || "").trim()
  };
}
async function fetch_referral_stats(params = {}) {
  const token = String(params.token || "").trim();
  if (!token) return { ok: false, error: "missing_token" };
  const resp = await authenticated_smart_plugins_request({
    token,
    path: "/api/referrals/stats",
    method: "GET"
  });
  if (resp.status === 401) {
    return {
      ok: false,
      message: get_response_message(resp) || "Invalid or expired token. Please log in again."
    };
  }
  if (resp.status !== 200) {
    throw new Error(
      get_response_message(resp) || `referrals stats error ${resp.status}`
    );
  }
  return get_response_json(resp);
}

// node_modules/obsidian-smart-env/src/utils/smart_plugins_state.js
var pro_plugin_ids_without_pro_in_name = /* @__PURE__ */ new Set(["smart-file-nav"]);
function infer_installed_plugin_type(params = {}) {
  const plugin_id = String(params.plugin_id || "").trim();
  if (pro_plugin_ids_without_pro_in_name.has(plugin_id)) {
    return "pro";
  }
  const manifest_name = String(params.manifest_name || "");
  return manifest_name.includes("Pro") ? "pro" : "core";
}
function should_block_pro_install(params = {}) {
  return String(params.item_type || "").trim() === "pro" && params.is_entitled !== true;
}
function should_offer_plugin_update(params = {}) {
  const item_type = String(params.item_type || "").trim();
  const installed_type = String(params.installed_type || "").trim();
  if (!item_type || item_type !== installed_type) {
    return false;
  }
  if (should_block_pro_install({
    item_type,
    is_entitled: params.is_entitled
  })) {
    return false;
  }
  const server_version = String(params.server_version || "").trim();
  const installed_version = String(params.installed_version || "").trim();
  if (!server_version || !installed_version) {
    return false;
  }
  if (typeof params.compare_versions !== "function") {
    return false;
  }
  return params.compare_versions(server_version, installed_version) > 0;
}
function should_signal_outdated_env_compatibility(params = {}) {
  const item_type = String(params.item_type || "").trim();
  const installed_type = String(params.installed_type || "").trim();
  if (!item_type || item_type !== installed_type) {
    return false;
  }
  if (should_block_pro_install({
    item_type,
    is_entitled: params.is_entitled
  })) {
    return false;
  }
  if (params.is_enabled !== true) {
    return false;
  }
  if (params.is_loaded === true || params.is_deferred === true) {
    return false;
  }
  return has_outdated_smart_env_version(params.loaded_env_version);
}
function compute_plugin_track_state(params = {}) {
  const item_type = String(params.item_type || "").trim();
  const has_core_plugin = params.has_core_plugin === true;
  const has_pro_plugin = params.has_pro_plugin === true;
  const has_group_ui = has_core_plugin && has_pro_plugin;
  const installed_type = String(params.installed_type || "").trim();
  if (item_type !== "core" && item_type !== "pro") {
    return null;
  }
  if (item_type === "core" && !has_core_plugin || item_type === "pro" && !has_pro_plugin) {
    return null;
  }
  let control_state = "cant_install";
  if (installed_type === item_type) {
    if (params.should_update === true) {
      control_state = "update_available";
    } else if (params.has_outdated_env_compatibility === true) {
      control_state = "outdated_env";
    } else if (params.is_deferred === true) {
      control_state = "deferred";
    } else if (params.is_loaded === true) {
      control_state = "loaded";
    } else if (params.is_enabled !== true) {
      control_state = "can_enable";
    } else {
      control_state = "installed";
    }
  } else if (item_type === "core") {
    if (has_group_ui && (installed_type === "pro" || params.is_entitled === true)) {
      control_state = "included_in_pro";
    } else if (has_group_ui) {
      control_state = "can_install_core_only";
    } else {
      control_state = "can_install";
    }
  } else if (item_type === "pro") {
    if (installed_type === "core") {
      control_state = "core_installed";
    } else if (params.is_entitled === true) {
      control_state = "can_install_pro";
    } else {
      control_state = "cant_install";
    }
  }
  return {
    item_type,
    is_installed_here: installed_type === item_type,
    control_state
  };
}
function compute_plugin_list_item_state(params = {}) {
  const has_core_plugin = params.has_core_plugin === true;
  const has_pro_plugin = params.has_pro_plugin === true;
  const has_group_ui = has_core_plugin && has_pro_plugin;
  const installed_type = String(params.installed_type || "").trim();
  const display_item_type = String(params.display_item_type || "").trim() || (has_pro_plugin ? "pro" : "core");
  const track_states = {
    core: compute_plugin_track_state({
      ...params,
      has_core_plugin,
      has_pro_plugin,
      installed_type,
      item_type: "core"
    }),
    pro: compute_plugin_track_state({
      ...params,
      has_core_plugin,
      has_pro_plugin,
      installed_type,
      item_type: "pro"
    })
  };
  let row = null;
  if (has_group_ui) {
    row = ["core", "pro"].includes(installed_type) ? track_states[installed_type] : params.is_entitled === true ? track_states.pro : track_states.core;
  } else {
    row = track_states[display_item_type] || null;
  }
  return {
    row,
    track_states
  };
}
function get_install_enable_behavior(params = {}) {
  const was_installed = params.was_installed === true;
  const was_enabled = params.was_enabled === true;
  return {
    should_disable_before_install: was_installed && was_enabled,
    should_enable_after_install: !was_installed || was_enabled
  };
}
function has_outdated_smart_env_version(version4 = "") {
  if (version4 === "") return false;
  const version_pcs = String(version4 || "").trim().split(".");
  const version_minor = Number.parseInt(version_pcs[1] || "0", 10);
  if (!Number.isFinite(version_minor)) {
    return false;
  }
  return version_minor < 4;
}

// node_modules/obsidian-smart-env/src/components/smart-plugins/style.css
var style_default2 = ".get-core-link {\n  text-wrap: nowrap;\n  font-size: var(--font-ui-small);\n}\n.core-installed-text {\n  text-wrap: nowrap;\n  font-size: var(--font-ui-small);\n  color: var(--text-muted);\n}\n\n.smart-plugins-core-list,\n.pro-plugins-list {\n  display: flex;\n  flex-direction: column;\n  row-gap: var(--size-4-3);\n  margin-top: var(--size-4-2);\n}\n\n.smart-plugins-section + .smart-plugins-section {\n  margin-top: var(--size-4-4);\n}\n\n.smart-plugins-section-title {\n  padding: 0 var(--size-4-4);\n  font-size: var(--font-ui-medium);\n  font-weight: var(--font-semibold);\n}\n\n.smart-plugins-section-description {\n  padding: 0 var(--size-4-4);\n  margin-top: var(--size-2-2);\n  font-size: var(--font-ui-small);\n  color: var(--text-muted);\n}\n\n.smart-plugins-server-message {\n  margin: var(--size-4-3) 0;\n\n  .callout-content,\n  .callout-content > :first-child {\n    margin-block-start: 0;\n  }\n\n  .callout-content > :last-child {\n    margin-block-end: 0;\n  }\n\n  .callout-icon svg {\n    width: 16px;\n    height: 16px;\n  }\n}\n\n.pro-plugins-list-item {\n  display: flex;\n  align-items: center;\n  padding: 0.75em 0;\n  border-top: 1px solid var(--background-modifier-border);\n  row-gap: var(--size-4-3);\n\n  .setting-item {\n    width: 100%;\n  }\n\n  .setting-item-name {\n    position: relative;\n  }\n}\n\n.smart-plugins-item-description,\n.smart-plugins-item-description > :first-child {\n  margin-block-start: 0;\n}\n\n.smart-plugins-item-description > :last-child {\n  margin-block-end: 0;\n}\n\n.smart-plugins-item-subscription-state {\n  margin-top: var(--size-2-2);\n  font-size: var(--font-ui-small);\n  color: var(--text-muted);\n}\n\n.smart-plugins-item-group-combined .setting-items > .pro-plugins-list-item:first-child {\n  border-top: none;\n}\n\n\n.smart-plugins-list-item-controls {\n  display: flex;\n  align-items: center;\n  justify-content: flex-end;\n  gap: var(--size-2-2);\n\n  > button,\n  > span {\n    white-space: nowrap;\n  }\n}\n\n.smart-plugins-details-button {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  min-width: 32px;\n  width: 32px;\n  padding: 0;\n}\n\n.smart-plugins-details-button svg {\n  width: 16px;\n  height: 16px;\n}\n\n.smart-badge.pro-badge::after,\n.smart-badge.core-badge::after {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  margin-left: 0.4em;\n  padding: 0.08em 0.55em;\n  border-radius: 999px;\n  white-space: nowrap;\n  vertical-align: middle;\n  font-size: 0.7em;\n  font-weight: 600;\n  letter-spacing: 0.14em;\n  text-transform: uppercase;\n  line-height: 1;\n  border: 1px solid var(--background-modifier-border);\n  box-shadow:\n    0 0 0 1px var(--background-primary),\n    0 1px 3px rgba(0, 0, 0, 0.35);\n  transform: translateY(-0.03em);\n}\n\n.smart-badge.pro-badge::after {\n  content: 'PRO';\n  background-color: var(--color-accent);\n  background-image: linear-gradient(\n    135deg,\n    var(--color-accent),\n    var(--interactive-accent-hover)\n  );\n  color: var(--text-on-accent, var(--background-primary));\n}\n\n.smart-badge.core-badge::after {\n  content: 'CORE';\n  background-color: var(--background-secondary);\n  color: var(--color-normal, var(--text-normal));\n}\n\n.smart-badge.pro-badge:hover::after {\n  background-color: var(--interactive-accent-hover);\n  filter: brightness(1.05);\n}\n\n.smart-badge.core-badge:hover::after {\n  background-color: var(--background-modifier-hover);\n}\n\n.smart-plugins-login .setting-item {\n  gap: var(--size-4-3);\n}\n\n.smart-plugins-login-manual {\n  margin-top: var(--size-4-3);\n}\n\n.smart-plugins-login-manual-instructions {\n  font-size: var(--font-ui-small);\n  color: var(--text-muted);\n  margin-bottom: var(--size-2-2);\n}\n\n.smart-plugins-login-manual-controls {\n  display: flex;\n  gap: var(--size-2-2);\n  align-items: center;\n}\n\n.smart-plugins-login-manual-input {\n  flex: 1;\n  min-width: 240px;\n  font-size: var(--font-ui-small);\n}\n";

// node_modules/obsidian-smart-env/node_modules/smart-utils/convert_to_time_until.js
function convert_to_time_until(timestamp) {
  const now = Date.now();
  const ms = timestamp < 1e12 ? timestamp * 1e3 : timestamp;
  const diff_ms = ms - now;
  if (diff_ms < 0) return "expired";
  const seconds = Math.floor(diff_ms / 1e3);
  const intervals = [
    { label: "decade", seconds: 31536e4 },
    { label: "year", seconds: 31536e3 },
    { label: "month", seconds: 2592e3 },
    { label: "day", seconds: 86400 },
    { label: "hour", seconds: 3600 },
    { label: "minute", seconds: 60 },
    { label: "second", seconds: 1 }
  ];
  for (const interval of intervals) {
    const count = Math.floor(seconds / interval.seconds);
    if (count >= 1) {
      if (interval.label === "decade") {
        return "never (lifetime access)";
      }
      return `in ${count} ${interval.label}${count > 1 ? "s" : ""}`;
    }
  }
  return "in a few seconds";
}

// node_modules/obsidian-smart-env/src/components/smart-plugins/list.js
var SMART_PLUGINS_DESC = `<a href="https://smartconnections.app/core-plugins/?utm_source=plugin-store" target="_external">Core plugins</a> provide essential functionality and a "just works" experience. Pro plugins enable advanced configuration and features for Obsidian AI experts. <a href="https://smartconnections.app/smart-plugins/?utm_source=plugin-store" target="_external">Learn more</a> about Smart Plugins.`;
var PRO_PLUGINS_FOOTER = `All Pro plugins include advanced configurations and additional model providers. Pro users get priority support via email. <a href="https://smartconnections.app/pro-plugins/?utm_source=plugin-store" target="_external">Learn more</a> about Pro Plugins.`;
var PRO_PLUGINS_URL = "https://smartconnections.app/pro-plugins/";
function default_smart_plugins_list() {
  return [
    {
      item_type: "core",
      install_method: "obsidian",
      item_name: "Smart Connections",
      item_desc: "See notes related to what you are working on right now.",
      item_repo: "brianpetro/obsidian-smart-connections",
      plugin_id: "smart-connections",
      url: "https://smartconnections.app/smart-connections/"
    },
    {
      item_type: "pro",
      item_name: "Connections Pro",
      plugin_id: "smart-connections",
      item_desc: "More opportunities for connections. Graph view for visualizing. Inline and footer views (great for mobile!). Configurable algorithms and additional embedding model providers.",
      url: "https://smartconnections.app/smart-connections/"
    },
    {
      item_type: "core",
      install_method: "obsidian",
      item_name: "Smart Context",
      item_desc: "Assemble notes into AI-ready context with selectors, links, and templates.",
      item_repo: "brianpetro/smart-context-obsidian",
      plugin_id: "smart-context",
      url: "https://smartconnections.app/smart-context/"
    },
    {
      item_type: "pro",
      item_name: "Context Pro",
      plugin_id: "smart-context",
      item_desc: "Advanced tools for context engineering. Utilize Bases, images, and external sources (great for coders!) in your contexts.",
      url: "https://smartconnections.app/smart-context/"
    },
    {
      item_type: "core",
      install_method: "obsidian",
      item_name: "Smart Chat",
      item_desc: "Run chat workflows in Obsidian with Smart Environment context.",
      plugin_id: "smart-chatgpt",
      item_repo: "brianpetro/smart-chatgpt-obsidian",
      url: "https://smartconnections.app/smart-chat/"
    },
    {
      item_type: "pro",
      item_name: "Chat Pro (API)",
      plugin_id: "smart-chat",
      item_desc: "Configure chat to use Local and Cloud API providers (Ollama, LM Studio, OpenAI, Gemini, Anthropic, Open Router, and more).",
      url: "https://smartconnections.app/smart-chat/"
    },
    {
      item_type: "core",
      item_name: "Smart Lookup",
      item_desc: "Run semantic search as a dedicated Smart Plugin.",
      item_repo: "brianpetro/smart-lookup-obsidian",
      plugin_id: "smart-lookup",
      url: "https://smartconnections.app/smart-lookup/",
      install_method: "github"
    },
    {
      item_type: "core",
      install_method: "obsidian",
      item_name: "Smart Templates",
      item_repo: "brianpetro/obsidian-smart-templates",
      plugin_id: "smart-templates",
      item_desc: "Create structured templates designed for Smart Plugins workflows.",
      url: "https://smartconnections.app/smart-templates/"
    },
    {
      item_type: "pro",
      item_name: "Connect Pro",
      plugin_id: "smart-connect-pro",
      item_desc: "Integrate with ChatGPT. Use a GPT that has access to Obsidian CLI.",
      url: "https://smartconnections.app/connect-pro/"
    }
  ];
}
var SMART_PLUGINS_LIST = default_smart_plugins_list();
function build_html18(env, params = {}) {
  return `
    <div class="pro-plugins-container setting-item-heading">
      <div class="setting-group">
        <div class="setting-item setting-item-heading">
          <div class="setting-item-control">
            <div class="smart-plugins-login"></div>
          </div>
        </div>
        <p>${SMART_PLUGINS_DESC}</p>
        <div class="smart-plugins-server-message callout" data-callout="note" style="display:none;">
          <div class="callout-title">
            <div class="callout-icon"></div>
            <div class="callout-title-inner">Store message</div>
          </div>
          <div class="callout-content markdown-rendered"></div>
        </div>
        <div class="smart-plugins-section">
          <div class="smart-plugins-official-list">Loading...</div>
        </div>
        <div class="smart-plugins-section smart-plugins-experimental-section" style="display:none;">
          <div class="smart-plugins-section-title">Experimental</div>
          <div class="smart-plugins-section-description">
            Available to Pro subscribers. These plugins may change quickly.
          </div>
          <div class="smart-plugins-experimental-list"></div>
        </div>
        <p>${PRO_PLUGINS_FOOTER}</p>
        <div class="smart-plugins-referral"></div>
      </div>
    </div>
  `;
}
async function render20(env, params = {}) {
  this.apply_style_sheet(style_default2);
  const html = build_html18.call(this, env, params);
  const frag = this.create_doc_fragment(html);
  const container = frag.firstElementChild;
  post_process17.call(this, env, container, params);
  return container;
}
async function post_process17(env, container, params = {}) {
  const plugin = env.plugin || null;
  const app2 = plugin?.app || window.app;
  const oauth_storage_prefix = get_oauth_storage_prefix(app2);
  const login_container = container.querySelector(".smart-plugins-login");
  const referral_container = container.querySelector(".smart-plugins-referral");
  const official_list_el = container.querySelector(".smart-plugins-official-list");
  const experimental_section_el = container.querySelector(".smart-plugins-experimental-section");
  const experimental_list_el = container.querySelector(".smart-plugins-experimental-list");
  const server_message_el = container.querySelector(".smart-plugins-server-message");
  const server_message_icon_el = server_message_el?.querySelector(".callout-icon");
  const server_message_title_el = server_message_el?.querySelector(".callout-title-inner");
  const server_message_content_el = server_message_el?.querySelector(".callout-content");
  const render_component = env.smart_components.render_component.bind(env.smart_components);
  const render_login = async (login_params = {}) => {
    const login_el = await render_component("smart_plugins_login", env, {
      ...params,
      ...login_params
    });
    this.empty(login_container);
    if (login_el) login_container.appendChild(login_el);
  };
  const render_referrals = async (referral_params = {}) => {
    const referral_el = await render_component("smart_plugins_referral", env, {
      ...params,
      ...referral_params
    });
    this.empty(referral_container);
    if (referral_el) referral_container.appendChild(referral_el);
  };
  const render_markdown = async (target_el, markdown = "") => {
    if (!target_el) return;
    this.empty(target_el);
    const safe_markdown = String(markdown || "").trim();
    if (!safe_markdown) return;
    await import_obsidian26.MarkdownRenderer.render(app2, safe_markdown, target_el, "", plugin);
    target_el.querySelectorAll("a").forEach((a) => {
      a.setAttribute("target", "_external");
    });
  };
  const render_server_message = async (message = "", opts = {}) => {
    const safe_message = String(message || "").trim();
    if (!safe_message) {
      if (server_message_el?.style) server_message_el.style.display = "none";
      if (server_message_content_el) this.empty(server_message_content_el);
      return;
    }
    const safe_title = String(opts.title || "Store message").trim() || "Store message";
    const callout_type = String(opts.callout_type || "note").trim() || "note";
    if (server_message_el?.style) server_message_el.style.display = "";
    server_message_el?.setAttribute?.("data-callout", callout_type);
    if (server_message_title_el) server_message_title_el.textContent = safe_title;
    if (server_message_content_el) await render_markdown(server_message_content_el, safe_message);
    if (server_message_icon_el) {
      (0, import_obsidian26.setIcon)(server_message_icon_el, callout_type === "warning" ? "alert-triangle" : "info");
    }
  };
  const render_plugin_items = async (list_container, plugin_items = []) => {
    this.empty(list_container);
    for (const item of plugin_items) {
      const row = await render_component("smart_plugins_list_item", item, {
        ...params,
        app: app2,
        env,
        token: item.auth_token,
        sub_exp: item.root_sub_exp
      });
      if (!row) continue;
      if (item.has_group_ui) {
        list_container.appendChild(row);
        continue;
      }
      const group_frag = this.create_doc_fragment('<div class="setting-group smart-plugins-item-group"><div class="setting-items"></div></div>');
      const group_el = group_frag.firstElementChild;
      const group_items_el = group_el.querySelector(".setting-items");
      group_items_el.appendChild(row);
      list_container.appendChild(group_el);
    }
  };
  let viewed_event_emitted = false;
  let handled_token_rejection = false;
  const render_smart_plugins = async () => {
    const token = localStorage.getItem(oauth_storage_prefix + "token") || "";
    const has_token = Boolean(token);
    if (!viewed_event_emitted) {
      viewed_event_emitted = true;
      emit_store_event(env, "pro_plugins:viewed", params, {
        event_source: "smart_plugins_list",
        recommended_track: "pro"
      });
    }
    await render_login({
      auth_state: has_token ? "checking" : "signed_out",
      sub_exp: null
    });
    this.empty(referral_container);
    await render_server_message();
    experimental_section_el.style.display = "none";
    this.empty(experimental_list_el);
    try {
      await app2.plugins.loadManifests();
      let resp = await fetch_server_plugin_list(token);
      let auth_state = has_token ? "signed_in" : "signed_out";
      let server_message = String(resp?.message || "").trim();
      if (resp?.status === 401 && has_token) {
        auth_state = "invalid";
        if (!handled_token_rejection) {
          handled_token_rejection = true;
          emit_store_event(env, "pro_plugins:oauth_token_rejected", params, {
            level: "warning",
            message: "Session expired. Please log in again.",
            event_source: "smart_plugins_list",
            recommended_track: "pro"
          });
        }
        server_message = build_invalid_credentials_message(server_message);
        const guest_resp = await fetch_server_plugin_list("");
        if (guest_resp?.status === 200) {
          resp = {
            ...guest_resp,
            status: resp.status
          };
        } else {
          resp = {
            list: [],
            sub_exp: null,
            status: resp.status
          };
        }
      } else {
        handled_token_rejection = false;
      }
      if (resp?.status && ![200, 401].includes(resp.status)) {
        throw new Error(`plugin_list error ${resp.status}: ${resp?.message || "Unknown error"}`);
      }
      const sub_exp = resp?.sub_exp ?? null;
      await render_login({
        auth_state,
        sub_exp
      });
      await render_referrals({
        token: auth_state === "signed_in" ? token : "",
        sub_exp,
        auth_state
      });
      await render_server_message(server_message, {
        callout_type: auth_state === "invalid" ? "warning" : "note",
        title: auth_state === "invalid" ? "Account message" : "Store message"
      });
      SMART_PLUGINS_LIST = await hydrate_plugins_list(resp, env, {
        root_sub_exp: sub_exp
      });
      const {
        official_items,
        experimental_items
      } = partition_plugin_items(SMART_PLUGINS_LIST);
      await render_plugin_items(official_list_el, official_items);
      if (experimental_items.length) {
        experimental_section_el.style.display = "";
        await render_plugin_items(experimental_list_el, experimental_items);
      } else {
        experimental_section_el.style.display = "none";
        this.empty(experimental_list_el);
      }
    } catch (err) {
      console.error("[smart-plugins:list] Failed to fetch plugin list:", err);
      this.empty(official_list_el);
      this.empty(experimental_list_el);
      experimental_section_el.style.display = "none";
      await render_server_message();
      official_list_el.appendChild(this.create_doc_fragment('<div class="error"><p>Failed to load plugin information.</p><button class="retry">Retry</button></div>'));
      SMART_PLUGINS_LIST = default_smart_plugins_list();
      const retry_button = official_list_el.querySelector(".retry");
      if (retry_button) {
        retry_button.addEventListener("click", render_smart_plugins);
      }
    }
  };
  const disposers = [];
  disposers.push(env.events.on("smart_plugins_oauth_completed", render_smart_plugins));
  disposers.push(env.events.on("pro_plugins:logged_out", render_smart_plugins));
  disposers.push(env.events.on("pro_plugins:refresh", render_smart_plugins));
  this.attach_disposer?.(container, disposers);
  await render_smart_plugins();
  return container;
}
function normalize_release_version(value) {
  const normalized_value = String(value || "").trim();
  if (!normalized_value) {
    return "";
  }
  return normalized_value.replace(/^v/i, "");
}
function get_source_surface(params = {}) {
  const source_surface = String(
    params.source_surface || params.event_source || "plugin_store"
  ).trim();
  return source_surface || "plugin_store";
}
function build_store_event_payload(params = {}, extra = {}) {
  return {
    plugin_key: null,
    feature_key: null,
    source_surface: get_source_surface(params),
    recommended_track: null,
    ...extra
  };
}
function emit_store_event(env, event_key, params = {}, extra = {}) {
  env?.events?.emit?.(event_key, build_store_event_payload(params, extra));
}
async function hydrate_plugins_list(server_resp, env, params = {}) {
  const smart_plugins_list = default_smart_plugins_list();
  const { list = [] } = server_resp || {};
  for (const server_item of list) {
    const server_item_type = get_plugin_item_type(server_item) || "pro";
    const local_item = smart_plugins_list.find((item) => {
      return item.plugin_id === server_item.plugin_id && get_plugin_item_type(item) === server_item_type;
    });
    if (!local_item) {
      smart_plugins_list.push({
        item_type: server_item_type,
        ...server_item
      });
      continue;
    }
    Object.assign(local_item, server_item, {
      item_type: server_item_type
    });
  }
  await hydrate_core_plugin_versions(smart_plugins_list);
  return build_plugin_list_items(smart_plugins_list, env, params);
}
async function hydrate_core_plugin_versions(smart_plugins_list = []) {
  for (const item of smart_plugins_list) {
    if (get_plugin_item_type(item) !== "core") continue;
    const repo = get_plugin_repo(item);
    if (!repo) continue;
    try {
      const { json: release } = await (0, import_obsidian26.requestUrl)({
        url: `https://api.github.com/repos/${repo}/releases/latest`,
        method: "GET",
        headers: {
          "Content-Type": "application/json"
        },
        contentType: "application/json"
      });
      const version4 = normalize_release_version(
        release?.tag_name || release?.name || item?.version || ""
      );
      if (version4) {
        item.version = version4;
      }
    } catch (error) {
      console.warn(`[smart-plugins:list] Failed to hydrate latest core release for ${repo}`, error);
    }
  }
}
function build_plugin_list_items(smart_plugins_list = [], env, params = {}) {
  const plugin_map = /* @__PURE__ */ new Map();
  for (const plugin of smart_plugins_list) {
    const group_key = get_plugin_group_key(plugin);
    if (!plugin_map.has(group_key)) {
      plugin_map.set(group_key, { core: null, pro: null });
    }
    const plugin_group = plugin_map.get(group_key);
    if (get_plugin_item_type(plugin) === "core") {
      plugin_group.core = plugin;
    } else {
      plugin_group.pro = plugin;
    }
  }
  return Array.from(plugin_map.values()).map((plugin_group) => {
    return new PluginListItem(env, plugin_group, params);
  });
}
var PluginListItem = class {
  constructor(env, plugins = {}, params = {}) {
    this.env = env;
    this.app = env.obsidian_app;
    this.core_plugin = plugins.core || null;
    this.pro_plugin = plugins.pro || null;
    this.root_sub_exp = normalize_positive_epoch_ms(params.root_sub_exp);
  }
  get auth_token() {
    const oauth_storage_prefix = get_oauth_storage_prefix(this.app);
    return localStorage.getItem(oauth_storage_prefix + "token") || "";
  }
  get plugin_id() {
    return String(
      this.core_plugin?.plugin_id || this.pro_plugin?.plugin_id || ""
    ).trim();
  }
  get has_core_plugin() {
    return Boolean(this.core_plugin);
  }
  get has_pro_plugin() {
    return Boolean(this.pro_plugin);
  }
  get has_group_ui() {
    return this.has_core_plugin && this.has_pro_plugin;
  }
  get display_item_type() {
    if (this.has_group_ui) return "group";
    return get_plugin_item_type(this.primary_plugin) || "pro";
  }
  get primary_plugin() {
    return this.core_plugin || this.pro_plugin || null;
  }
  get installed_manifest() {
    return this.app.plugins?.manifests?.[this.plugin_id] || null;
  }
  get is_enabled() {
    return this.app.plugins.enabledPlugins.has(this.plugin_id);
  }
  get env_plugin_state() {
    return this.env?.plugin_states?.[this.plugin_id] || null;
  }
  get installed_type() {
    if (!this.installed_manifest) return null;
    if (this.has_pro_plugin && !this.has_core_plugin) {
      return "pro";
    }
    if (this.has_core_plugin && !this.has_pro_plugin) {
      return "core";
    }
    return infer_installed_plugin_type({
      plugin_id: this.plugin_id,
      manifest_name: this.installed_manifest.name
    });
  }
  get installed_plugin() {
    return this.get_track_plugin(this.installed_type);
  }
  get loaded_version() {
    return this.app.plugins.plugins[this.plugin_id]?.manifest?.version || null;
  }
  get loaded_env_version() {
    return this.app.plugins.plugins[this.plugin_id]?.SmartEnv?.version || "";
  }
  get installed_version() {
    return this.installed_manifest?.version || null;
  }
  get is_entitled() {
    return this.pro_plugin?.entitled === true;
  }
  get tags() {
    return [.../* @__PURE__ */ new Set([
      ...get_plugin_tags(this.core_plugin),
      ...get_plugin_tags(this.pro_plugin)
    ])];
  }
  get is_experimental() {
    return this.tags.includes("experimental");
  }
  get canonical_url() {
    if (!this.has_group_ui) {
      return this.get_track_canonical_url(this.display_item_type);
    }
    return this.get_track_canonical_url(this.installed_type || "pro") || this.get_track_canonical_url("core") || "";
  }
  get details_url() {
    if (!this.has_group_ui) {
      return this.get_track_details_url(this.display_item_type);
    }
    return this.get_track_details_url(this.installed_type || "pro") || this.get_track_details_url("core") || "";
  }
  get item_sub_exp() {
    return normalize_positive_epoch_ms(this.pro_plugin?.sub_exp);
  }
  get should_show_item_subscription_state() {
    return this.has_pro_plugin && this.root_sub_exp === null && this.item_sub_exp !== null;
  }
  get subscription_status_text() {
    if (!this.should_show_item_subscription_state) {
      return "";
    }
    if (this.item_sub_exp < Date.now()) {
      return `Subscription expired ${convert_to_time_ago(this.item_sub_exp)}.`;
    }
    return `Subscription active, renews ${convert_to_time_until(this.item_sub_exp)}.`;
  }
  get formatted_name() {
    if (this.has_group_ui) {
      return format_plugin_name(get_plugin_name(this.core_plugin)) || format_plugin_name(get_plugin_name(this.pro_plugin)) || this.plugin_id;
    }
    return format_plugin_name(get_plugin_name(this.primary_plugin)) || this.plugin_id;
  }
  get label() {
    return get_plugin_label(this.primary_plugin) || this.plugin_id || "plugin";
  }
  get formatted_description() {
    return get_plugin_description(this.primary_plugin);
  }
  get is_loaded() {
    if (!this.installed_type) return false;
    return this.is_enabled && this.env_plugin_state === "loaded";
  }
  get is_deferred() {
    if (!this.installed_type) return false;
    return this.is_enabled && (this.env_plugin_state === "deferred" || this.loaded_version && this.installed_version && this.loaded_version !== this.installed_version);
  }
  get should_update() {
    const installed_type = this.installed_type;
    if (!installed_type) return false;
    const installed_plugin = this.get_track_plugin(installed_type);
    return should_offer_plugin_update({
      item_type: installed_type,
      installed_type,
      is_entitled: installed_type === "pro" ? this.is_entitled : true,
      server_version: normalize_release_version(installed_plugin?.version),
      installed_version: this.installed_version,
      compare_versions
    });
  }
  get has_outdated_env_compatibility() {
    const installed_type = this.installed_type;
    if (!installed_type) return false;
    return should_signal_outdated_env_compatibility({
      item_type: installed_type,
      installed_type,
      is_entitled: installed_type === "pro" ? this.is_entitled : true,
      is_enabled: this.is_enabled,
      is_loaded: this.is_loaded,
      is_deferred: this.is_deferred,
      loaded_env_version: this.loaded_env_version
    });
  }
  get is_installed() {
    return Boolean(this.installed_type);
  }
  get computed_state() {
    const computed_state = compute_plugin_list_item_state({
      has_core_plugin: this.has_core_plugin,
      has_pro_plugin: this.has_pro_plugin,
      display_item_type: this.display_item_type,
      installed_type: this.installed_type,
      is_entitled: this.is_entitled,
      should_update: this.should_update,
      has_outdated_env_compatibility: this.has_outdated_env_compatibility,
      is_deferred: this.is_deferred,
      is_loaded: this.is_loaded,
      is_enabled: this.is_enabled
    });
    const hydrate_track_state = (track_state) => {
      if (!track_state) return null;
      return {
        ...track_state,
        plugin: this.get_track_plugin(track_state.item_type),
        control_specs: this.get_control_specs_for_state(track_state.control_state)
      };
    };
    return {
      row: hydrate_track_state(computed_state.row),
      track_states: {
        core: hydrate_track_state(computed_state.track_states.core),
        pro: hydrate_track_state(computed_state.track_states.pro)
      }
    };
  }
  get control_specs() {
    return this.computed_state.row?.control_specs || [];
  }
  get_track_state(item_type) {
    return this.computed_state.track_states?.[item_type] || null;
  }
  get_control_specs_for_state(control_state) {
    switch (control_state) {
      case "deferred": {
        const is_updated = this.loaded_version && this.loaded_version !== this.installed_version;
        return [
          { type: "status", text: `${is_updated ? "Update ready." : "Installed & enabled."} Reload to activate` },
          { type: "button", action: "restart_obsidian", text: "Reload", variant: "primary" }
        ];
      }
      case "update_available":
        return [
          { type: "status", text: "Update available" },
          { type: "button", action: "install", text: "Update", variant: "primary" }
        ];
      case "outdated_env":
        return [
          { type: "status", text: "Reload required for current Smart Environment" },
          { type: "button", action: "restart_obsidian", text: "Reload", variant: "primary" }
        ];
      case "loaded":
        return [
          { type: "status", text: "Active" },
          { type: "button", action: "open_settings", text: "Open settings", variant: "secondary" }
        ];
      case "installed":
        return [
          { type: "status", text: "Installed" }
        ];
      case "can_enable":
        return [
          { type: "button", action: "enable", text: "Enable", variant: "primary" }
        ];
      case "included_in_pro":
        return [
          { type: "status", text: "Included in Pro" }
        ];
      case "core_installed":
        return [
          { type: "status", text: "Core installed" },
          ...this.is_entitled ? [{ type: "button", action: "install", text: "Install Pro", variant: "primary" }] : [],
          { type: "button", action: "learn_more", text: "Learn more", variant: "secondary" }
        ];
      case "can_install_core_only":
        return [
          { type: "button", action: "install", text: "Install Core", variant: "primary" }
        ];
      case "can_install_pro":
        return [
          { type: "button", action: "install", text: "Install Pro", variant: "primary" },
          { type: "button", action: "learn_more", text: "Learn more", variant: "secondary" }
        ];
      case "cant_install":
        return [
          { type: "button", action: "learn_more", text: "Learn more", variant: "secondary" }
        ];
      case "can_install":
      default:
        return [
          { type: "button", action: "install", text: "Install", variant: "primary" },
          { type: "button", action: "learn_more", text: "Learn more", variant: "secondary" }
        ];
    }
  }
  get_track_control_specs(item_type) {
    return this.get_track_state(item_type)?.control_specs || [];
  }
  get_busy_text(action) {
    const control_state = this.computed_state.row?.control_state || "";
    switch (action) {
      case "install":
        return ["update_available", "outdated_env"].includes(control_state) ? "Updating\u2026" : "Installing\u2026";
      case "enable":
        return "Enabling\u2026";
      default:
        return "";
    }
  }
  get_busy_text_for_track(item_type, action) {
    const control_state = this.get_track_state(item_type)?.control_state || "";
    switch (action) {
      case "install":
        return ["update_available", "outdated_env"].includes(control_state) ? "Updating\u2026" : "Installing\u2026";
      case "enable":
        return "Enabling\u2026";
      default:
        return this.get_busy_text(action);
    }
  }
  get install_target_plugin() {
    const control_state = this.computed_state.row?.control_state || "can_install";
    if (["update_available", "outdated_env"].includes(control_state)) {
      return this.installed_plugin;
    }
    if (control_state === "can_install_pro" || control_state === "core_installed") {
      return this.pro_plugin;
    }
    if (control_state === "can_install_core_only") {
      return this.core_plugin;
    }
    if (control_state === "can_install") {
      return this.core_plugin || this.pro_plugin;
    }
    return this.installed_plugin || this.core_plugin || this.pro_plugin;
  }
  get install_target_item_type() {
    return get_plugin_item_type(this.install_target_plugin);
  }
  get install_target_label() {
    return get_plugin_label(this.install_target_plugin) || this.label || this.plugin_id || "plugin";
  }
  get_track_plugin(item_type) {
    if (item_type === "core") return this.core_plugin;
    if (item_type === "pro") return this.pro_plugin;
    return null;
  }
  get_track_name(item_type) {
    return format_plugin_name(get_plugin_name(this.get_track_plugin(item_type))) || this.formatted_name || this.plugin_id;
  }
  get_track_description(item_type) {
    return get_plugin_description(this.get_track_plugin(item_type));
  }
  get_track_canonical_url(item_type) {
    const plugin = this.get_track_plugin(item_type) || this.primary_plugin;
    return get_plugin_canonical_url(plugin) || get_plugin_details_url(plugin) || PRO_PLUGINS_URL;
  }
  get_track_details_url(item_type) {
    const plugin = this.get_track_plugin(item_type) || this.primary_plugin;
    const release_page_url = this.installed_type === item_type ? build_plugin_release_page_url(plugin, this.installed_version) : "";
    return release_page_url || get_plugin_details_url(plugin) || get_plugin_canonical_url(plugin) || "";
  }
  get_track_subscription_status_text(item_type) {
    if (item_type !== "pro") return "";
    return this.subscription_status_text;
  }
  get_plugin_action_label(plugin) {
    return get_plugin_label(plugin) || this.install_target_label || this.label || this.plugin_id || "plugin";
  }
  async handle_action(action, params = {}) {
    switch (action) {
      case "install":
        if (should_block_pro_install({
          item_type: this.install_target_item_type,
          is_entitled: this.is_entitled
        })) {
          emit_store_event(this.env, "pro_plugins:install_blocked", params, {
            plugin_key: this.plugin_id,
            recommended_track: "pro",
            level: "warning",
            message: `${this.install_target_label} requires an active Pro entitlement.`,
            event_source: "browse_smart_plugins.list_item.install"
          });
          return;
        }
        await this.install(params);
        return;
      case "enable":
        await this.enable(params);
        return;
      case "restart_obsidian":
        this.restart_obsidian();
        return;
      case "open_settings":
        this.open_settings();
        return;
      case "learn_more":
        this.open_plugin_url(params);
        return;
      case "open_details":
        this.open_details_url();
        return;
      default:
        return;
    }
  }
  async handle_track_action(item_type, action, params = {}) {
    if (action === "install") {
      const plugin = this.get_track_plugin(item_type);
      const track_item_type = get_plugin_item_type(plugin);
      const track_label = get_plugin_label(plugin) || this.install_target_label || this.label;
      if (should_block_pro_install({
        item_type: track_item_type,
        is_entitled: this.is_entitled
      })) {
        emit_store_event(this.env, "pro_plugins:install_blocked", params, {
          plugin_key: this.plugin_id,
          recommended_track: "pro",
          level: "warning",
          message: `${track_label} requires an active Pro entitlement.`,
          event_source: "browse_smart_plugins.list_item.install"
        });
        return;
      }
      await this.install(params, plugin);
      return;
    }
    if (action === "open_details") {
      this.open_track_details_url(item_type);
      return;
    }
    if (action === "learn_more") {
      this.open_track_plugin_url(item_type, params);
      return;
    }
    await this.handle_action(action, params);
  }
  async install(params = {}, plugin = this.install_target_plugin) {
    if (!plugin) return;
    if (get_plugin_item_type(plugin) === "core") {
      if (get_plugin_install_method(plugin) === "github") {
        await this.install_github_release_plugin(params, plugin);
        return;
      }
      await this.install_core_plugin(plugin);
      return;
    }
    emit_store_event(this.env, "pro_plugin_install_clicked", params, {
      plugin_key: this.plugin_id,
      recommended_track: "pro",
      event_source: "browse_smart_plugins.list_item.install"
    });
    await this.install_plugin(params, plugin);
  }
  async enable(params = {}) {
    try {
      await enable_plugin(this.app, this.plugin_id);
      this.env?.events?.emit?.("pro_plugins:enabled", {
        level: "debug",
        message: `${this.label} enabled.`,
        event_source: "browse_smart_plugins.list_item"
      });
      this.env?.events?.emit?.("pro_plugins:refresh", {
        event_source: "browse_smart_plugins.list_item.enable"
      });
    } catch (err) {
      console.error("[smart-plugins:list] Enable error:", err);
      this.env?.events?.emit?.("pro_plugins:enable_failed", {
        level: "error",
        message: `Enable failed: ${err.message}`,
        details: err?.stack || "",
        event_source: "browse_smart_plugins.list_item"
      });
    }
  }
  restart_obsidian() {
    if (typeof this.app?.commands?.executeCommandById === "function") {
      this.app.commands.executeCommandById("app:reload");
      return;
    }
    window.location.reload();
  }
  open_settings() {
    this.app.setting.open();
    this.app.setting.openTabById(this.plugin_id);
  }
  open_details_url() {
    const details_url = this.details_url || PRO_PLUGINS_URL;
    window.open(with_utm_source(details_url, "plugin-store"), "_external");
  }
  open_track_details_url(item_type) {
    const details_url = this.get_track_details_url(item_type) || PRO_PLUGINS_URL;
    window.open(with_utm_source(details_url, "plugin-store"), "_external");
  }
  open_plugin_url(params = {}) {
    if (this.has_pro_plugin && !this.is_entitled) {
      emit_store_event(this.env, "pro_trial_cta_clicked", params, {
        plugin_key: this.plugin_id,
        recommended_track: "pro",
        event_source: "browse_smart_plugins.list_item.learn_more"
      });
    }
    const url = this.canonical_url || PRO_PLUGINS_URL;
    window.open(with_utm_source(url, "plugin-store"), "_external");
  }
  open_track_plugin_url(item_type, params = {}) {
    if (item_type === "pro" && this.has_pro_plugin && !this.is_entitled) {
      emit_store_event(this.env, "pro_trial_cta_clicked", params, {
        plugin_key: this.plugin_id,
        recommended_track: "pro",
        event_source: "browse_smart_plugins.list_item.learn_more"
      });
    }
    const url = this.get_track_canonical_url(item_type) || PRO_PLUGINS_URL;
    window.open(with_utm_source(url, "plugin-store"), "_external");
  }
  async install_core_plugin(plugin) {
    const was_installed = this.is_installed;
    const was_enabled = this.is_enabled;
    const plugin_label = this.get_plugin_action_label(plugin);
    const install_enable_behavior = get_install_enable_behavior({
      was_installed,
      was_enabled
    });
    try {
      this.env?.events?.emit?.("smart_plugins:install_started", {
        level: "debug",
        message: `Installing "${plugin_label}" ...`,
        event_source: "browse_smart_plugins.list_item"
      });
      const { json: release } = await this.get_latest_github_release(plugin);
      const version4 = release?.tag_name;
      await this.app.plugins.installPlugin(get_plugin_repo(plugin), version4, {
        id: this.plugin_id,
        name: plugin_label
      });
      if (install_enable_behavior.should_enable_after_install) {
        await this.enable();
      }
      this.env?.events?.emit?.("smart_plugins:install_completed", {
        level: "debug",
        message: `${plugin_label} installed successfully.`,
        event_source: "browse_smart_plugins.list_item"
      });
      this.env?.events?.emit?.("pro_plugins:refresh", {
        event_source: "browse_smart_plugins.list_item.core_install"
      });
    } catch (err) {
      console.error("[smart-plugins:list] Core install error:", err);
      this.env?.events?.emit?.("smart_plugins:install_failed", {
        level: "error",
        message: `Install failed: ${err.message}`,
        details: err?.stack || "",
        event_source: "browse_smart_plugins.list_item"
      });
    }
  }
  async download_plugin_files(plugin) {
    if (!this.auth_token) {
      throw new Error("Login required to install this plugin.");
    }
    const version4 = String(plugin?.version || "").trim() || null;
    const repo = get_plugin_repo(plugin);
    const files = [];
    for (const file_name of install_file_names) {
      const response = await fetch_plugin_file(repo, this.auth_token, {
        file: file_name,
        version: version4
      });
      files.push(build_plugin_file_record(file_name, response));
    }
    return files;
  }
  async install_plugin(params = {}, plugin) {
    const was_installed = this.is_installed;
    const was_enabled = this.is_enabled;
    const plugin_label = this.get_plugin_action_label(plugin);
    const install_enable_behavior = get_install_enable_behavior({
      was_installed,
      was_enabled
    });
    try {
      this.env?.events?.emit?.("pro_plugins:install_started", {
        level: "debug",
        message: `Installing "${plugin_label}" ...`,
        event_source: "browse_smart_plugins.list_item"
      });
      const files = await this.download_plugin_files(plugin);
      const folder_name = String(this.plugin_id || "").trim();
      if (!folder_name) {
        throw new Error(`Missing plugin id for "${plugin_label}".`);
      }
      const base_folder = `${this.app.vault.configDir}/plugins/${folder_name}`;
      await write_files_with_adapter(this.app.vault.adapter, base_folder, files);
      await this.app.plugins.loadManifests();
      if (install_enable_behavior.should_disable_before_install) {
        await this.app.plugins.disablePlugin(this.plugin_id);
      }
      if (install_enable_behavior.should_enable_after_install) {
        await enable_plugin(this.app, this.plugin_id);
      }
      this.env?.events?.emit?.("pro_plugins:install_completed", {
        level: "debug",
        message: `${plugin_label} installed successfully.`,
        event_source: "browse_smart_plugins.list_item"
      });
      emit_store_event(this.env, "pro_plugin_installed", params, {
        plugin_key: this.plugin_id,
        recommended_track: "pro",
        event_source: "browse_smart_plugins.list_item.install"
      });
      this.env?.events?.emit?.("pro_plugins:refresh", {
        event_source: "browse_smart_plugins.list_item.install"
      });
      if (typeof params.on_installed === "function") {
        await params.on_installed();
      }
    } catch (err) {
      console.error("[smart-plugins:list] Install error:", err);
      this.env?.events?.emit?.("pro_plugins:install_failed", {
        level: "error",
        message: `Install failed: ${err.message}`,
        details: err?.stack || "",
        event_source: "browse_smart_plugins.list_item"
      });
    }
  }
  async install_github_release_plugin(params = {}, plugin) {
    const app2 = params.app || this.app;
    const env = params.env || null;
    const repo = get_plugin_repo(plugin);
    const was_installed = this.is_installed;
    const was_enabled = this.is_enabled;
    const plugin_label = this.get_plugin_action_label(plugin);
    const install_enable_behavior = get_install_enable_behavior({
      was_installed,
      was_enabled
    });
    if (!repo) {
      throw new Error(`Missing GitHub repo for "${plugin_label}".`);
    }
    if (!this.plugin_id) {
      throw new Error(`Missing plugin id for "${plugin_label}".`);
    }
    try {
      env?.events?.emit?.("smart_plugins:install_started", {
        level: "debug",
        message: `Installing "${plugin_label}" ...`,
        event_source: "browse_smart_plugins.list_item"
      });
      const { json: release } = await this.get_latest_github_release(plugin);
      const assets = release?.assets || [];
      const main_asset = assets.find((asset) => asset.name === "main.js");
      const manifest_asset = assets.find((asset) => asset.name === "manifest.json");
      const styles_asset = assets.find((asset) => asset.name === "styles.css");
      if (!main_asset || !manifest_asset || !styles_asset) {
        throw new Error("Failed to find necessary assets in the latest GitHub release.");
      }
      const plugin_folder = `${app2.vault.configDir}/plugins/${this.plugin_id}`;
      if (!await app2.vault.adapter.exists(plugin_folder)) {
        await app2.vault.adapter.mkdir(plugin_folder);
      }
      await Promise.all([
        this.download_and_write_release_asset(app2, main_asset.browser_download_url, `${plugin_folder}/main.js`),
        this.download_and_write_release_asset(app2, manifest_asset.browser_download_url, `${plugin_folder}/manifest.json`),
        this.download_and_write_release_asset(app2, styles_asset.browser_download_url, `${plugin_folder}/styles.css`)
      ]);
      await app2.plugins.loadManifests();
      if (install_enable_behavior.should_disable_before_install) {
        await app2.plugins.disablePlugin(this.plugin_id);
      }
      if (install_enable_behavior.should_enable_after_install) {
        await enable_plugin(app2, this.plugin_id);
      }
      env?.events?.emit?.("smart_plugins:install_completed", {
        level: "debug",
        message: `${plugin_label} installed successfully.`,
        event_source: "browse_smart_plugins.list_item.install_github_release_plugin"
      });
      env?.events?.emit?.("pro_plugins:refresh", {
        event_source: "browse_smart_plugins.list_item.install_github_release_plugin"
      });
      if (typeof params.on_installed === "function") {
        await params.on_installed();
      }
    } catch (err) {
      console.error("[smart-plugins:list] GitHub install error:", err);
      env?.events?.emit?.("smart_plugins:install_failed", {
        level: "error",
        message: `Install failed: ${err.message}`,
        details: err?.stack || "",
        event_source: "browse_smart_plugins.list_item.install_github_release_plugin"
      });
    }
  }
  async get_latest_github_release(plugin) {
    const repo = get_plugin_repo(plugin) || get_plugin_repo(this.install_target_plugin);
    return await (0, import_obsidian26.requestUrl)({
      url: `https://api.github.com/repos/${repo}/releases/latest`,
      method: "GET",
      headers: {
        "Content-Type": "application/json"
      },
      contentType: "application/json"
    });
  }
  async download_and_write_release_asset(app2, download_url, output_path) {
    const resp = await (0, import_obsidian26.requestUrl)({
      url: download_url,
      method: "GET"
    });
    await app2.vault.adapter.write(output_path, resp.text);
  }
};
function get_plugin_group_key(plugin) {
  return plugin?.plugin_id || `${get_plugin_item_type(plugin)}:${get_plugin_name(plugin)}`;
}
function get_plugin_item_type(plugin = {}) {
  return String(plugin?.item_type || "").trim();
}
function get_plugin_repo(plugin = {}) {
  return String(plugin?.item_repo || plugin?.repo || "").trim();
}
function get_plugin_name(plugin = {}) {
  return String(plugin?.item_name || plugin?.name || plugin?.plugin_id || "").trim();
}
function get_plugin_label(plugin = {}) {
  return String(
    plugin?.item_name || plugin?.name || plugin?.plugin_id || plugin?.repo || "plugin"
  ).trim();
}
function get_plugin_description(plugin = {}) {
  return String(plugin?.item_desc || plugin?.description || "").trim();
}
function get_plugin_canonical_url(plugin = {}) {
  return String(
    plugin?.main_url || plugin?.url || plugin?.info_url || plugin?.details_url || plugin?.docs_url || ""
  ).trim();
}
function get_plugin_details_url(plugin = {}) {
  return String(
    plugin?.details_url || plugin?.docs_url || plugin?.info_url || plugin?.url || ""
  ).trim();
}
function build_plugin_release_page_url(plugin = {}, version4 = "") {
  const main_url = String(plugin?.main_url || plugin?.url || "").trim();
  const version_slug = get_release_page_slug(version4);
  if (!main_url || !version_slug) return "";
  const base_url = main_url.split("#")[0].split("?")[0].replace(/\/+$/, "");
  if (!base_url) return "";
  return `${base_url}/releases/${version_slug}/`;
}
function get_release_page_slug(version4 = "") {
  const version_pcs = normalize_release_version(version4).split(".");
  const major = String(version_pcs[0] || "").trim();
  const minor = String(version_pcs[1] || "").trim();
  if (!major || !minor) return "";
  return `${major}-${minor}`;
}
function get_plugin_install_method(plugin = {}) {
  return String(plugin?.install_method || "server").trim() || "server";
}
function get_plugin_tags(plugin = {}) {
  return Array.isArray(plugin?.tags) ? plugin.tags.map((tag) => String(tag || "").trim().toLowerCase()).filter(Boolean) : [];
}
function format_plugin_name(name = "") {
  return String(name || "").replace(/\bSmart\s+/g, "").replace(/\bPro\b/g, "").replace(/\s{2,}/g, " ").trim();
}
function partition_plugin_items(plugin_items = []) {
  return plugin_items.reduce((acc, item) => {
    if (item.is_experimental) {
      acc.experimental_items.push(item);
    } else {
      acc.official_items.push(item);
    }
    return acc;
  }, {
    official_items: [],
    experimental_items: []
  });
}
function with_utm_source(url, source) {
  if (!url) return PRO_PLUGINS_URL;
  return url.includes("?") ? `${url}&utm_source=${source}` : `${url}?utm_source=${source}`;
}
function build_invalid_credentials_message(server_message = "") {
  const default_message = "Invalid account credentials. Log out and log in again.";
  const safe_server_message = String(server_message || "").trim();
  if (!safe_server_message) {
    return default_message;
  }
  const normalized_safe_message = safe_server_message.toLowerCase();
  if (normalized_safe_message === "unauthorized" || normalized_safe_message === default_message.toLowerCase()) {
    return default_message;
  }
  return `${default_message}

${safe_server_message}`;
}

// node_modules/obsidian-smart-env/src/components/smart-plugins/list_item.js
var import_obsidian27 = require("obsidian");
function build_html19(item, params = {}) {
  if (item.has_group_ui) {
    return build_group_html(item, params);
  }
  const row_state = item.computed_state.row;
  return build_row_html(item, {
    item_type: item.display_item_type,
    name: item.formatted_name,
    subscription_status_text: item.subscription_status_text,
    control_state: row_state?.control_state || "can_install"
  });
}
function build_group_html(item, params = {}) {
  const core_state = item.get_track_state("core");
  const pro_state = item.get_track_state("pro");
  return `<div class="setting-group smart-plugins-item-group smart-plugins-item-group-combined" data-group-size="2">
    <div class="setting-items">
      ${build_row_html(item, {
    item_type: "core",
    track_item_type: "core",
    name: item.get_track_name("core"),
    subscription_status_text: "",
    control_state: core_state?.control_state || "cant_install"
  })}
      ${build_row_html(item, {
    item_type: "pro",
    track_item_type: "pro",
    name: item.get_track_name("pro"),
    subscription_status_text: item.get_track_subscription_status_text("pro"),
    control_state: pro_state?.control_state || "cant_install"
  })}
    </div>
  </div>`;
}
function build_row_html(item, row = {}) {
  const item_type = row.item_type || item.display_item_type;
  const control_state = row.control_state || item.computed_state.row?.control_state || "can_install";
  const subscription_state_html = row.subscription_status_text ? `<div class="smart-plugins-item-subscription-state">${row.subscription_status_text}</div>` : "";
  const track_item_type_attr = row.track_item_type ? ` data-track-item-type="${row.track_item_type}"` : "";
  return `<div class="setting-item pro-plugins-list-item" data-item-type="${item_type}" data-item-state="${control_state}" data-row-control-state="${control_state}"${track_item_type_attr}>
    <div class="setting-item-info">
      <div class="setting-item-name ${item_type === "core" ? "smart-badge core-badge" : "smart-badge pro-badge"}">${row.name || ""}</div>
      <div class="setting-item-description smart-plugins-item-description markdown-rendered"></div>
      ${subscription_state_html}
    </div>
    <div class="setting-item-control"></div>
  </div>`;
}
async function render21(item, params = {}) {
  const html = build_html19(item, params);
  const frag = this.create_doc_fragment(html);
  const container = frag.firstElementChild;
  await post_process18.call(this, item, container, params);
  return container;
}
async function post_process18(item, container, params = {}) {
  const rows = item.has_group_ui ? Array.from(container.querySelectorAll(".pro-plugins-list-item[data-track-item-type]")) : [container];
  for (const row of rows) {
    const track_item_type = row.getAttribute("data-track-item-type") || "";
    const description_container = row.querySelector(".setting-item-description");
    await render_description.call(this, item, description_container, get_row_description(item, track_item_type), params);
    const control_container = row.querySelector(".setting-item-control");
    if (!control_container) continue;
    const controls = await render_controls.call(this, item, {
      ...params,
      track_item_type
    });
    this.empty(control_container);
    if (controls) control_container.appendChild(controls);
  }
  return container;
}
function get_row_description(item, track_item_type = "") {
  const safe_track_item_type = String(track_item_type || "").trim();
  if (safe_track_item_type) {
    return item.get_track_description(safe_track_item_type);
  }
  return item.formatted_description;
}
async function render_description(item, container, markdown = "", params = {}) {
  if (!container) return;
  this.empty(container);
  const safe_markdown = String(markdown || "").trim();
  if (!safe_markdown) return;
  const app2 = params.app || item.app || item.env?.obsidian_app || window.app;
  const component = item.env?.main || item.env?.plugin || null;
  await import_obsidian27.MarkdownRenderer.render(app2, safe_markdown, container, "", component);
  container.querySelectorAll("a").forEach((a) => {
    a.setAttribute("target", "_external");
    try {
      const url = new URL(a.href);
      if (!url.searchParams.has("utm_source")) {
        url.searchParams.set("utm_source", "plugin_list_description");
        a.href = url.toString();
      }
    } catch (e) {
    }
  });
}
function build_controls_html(item, params = {}) {
  const track_item_type = String(params.track_item_type || "").trim();
  const details_url = track_item_type ? item.get_track_details_url(track_item_type) : item.details_url;
  const control_specs = track_item_type ? item.get_track_control_specs(track_item_type) : item.control_specs;
  const details_button_html = details_url ? '<button class="smart-plugins-details-button" data-action="open_details" aria-label="Open details" title="Open details"></button>' : "";
  return `<div class="smart-plugins-list-item-controls">${details_button_html}${control_specs.map(build_control_html).join("")}</div>`;
}
async function render_controls(item, params = {}) {
  const html = build_controls_html(item, params);
  const frag = this.create_doc_fragment(html);
  const container = frag.firstElementChild;
  post_process_controls.call(this, item, container, params);
  return container;
}
async function post_process_controls(item, container, params = {}) {
  const track_item_type = String(params.track_item_type || "").trim();
  const details_buttons = container.querySelectorAll(".smart-plugins-details-button");
  for (const details_button of details_buttons) {
    (0, import_obsidian27.setIcon)(details_button, "info");
  }
  const buttons = container.querySelectorAll("button[data-action]");
  for (const button of buttons) {
    button.addEventListener("click", async () => {
      const action = button.getAttribute("data-action");
      if (!action) return;
      const busy_text = track_item_type ? item.get_busy_text_for_track(track_item_type, action) : item.get_busy_text(action);
      if (busy_text) {
        await run_busy_action(button, async () => {
          if (track_item_type) {
            await item.handle_track_action(track_item_type, action, params);
            return;
          }
          await item.handle_action(action, params);
        }, busy_text);
        return;
      }
      if (track_item_type) {
        await item.handle_track_action(track_item_type, action, params);
        return;
      }
      await item.handle_action(action, params);
    });
  }
  return container;
}
function build_control_html(control_spec) {
  if (control_spec.type === "status") {
    return `<span class="core-installed-text">${control_spec.text}</span>`;
  }
  const class_name = control_spec.variant === "primary" ? "mod-cta" : "";
  return `<button class="${class_name}" data-action="${control_spec.action}">${control_spec.text}</button>`;
}
async function run_busy_action(button, callback, busy_text) {
  if (!button || typeof callback !== "function") return;
  const idle_text = button.textContent;
  button.disabled = true;
  if (busy_text) button.textContent = busy_text;
  try {
    await callback();
  } finally {
    button.disabled = false;
    button.textContent = idle_text;
  }
}

// node_modules/obsidian-smart-env/src/components/smart-plugins/login.js
var import_obsidian28 = require("obsidian");
function build_html20(env, params = {}) {
  return `<div class="smart-plugins-login-component"></div>`;
}
async function render22(env, params = {}) {
  const html = build_html20.call(this, env, params);
  const frag = this.create_doc_fragment(html);
  const container = frag.firstElementChild;
  await post_process19.call(this, env, container, params);
  return container;
}
async function post_process19(env, container, params = {}) {
  const app2 = env?.plugin?.app || window.app;
  const oauth_storage_prefix = get_oauth_storage_prefix(app2);
  const sub_exp = Number(params.sub_exp ?? 0) || 0;
  let login_click_count = 0;
  let last_login_url = "";
  let manual_login_el = null;
  const token = localStorage.getItem(oauth_storage_prefix + "token") || "";
  const auth_state = String(params.auth_state || "").trim() || (token ? "signed_in" : "signed_out");
  const logout = () => {
    localStorage.removeItem(oauth_storage_prefix + "token");
    localStorage.removeItem(oauth_storage_prefix + "refresh");
    env?.events?.emit?.("pro_plugins:logged_out", {
      level: "info",
      message: "Logged out of Smart Plugins",
      event_source: "smart_plugins_login"
    });
  };
  const render_manual_login_link = (login_url) => {
    if (!login_url) return;
    if (!manual_login_el || !manual_login_el.isConnected) {
      manual_login_el = document.createElement("div");
      manual_login_el.classList.add("smart-plugins-login-manual");
      container.appendChild(manual_login_el);
    }
    manual_login_el.innerHTML = "";
    const instructions = document.createElement("div");
    instructions.classList.add("smart-plugins-login-manual-instructions");
    instructions.textContent = "If the login page did not open, copy this link and paste it into your browser to open the login page:";
    manual_login_el.appendChild(instructions);
    const controls = document.createElement("div");
    controls.classList.add("smart-plugins-login-manual-controls");
    manual_login_el.appendChild(controls);
    const input = document.createElement("input");
    input.classList.add("smart-plugins-login-manual-input");
    input.type = "text";
    input.value = login_url;
    input.readOnly = true;
    input.addEventListener("focus", () => input.select());
    controls.appendChild(input);
    const btn = document.createElement("button");
    btn.classList.add("mod-cta");
    btn.textContent = "Copy";
    btn.addEventListener("click", async () => {
      await copy_to_clipboard(login_url, {
        env,
        event_source: "smart_plugins_login.manual_link_copy",
        success_event_key: "pro_plugins:login_link_copied",
        error_event_key: "pro_plugins:login_link_copy_failed",
        unavailable_event_key: "pro_plugins:login_link_copy_unavailable"
      });
    });
    controls.appendChild(btn);
  };
  if (auth_state === "checking") {
    new import_obsidian28.Setting(container).setName("Checking session...").setDesc("Validating your Smart Plugins account session.");
    return container;
  }
  if (auth_state === "invalid") {
    const setting2 = new import_obsidian28.Setting(container).setName("Session needs refresh").setDesc("Invalid account credentials. Log out and log in again.");
    setting2.addButton((btn) => {
      btn.setButtonText("Refresh");
      btn.onClick(() => {
        env?.events?.emit?.("pro_plugins:refresh", {
          event_source: "smart_plugins_login"
        });
      });
    });
    setting2.addButton((btn) => {
      btn.setButtonText("Logout");
      btn.onClick(() => {
        logout();
      });
    });
    return container;
  }
  if (auth_state === "signed_out" || !token) {
    const setting2 = new import_obsidian28.Setting(container).setName("Connect account").setDesc("Log in with the key provided in your Pro welcome email.");
    setting2.addButton((btn) => {
      btn.setButtonText("Login");
      btn.onClick(() => {
        login_click_count += 1;
        last_login_url = initiate_smart_plugins_oauth();
        if (login_click_count >= 2) {
          render_manual_login_link(last_login_url);
        }
        env?.events?.emit?.("pro_plugins:oauth_browser_login_requested", {
          level: "info",
          message: "Please complete the login in your browser.",
          event_source: "smart_plugins_login"
        });
      });
    });
    return container;
  }
  if (sub_exp && sub_exp < Date.now()) {
    const setting2 = new import_obsidian28.Setting(container).setName("All-access subscription expired").setDesc("Your Smart Plugins all-access subscription has expired. Please update your subscription to retain access to Pro plugins.");
    setting2.addButton((btn) => {
      btn.setButtonText("Get Pro");
      btn.onClick(() => {
        window.open("https://smartconnections.app/subscribe/", "_external");
      });
    });
    setting2.addButton((btn) => {
      btn.setButtonText("Update subscription");
      btn.onClick(() => {
        window.open("https://smartconnections.app/subscription-update/", "_external");
      });
    });
    setting2.addButton((btn) => {
      btn.setButtonText("Refresh");
      btn.onClick(() => {
        env?.events?.emit?.("pro_plugins:refresh", {
          event_source: "smart_plugins_login"
        });
      });
    });
    return container;
  }
  const setting = new import_obsidian28.Setting(container);
  const subscription_text = subscription_status(sub_exp);
  setting.setDesc(
    subscription_text ? `Signed in to Smart Plugins account. ${subscription_text}` : "Signed in to Smart Plugins account."
  );
  setting.addButton((btn) => {
    btn.setButtonText("Logout");
    btn.onClick(() => {
      logout();
    });
  });
  return container;
}
function initiate_smart_plugins_oauth() {
  const state = Math.random().toString(36).slice(2);
  const redirect_uri = encodeURIComponent("obsidian://smart-plugins/callback");
  const url = `${get_smart_server_url()}/oauth?client_id=smart-plugins-op&redirect_uri=${redirect_uri}&state=${state}`;
  window.open(url, "_external");
  return url;
}
function subscription_status(sub_exp) {
  const normalized_sub_exp = Number(sub_exp || 0);
  if (!Number.isFinite(normalized_sub_exp) || normalized_sub_exp <= 0) {
    return "";
  }
  if (normalized_sub_exp < Date.now()) {
    return `All-access subscription expired ${convert_to_time_ago(normalized_sub_exp)}.`;
  }
  return `All-access subscription active, expires ${convert_to_time_until(normalized_sub_exp)}.`;
}

// node_modules/obsidian-smart-env/src/components/smart-plugins/referral.js
var import_obsidian29 = require("obsidian");

// node_modules/obsidian-smart-env/src/components/smart-plugins/onboarding_signup.js
var DEFAULT_ONBOARDING_START_URL = "https://smartconnections.app/onboarding/start/";
function build_onboarding_start_url(params = {}) {
  const source = String(params.source || "").trim();
  if (!source) return DEFAULT_ONBOARDING_START_URL;
  const url = new URL(DEFAULT_ONBOARDING_START_URL);
  url.searchParams.set("source", source);
  return url.toString();
}
function get_onboarding_signup_setting_copy() {
  return {
    name: "Get the 12-part getting started email series",
    description: "Receive a practical onboarding sequence with focused Smart Plugins workflows.",
    button_text: "Subscribe"
  };
}

// node_modules/obsidian-smart-env/src/components/smart-plugins/referral.js
function build_html21(env, params = {}) {
  return `<div class="smart-plugins-referral-component"></div>`;
}
async function render23(env, params = {}) {
  const html = build_html21.call(this, env, params);
  const frag = this.create_doc_fragment(html);
  const container = frag.firstElementChild;
  await post_process20.call(this, env, container, params);
  return container;
}
async function post_process20(env, container, params = {}) {
  const render_onboarding_signup_section = () => {
    const copy = get_onboarding_signup_setting_copy();
    const setting = new import_obsidian29.Setting(container).setName(copy.name).setDesc(copy.description);
    setting.addButton((btn) => {
      btn.setButtonText(copy.button_text);
      btn.onClick(() => {
        const onboarding_url = build_onboarding_start_url({ source: "plugins_settings" });
        window.open(onboarding_url, "_external");
        env?.events?.emit?.("onboarding:opened_signup", {
          event_source: "smart_plugins_referral"
        });
      });
    });
  };
  const emit_referral_event = (event_key) => {
    env?.events?.emit?.(event_key, {
      event_source: "smart_plugins_referral"
    });
  };
  render_onboarding_signup_section();
  const token = String(params.token || "").trim();
  if (!token) {
    const setting = new import_obsidian29.Setting(container).setName("Give $30 off Pro. Get 30 days of Pro").setDesc("Start a free trial to unlock your referral link.");
    setting.addButton((btn) => {
      btn.setButtonText("Start free trial");
      btn.onClick(() => {
        window.open("https://smartconnections.app/pro-plugins/", "_external");
      });
    });
    return container;
  }
  const sub_exp = Number(params.sub_exp ?? 0) || 0;
  if (sub_exp && sub_exp < Date.now()) {
    return container;
  }
  try {
    const stats = await fetch_referral_stats({ token });
    const referral_link = String(stats?.referral_link || "").trim();
    if (!referral_link) return container;
    const setting = new import_obsidian29.Setting(container).setName("Referral link").setDesc("Give $30 off Pro. Get 30 days of Pro.");
    setting.addButton((btn) => {
      btn.setButtonText("Copy link");
      btn.onClick(async () => {
        const copied = await copy_to_clipboard(referral_link, {
          env,
          event_source: "smart_plugins_referral.copy_link",
          success_event_key: "referrals:copied_link_notice",
          error_event_key: "referrals:copy_link_failed",
          unavailable_event_key: "referrals:copy_link_unavailable"
        });
        if (copied) emit_referral_event("referrals:copied_link");
      });
    });
    setting.addButton((btn) => {
      btn.setButtonText("Open referrals");
      btn.onClick(() => {
        window.open("https://smartconnections.app/my-referrals/", "_external");
        emit_referral_event("referrals:opened_dashboard");
      });
    });
  } catch (err) {
    console.error("[smart-plugins:referral] Failed to load referral stats:", err);
  }
  return container;
}

// node_modules/obsidian-smart-env/src/components/source_inspector.css
var source_inspector_default = ".source-inspector {\r\n  background-color: var(--background-secondary-alt);\r\n  margin: var(--size-4-3) 0;\r\n  padding: var(--size-4-3);\r\n  border-radius: var(--radius-m);\r\n}\r\n\r\n.source-inspector-blocks-container {\r\n  margin-top: var(--size-4-2);\r\n  display: flex;\r\n  flex-direction: column;\r\n  gap: var(--size-4-3);\r\n}\r\n\r\n.source-inspector-blocks-container blockquote {\r\n  margin-left: var(--size-4-3);\r\n  padding-left: var(--size-4-3);\r\n  border-left: 2px solid var(--text-faint);\r\n}\r\n";

// node_modules/obsidian-smart-env/src/components/source_inspector.js
function build_html22(source, opts = {}) {
  return `<div>
    <div class="source-inspector-source-info">
      <button class="source-inspector-show-data-btn" type="button">Show source data</button>
      <div class="source-inspector-source-data" style="display:none; margin: 0.5em 0;">
        <pre style="max-height:300px; overflow:auto; background:#222; color:#fff; padding:0.5em; border-radius:4px;"></pre>
      </div>
    </div>
    <div class="smart-chat-message source-inspector">
      <h2>Blocks</h2>
      <div class="source-inspector-blocks-container"></div>
    </div>
  </div>`;
}
async function render24(source, opts = {}) {
  const html = build_html22(source, opts);
  const frag = this.create_doc_fragment(html);
  this.apply_style_sheet(source_inspector_default);
  await post_process21.call(this, source, frag, opts);
  return frag;
}
async function post_process21(source, frag, opts = {}) {
  const container = frag.querySelector(".source-inspector .source-inspector-blocks-container");
  if (!container) return frag;
  const source_info = frag.querySelector(".source-inspector-source-info");
  const btn = frag.querySelector(".source-inspector-show-data-btn");
  const data_div = frag.querySelector(".source-inspector-source-data");
  const pre = data_div?.querySelector("pre");
  if (btn && data_div && pre) {
    btn.addEventListener("click", () => {
      if (data_div.style.display === "none") {
        pre.textContent = JSON.stringify(source.data, null, 2);
        data_div.style.display = "";
        btn.textContent = "Hide source data";
      } else {
        data_div.style.display = "none";
        btn.textContent = "Show source data";
      }
    });
  }
  const source_should_embed = source.should_embed ? `<span style="color: green;">should embed</span>` : `<span style="color: orange;">embedding skipped</span>`;
  const source_embed_status = source.vec ? `<span style="color: green;">vectorized</span>` : `<span style="color: orange;">not vectorized</span>`;
  const source_info_frag = this.create_doc_fragment(`<p>${source_should_embed} | ${source_embed_status}</p>`);
  source_info.appendChild(source_info_frag);
  if (!source || !source.blocks || source.blocks.length === 0) {
    this.safe_inner_html(container, `<p>No blocks</p>`);
    return frag;
  }
  const sorted_blocks = source.blocks.sort((a, b) => a.line_start - b.line_start);
  for (const block of sorted_blocks) {
    const sub_key_display = block.sub_key.split("#").join(" > ");
    const block_info = `${sub_key_display} (${block.size} chars; lines: ${block.line_start}-${block.line_end})`;
    const should_embed = block.should_embed ? `<span style="color: green;">should embed</span>` : `<span style="color: orange;">embedding skipped</span>`;
    const embed_status = block.vec ? `<span style="color: green;">vectorized</span>` : `<span style="color: orange;">not vectorized</span>`;
    let block_content = "";
    let embed_input = "";
    try {
      const raw = await block.read();
      block_content = raw.replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\n/g, "<br>").replace(/\t/g, "&nbsp;&nbsp;");
      const embed_raw = await block.get_embed_input(raw);
      embed_input = embed_raw.replace(/</g, "&lt;").replace(/>/g, "&gt;");
    } catch (err) {
      console.error("[source_inspector] Error reading block:", err);
      block_content = `<em style="color:red;">Error reading block content</em>`;
    }
    const block_frag = this.create_doc_fragment(`
      <p>
        ${block_info}<br>
        ${should_embed} | ${embed_status}
      </p>
      <details class="source-inspector-embed-input">
        <summary>Embed input</summary>
        <pre style="max-height:300px; overflow:auto; background:#222; color:#fff; padding:0.5em; border-radius:4px;">${embed_input}</pre>
      </details>
      <blockquote>${block_content}</blockquote>
      <hr>
    `);
    container.appendChild(block_frag);
  }
  return frag;
}

// node_modules/obsidian-smart-env/src/components/status_bar.js
var import_obsidian33 = require("obsidian");

// node_modules/obsidian-smart-env/src/utils/register_status_bar_context_menu.js
var import_obsidian32 = require("obsidian");

// node_modules/obsidian-smart-env/views/source_inspector.js
var import_obsidian30 = require("obsidian");
var SmartNoteInspectModal = class extends import_obsidian30.Modal {
  constructor(smart_connections_plugin, entity) {
    super(smart_connections_plugin.app);
    this.smart_connections_plugin = smart_connections_plugin;
    this.entity = entity;
  }
  get env() {
    return this.smart_connections_plugin.env;
  }
  onOpen() {
    this.titleEl.innerText = this.entity.key;
    this.render();
  }
  async render() {
    this.contentEl.empty();
    const frag = await this.env.smart_components.render_component("source_inspector", this.entity);
    this.contentEl.appendChild(frag);
  }
};

// node_modules/obsidian-smart-env/src/modals/env_stats.js
var import_obsidian31 = require("obsidian");
var EnvStatsModal = class extends import_obsidian31.Modal {
  constructor(app2, env) {
    super(app2);
    this.env = env;
  }
  onOpen() {
    this.titleEl.setText("Smart Environment");
    this.contentEl.empty();
    this.contentEl.createEl("p", { text: "Loading stats..." });
    setTimeout(this.render.bind(this), 100);
  }
  async render() {
    const frag = await this.env.smart_components.render_component("env_stats", this.env);
    this.contentEl.empty();
    if (frag) {
      this.contentEl.appendChild(frag);
    } else {
      this.contentEl.createEl("p", { text: "Failed to load stats." });
    }
  }
};

// node_modules/obsidian-smart-env/src/utils/register_status_bar_context_menu.js
function register_status_bar_context_menu(env, status_container, deps = {}) {
  const { Menu: MenuClass = import_obsidian32.Menu } = deps;
  const plugin = env.main;
  const on_context_menu = (ev) => {
    ev.preventDefault();
    ev.stopPropagation();
    const menu = new MenuClass(plugin.app);
    menu.addItem(
      (item) => item.setTitle("Inspect active note").setIcon("search").onClick(async () => {
        const active_file = plugin.app.workspace.getActiveFile();
        if (!active_file) {
          env?.events?.emit?.("status_bar:inspect_active_note_missing", {
            level: "warning",
            message: "No active note found",
            event_source: "register_status_bar_context_menu.inspect"
          });
          return;
        }
        const src = env.smart_sources?.get(active_file.path);
        if (!src) {
          env?.events?.emit?.("status_bar:inspect_source_missing", {
            level: "warning",
            message: "Active note is not indexed by Smart Environment",
            event_source: "register_status_bar_context_menu.inspect"
          });
          return;
        }
        new SmartNoteInspectModal(plugin, src).open();
      })
    );
    menu.addItem(
      (item) => item.setTitle("Show stats").setIcon("chart-pie").onClick(() => {
        const modal = new EnvStatsModal(plugin.app, env);
        modal.open();
      })
    );
    menu.addItem(
      (item) => item.setTitle("Export data").setIcon("download").onClick(() => {
        env.export_json();
        env?.events?.emit?.("smart_env:exported", {
          level: "attention",
          message: "Smart Env exported",
          event_source: "register_status_bar_context_menu.export"
        });
      })
    );
    menu.addItem(
      (item) => item.setTitle("Milestones").setIcon("flag").onClick(() => {
        env.open_milestones_modal();
      })
    );
    menu.addItem(
      (item) => item.setTitle("Notifications").setIcon("bell").onClick(() => {
        env.open_notifications_feed_modal();
      })
    );
    menu.addSeparator();
    menu.addItem(
      (item) => item.setTitle("Browse Smart Plugins").setIcon("package").onClick(() => {
        env.events?.emit?.("smart_plugins:browse", {
          event_source: "status_bar"
        });
      })
    );
    if (env.is_pro) {
      menu.addItem(
        (item) => item.setTitle("Refer a friend (Give 30, Get 30)").setIcon("hand-heart").onClick(() => {
          const url = "https://smartconnections.app/my-referrals/?utm_source=status-bar";
          window.open(url, "_external");
        })
      );
    } else {
      menu.addItem(
        (item) => item.setTitle("Start 14-day Pro trial").setIcon("hand-heart").onClick(() => {
          const url = "https://smartconnections.app/pro-plugins/?utm_source=status-bar";
          window.open(url, "_external");
        })
      );
    }
    menu.showAtPosition({ x: ev.pageX, y: ev.pageY });
  };
  plugin.registerDomEvent(status_container, "contextmenu", on_context_menu);
  return on_context_menu;
}

// node_modules/obsidian-smart-env/src/components/status_bar.css
var status_bar_default = ".status-bar-item:has(.smart-env-status-container) {\n  padding: 0 0.5em;\n\n  &:hover {\n    background-color: var(--background-modifier-hover);\n  }\n  & > .smart-env-status-container {\n    display: flex;\n    align-items: center;\n    gap: 0.5em;\n    text-decoration: none;\n    color: var(--status-bar-text-color);\n  }\n}\n\n.smart-env-status-indicator {\n  --smart-env-status-indicator-color: var(--status-bar-text-color);\n  --smart-env-status-indicator-glow-size: 0.28em;\n  --smart-env-status-indicator-glow-opacity: 0.38;\n\n  position: relative;\n  isolation: isolate;\n\n  width: 0.62em;\n  height: 0.62em;\n  min-width: 0.62em;\n  min-height: 0.62em;\n  border-radius: 999px;\n\n  color: var(--smart-env-status-indicator-color);\n  background-color: currentColor;\n  opacity: 1;\n  transform: scale(1);\n  box-shadow:\n    0 0 0 1px color-mix(in srgb, currentColor 24%, transparent),\n    inset 0 0 0 1px color-mix(in srgb, var(--background-primary) 78%, transparent);\n  transition:\n    background-color 150ms ease,\n    box-shadow 150ms ease,\n    transform 150ms ease,\n    color 150ms ease;\n  cursor: pointer;\n}\n\n.smart-env-status-indicator::before {\n  content: '';\n  position: absolute;\n  inset: calc(var(--smart-env-status-indicator-glow-size) * -1);\n  border-radius: inherit;\n  pointer-events: none;\n  z-index: -1;\n  opacity: var(--smart-env-status-indicator-glow-opacity);\n  transform: scale(1);\n  filter: blur(7px);\n  background:\n    radial-gradient(\n      circle at center,\n      color-mix(in srgb, currentColor 74%, transparent) 0%,\n      color-mix(in srgb, currentColor 46%, transparent) 34%,\n      color-mix(in srgb, currentColor 18%, transparent) 58%,\n      transparent 80%\n    );\n  transition:\n    opacity 150ms ease,\n    transform 150ms ease,\n    filter 150ms ease;\n}\n\n.smart-env-status-indicator::after {\n  content: '';\n  position: absolute;\n  inset: -1px;\n  border-radius: inherit;\n  pointer-events: none;\n  box-shadow: 0 0 0 1px color-mix(in srgb, currentColor 28%, transparent);\n  opacity: 0.82;\n}\n\n.smart-env-status-indicator:hover::before,\n.smart-env-status-indicator:focus-visible::before {\n  opacity: max(var(--smart-env-status-indicator-glow-opacity), 0.56);\n  transform: scale(1.08);\n}\n\n.smart-env-status-indicator:focus-visible {\n  outline: 2px solid var(--color-accent);\n  outline-offset: 2px;\n}\n\n.smart-env-status-indicator[data-level='default'] {\n  --smart-env-status-indicator-color: var(--status-bar-text-color);\n  --smart-env-status-indicator-glow-size: 0.24em;\n  --smart-env-status-indicator-glow-opacity: 0.32;\n}\n\n.smart-env-status-indicator[data-level='info'] {\n  --smart-env-status-indicator-color: var(--status-bar-text-color);\n  --smart-env-status-indicator-glow-size: 0.28em;\n  --smart-env-status-indicator-glow-opacity: 0.4;\n}\n\n.smart-env-status-indicator[data-level='milestone'] {\n  --smart-env-status-indicator-color: var(--color-accent);\n  --smart-env-status-indicator-glow-size: 0.52em;\n  --smart-env-status-indicator-glow-opacity: 0.86;\n}\n\n.smart-env-status-indicator[data-level='attention'] {\n  --smart-env-status-indicator-color: var(--color-yellow);\n  --smart-env-status-indicator-glow-size: 0.5em;\n  --smart-env-status-indicator-glow-opacity: 0.84;\n}\n\n.smart-env-status-indicator[data-level='warning'] {\n  --smart-env-status-indicator-color: var(--color-orange);\n  --smart-env-status-indicator-glow-size: 0.54em;\n  --smart-env-status-indicator-glow-opacity: 0.9;\n}\n\n.smart-env-status-indicator[data-level='error'] {\n  --smart-env-status-indicator-color: var(--color-red);\n  --smart-env-status-indicator-glow-size: 0.58em;\n  --smart-env-status-indicator-glow-opacity: 0.96;\n}\n\n.smart-env-status-indicator[data-count] {\n  transform: scale(1.08);\n  box-shadow:\n    0 0 0 1px color-mix(in srgb, currentColor 30%, transparent),\n    inset 0 0 0 1px color-mix(in srgb, var(--background-primary) 84%, transparent);\n}\n\n.smart-env-status-indicator[data-count]::before {\n  opacity: 1;\n  transform: scale(1.12);\n  filter: blur(7px);\n  animation: smart-env-status-indicator-glow 2200ms ease-in-out infinite;\n}\n\n.status-bar-mobile {\n  position: var(--status-bar-position);\n  bottom: 0;\n  border-radius: 0 8px 0 0;\n  border-style: solid;\n  border-width: 1px;\n  border-color: var(--status-bar-border-color);\n  background-color: var(--status-bar-background);\n  color: var(--status-bar-text-color);\n  font-size: var(--status-bar-font-size);\n  min-height: 18px;\n  padding: var(--size-4-1);\n  user-select: none;\n  z-index: var(--layer-status-bar);\n  font-variant-numeric: tabular-nums;\n  & > .smart-env-status-container {\n    padding: 5px 5px 5px 0;\n  }\n}\n\n/* footer view on mobile */\n.embedded-backlinks > .status-bar-mobile {\n  position: relative;\n  border-style: none;\n}\n\n@keyframes smart-env-status-indicator-glow {\n  0%,\n  100% {\n    transform: scale(0.88);\n    opacity: 0.92;\n  }\n\n  50% {\n    transform: scale(1.23);\n    opacity: 1;\n  }\n}\n";

// node_modules/obsidian-smart-env/src/components/status_bar.js
function build_html23() {
  return `
    <a
      class="smart-env-status-container"
      role="button"
      title="Smart Environment status"
      tabindex="0"
    >
      <span class="smart-env-status-icon" aria-hidden="true"></span>
      <span class="smart-env-status-msg" aria-live="polite"></span>
      <span
        class="smart-env-status-indicator"
        title="Open events feed"
        role="button"
        tabindex="0"
      ></span>
    </a>
  `;
}
async function render25(env, opts = {}) {
  this.apply_style_sheet(status_bar_default);
  const frag = this.create_doc_fragment(build_html23());
  const anchor = frag.firstElementChild;
  post_process22.call(this, env, anchor, opts);
  return anchor;
}
function post_process22(env, container, opts = {}) {
  const icon_slot = container?.querySelector?.(".smart-env-status-icon");
  const status_indicator = container?.querySelector?.(".smart-env-status-indicator");
  const status_msg = container?.querySelector?.(".smart-env-status-msg");
  const pause_embedding = () => {
    return env?.smart_sources?.entities_vector_adapter?.halt_embed_queue_processing?.();
  };
  const resume_embedding = () => {
    return env?.smart_sources?.entities_vector_adapter?.resume_embed_queue_processing?.();
  };
  const set_status_message = (message) => {
    if (!status_msg) return;
    if (typeof status_msg.setText === "function") {
      status_msg.setText(message);
      return;
    }
    status_msg.textContent = message;
  };
  const open_notifications_feed3 = (event) => {
    event.preventDefault?.();
    event.stopPropagation?.();
    env.open_notifications_feed_modal?.();
  };
  const update_indicator = (status_state) => {
    if (!status_indicator) return;
    const {
      indicator_count,
      indicator_level
    } = status_state;
    status_indicator.dataset.level = indicator_level || "default";
    if (indicator_count > 0) {
      status_indicator.dataset.count = String(indicator_count);
    } else {
      status_indicator.removeAttribute("data-count");
    }
    const indicator_title = indicator_count > 0 ? `${indicator_count} unseen notification${indicator_count === 1 ? "" : "s"}` : "Open notifications feed";
    status_indicator.setAttribute("title", indicator_title);
  };
  const action_handlers = {
    pause_embed(event) {
      event.preventDefault?.();
      event.stopPropagation?.();
      pause_embedding();
    },
    resume_embed(event) {
      event.preventDefault?.();
      event.stopPropagation?.();
      resume_embedding();
    },
    run_reimport(event) {
      event.preventDefault?.();
      event.stopPropagation?.();
      set_status_message("Re-importing\u2026");
      env.run_re_import?.();
    },
    noop() {
    },
    context_menu(event) {
      const context_event = new MouseEvent("contextmenu", {
        bubbles: true,
        cancelable: true,
        clientX: event?.clientX || 0,
        clientY: event?.clientY || 0
      });
      container.dispatchEvent?.(context_event);
    }
  };
  const render_status_elm = () => {
    const status_state = get_status_bar_state(env);
    const {
      message,
      title
    } = status_state;
    if (icon_slot) {
      (0, import_obsidian33.setIcon)(icon_slot, "smart-connections");
    }
    update_indicator(status_state);
    set_status_message(message);
    container.setAttribute?.("title", title);
    container.removeAttribute?.("href");
    container.removeAttribute?.("target");
  };
  const run_container_action = (event) => {
    const status_state = get_status_bar_state(env);
    const action_key = Object.prototype.hasOwnProperty.call(action_handlers, status_state.click_action) ? status_state.click_action : "context_menu";
    action_handlers[action_key](event);
  };
  register_status_bar_context_menu(env, container);
  let progress_poll_interval = null;
  const set_polling = (active) => {
    if (active) {
      if (progress_poll_interval) return;
      progress_poll_interval = setInterval(() => {
        refresh_status_bar();
      }, 1e3);
      return;
    }
    if (!progress_poll_interval) return;
    clearInterval(progress_poll_interval);
    progress_poll_interval = null;
  };
  const refresh_status_bar = () => {
    render_status_elm();
    set_polling(should_poll_env_activity(env));
  };
  refresh_status_bar();
  bind_once(status_indicator, "_click_handler", "click", open_notifications_feed3);
  bind_once(status_indicator, "_keydown_handler", "keydown", (event) => {
    if (event.key !== "Enter" && event.key !== " ") return;
    open_notifications_feed3(event);
  });
  bind_once(container, "_click_handler", "click", (event) => {
    run_container_action(event);
  });
  bind_once(container, "_keydown_handler", "keydown", (event) => {
    if (event.key !== "Enter" && event.key !== " ") return;
    event.preventDefault();
    run_container_action(event);
  });
  let debounce_timeout = null;
  const debounce_refresh_status_bar = () => {
    if (debounce_timeout) clearTimeout(debounce_timeout);
    debounce_timeout = setTimeout(() => {
      refresh_status_bar();
      debounce_timeout = null;
    }, 100);
  };
  const disposers = [];
  disposers.push(env.events.on("*", debounce_refresh_status_bar));
  disposers.push(() => {
    set_polling(false);
    if (debounce_timeout) clearTimeout(debounce_timeout);
  });
  this.attach_disposer(container, disposers);
}
function bind_once(element, handler_key, event_name, handler) {
  if (!element || typeof handler !== "function") return;
  if (element[handler_key]) return;
  element[handler_key] = handler;
  element.addEventListener(event_name, handler);
}

// node_modules/obsidian-smart-env/src/components/suggest_display_right.css
var suggest_display_right_default = ".sc-modal-suggestion-right {\r\n  margin-left: auto;\r\n  text-align: right;\r\n  white-space: nowrap;\r\n  font-size: var(--font-ui-smaller);\r\n  color: var(--text-muted);\r\n  font-variant-numeric: tabular-nums;\r\n  float: right;\r\n}";

// node_modules/obsidian-smart-env/src/components/suggest_display_right.js
function build_html24(display_right, params = {}) {
  return `<span class="sc-modal-suggestion-right" data-sc-display-right="true">${display_right}</span>`;
}
function render26(display_right, params = {}) {
  this.apply_style_sheet(suggest_display_right_default);
  const frag = this.create_doc_fragment(build_html24(display_right, params));
  const container = frag.firstElementChild;
  return container;
}

// node_modules/obsidian-smart-env/src/components/supporter_callout.js
var import_obsidian34 = require("obsidian");
function build_html25(plugin, opts = {}) {
  const { plugin_name = plugin.manifest.name } = opts;
  return `<div class="wrapper">
    <div id="footer-callout" data-callout-metadata="" data-callout-fold="" data-callout="info" class="callout" style="mix-blend-mode: unset;">
      <div class="callout-title" style="align-items: center;">
        <div class="callout-icon">
          <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"
            viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
            stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-info">
            <circle cx="12" cy="12" r="10"></circle>
            <path d="M12 16v-4"></path>
            <path d="M12 8h.01"></path>
          </svg>
        </div>
        <div class="callout-title-inner"><strong>Become a Supporter</strong></div>
      </div>
      <div class="callout-content">
        <p>Try early &amp; experimental features:
          <ul>
            <li><b>Smart Connections Early Release:</b>
              <ul>
                <li>Inline block connections</li>
                <li>Footer connections view</li>
                <li>Connections re-ranking</li>
              </ul>
            </li>
            <li><b>Smart Context Early Release:</b>
              <ul>
                <li>Named contexts</li>
                <li>External sources: include code from external repositories</li>
                <li>Context codeblocks: embed context in notes ("My most valuable workflow" - \u{1F334} Brian)</li>
              </ul>
            </li>
            <li><b>Smart Editor:</b>
              <ul>
                <li>Generate &amp; review changes</li>
              </ul>
            </li>
            <li><em>Be the first to know what's coming next!</em></li>
          </ul>
        </p>
        <p>Access the Supporter Community Campfire Chat:
          <ul>
            <li>Supporter-only private discussions</li>
            <li>Share workflows</li>
            <li>Get priority help &amp; support</li>
          </ul>
        </p>
        <p>Guaranteed seat in the Community Lean Coffee meetings.</p>
        <p><i>Your support shapes the future of ${plugin_name}.</i></p>
        <p>
          <strong>Fuel the circle of empowerment.</strong> <a href="https://smartconnections.app/community-supporters?utm_source=obsidian-${plugin_name.replace(/[^a-zA-Z0-9]/g, "-").toLowerCase()}" class="button" target="_external">Become a Supporter</a>
        </p>
      </div>
    </div>
  </div>`;
}
function render27(plugin, opts = {}) {
  const html = build_html25.call(this, plugin, opts);
  const frag = this.create_doc_fragment(html);
  const container = frag.querySelector(".wrapper");
  post_process23.call(this, plugin, container, opts);
  return container;
}
async function post_process23(plugin, container) {
  const icon_container = container.querySelector(".callout-icon");
  const icon = (0, import_obsidian34.getIcon)("hand-heart");
  if (icon) {
    this.empty(icon_container);
    icon_container.appendChild(icon);
  }
  const oauth_storage_prefix = plugin.app.vault.getName().toLowerCase().replace(/[^a-z0-9]/g, "_") + "_smart_plugins_oauth_";
  const is_logged_in = !!localStorage.getItem(oauth_storage_prefix + "token");
  if (is_logged_in) container.querySelector("#footer-callout").style.display = "none";
  await this.render_setting_components(container, { scope: plugin.env });
}

// node_modules/obsidian-smart-env/src/components/user_agreement_callout.js
var import_obsidian35 = require("obsidian");
function build_html26(plugin, opts = {}) {
  const { plugin_name = plugin.manifest.name } = opts;
  return `<div class="wrapper">
    <div id="footer-callout" data-callout-metadata="" data-callout-fold="" data-callout="info" class="callout" style="mix-blend-mode: unset;">
      <div class="callout-title" style="align-items: center;">
        <div class="callout-icon">
          <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"
            viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
            stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-info">
            <circle cx="12" cy="12" r="10"></circle>
            <path d="M12 16v-4"></path>
            <path d="M12 8h.01"></path>
          </svg>
        </div>
        <div class="callout-title-inner"><strong>User Agreement</strong></div>
      </div>
      <div class="callout-content">
        <p>By using ${plugin_name} you agree to share how it helps you with at least one other person \u{1F60A}\u{1F334}</p>
      </div>
    </div>
  </div>`;
}
function render28(plugin, opts = {}) {
  const html = build_html26.call(this, plugin, opts);
  const frag = this.create_doc_fragment(html);
  const callout = frag.querySelector("#footer-callout");
  const icon_container = callout.querySelector(".callout-icon");
  const icon = (0, import_obsidian35.getIcon)("smart-connections");
  if (icon) {
    this.empty(icon_container);
    icon_container.appendChild(icon);
  }
  post_process24.call(this, plugin, callout, opts);
  return callout;
}
function post_process24(plugin, callout) {
}

// node_modules/obsidian-smart-env/src/utils/smart-context/format_stats_message.js
function format_stats_message(stats = {}) {
  const item_count = Number.isFinite(stats.item_count) ? stats.item_count : 0;
  const char_count = Number.isFinite(stats.char_count) ? stats.char_count : 0;
  const segments = [];
  segments.push(`${item_count} file(s)`);
  segments.push(`${format_char_count(char_count)} chars`);
  if (Number.isFinite(stats.max_depth)) {
    segments.push(`depth\u2264${stats.max_depth}`);
  }
  const excluded_total = sum_exclusions(stats.exclusions);
  if (excluded_total > 0) {
    segments.push(`${excluded_total} section(s) excluded`);
  }
  return `Copied to clipboard! (${segments.join(", ")})`;
}
function format_char_count(char_count) {
  if (!Number.isFinite(char_count)) return "0";
  if (char_count >= 1e5) {
    return `~${Math.round(char_count / 1e3)}k`;
  }
  return char_count.toLocaleString();
}
function sum_exclusions(exclusions) {
  if (!exclusions) return 0;
  return Object.values(exclusions).reduce((total, value) => {
    const numeric = Number.isFinite(value) ? value : 0;
    return total + numeric;
  }, 0);
}

// node_modules/obsidian-smart-env/src/actions/context/copy_to_clipboard.js
async function copy_to_clipboard2(params = {}) {
  const context_items = this.context_items.filter(params.filter);
  if (!context_items.length) {
    this.emit_event("context:copy_empty", {
      level: "warning",
      message: "No context items to copy.",
      event_source: "context_actions.copy_to_clipboard"
    });
    return false;
  }
  const content = await this.get_text(params);
  const copied = await copy_to_clipboard(content, {
    env: this.env,
    event_source: "context_actions.copy_to_clipboard.base_copy",
    success_event_key: "context:clipboard_raw_copied",
    error_event_key: "context:clipboard_raw_copy_failed",
    unavailable_event_key: "context:clipboard_copy_unavailable"
  });
  if (!copied) return false;
  const message = format_stats_message({
    item_count: context_items.length,
    char_count: content.length,
    max_depth: params.max_depth,
    exclusions: params.exclusions
  });
  this.emit_event("context:copied", {
    level: "info",
    message,
    event_source: "context_actions.copy_to_clipboard"
  });
  return true;
}

// node_modules/obsidian-smart-env/src/utils/smart-context/template_presets.js
var DEFAULT_TEMPLATE_PRESET = "xml_structured";
var template_presets = {
  xml_structured: {
    label: "XML-style (default)",
    context_template_before: "<context>\n{{FILE_TREE}}",
    context_template_after: "</context>",
    item_template_before: '<item loc="{{KEY}}" at="{{TIME_AGO}}" depth="{{LINK_DEPTH}}">',
    item_template_after: "</item>"
  },
  markdown_headings: {
    label: "Markdown headings",
    context_template_before: "{{FILE_TREE}}",
    context_template_after: "",
    item_template_before: [
      "## {{KEY}}",
      "Updated: {{TIME_AGO}} | Depth: {{LINK_DEPTH}}",
      "````{{EXT}}"
    ].join("\n"),
    item_template_after: "````\n"
  },
  json_structured: {
    label: "JSON structured",
    context_template_before: '{\n  "context": {',
    context_template_after: "  }\n}",
    item_template_before: '    "{{KEY}}": { "name": "{{ITEM_NAME}}", "updated": "{{TIME_AGO}}", "depth": {{LINK_DEPTH}}, "content": ',
    item_template_after: "    },",
    json_stringify: true
  },
  // PRO
  custom: {
    label: "Custom (PRO)"
  }
};
var get_preset_key = (settings = {}) => {
  const preset_key = settings.template_preset || DEFAULT_TEMPLATE_PRESET;
  if (template_presets[preset_key]) return preset_key;
  return "custom";
};
var get_template_value = (settings, defaults, preset_field_key, settings_field_key) => {
  const preset_key = get_preset_key(settings);
  const preset = template_presets[preset_key];
  const value_from_settings = settings?.[settings_field_key];
  if (preset_key !== "custom" && preset && typeof preset[preset_field_key] === "string") {
    return preset[preset_field_key];
  }
  if (preset_key === "custom" && typeof value_from_settings === "string") {
    return value_from_settings;
  }
  return defaults?.[settings_field_key];
};
function get_template_preset_options() {
  return Object.entries(template_presets).map(([value, config]) => ({
    value,
    label: config.label || value
  }));
}
function get_context_templates(settings = {}, defaults = {}) {
  return {
    template_before: get_template_value(settings, defaults, "context_template_before", "template_before"),
    template_after: get_template_value(settings, defaults, "context_template_after", "template_after")
  };
}
function get_item_templates(settings = {}, defaults = {}) {
  const preset_key = get_preset_key(settings);
  const preset = template_presets[preset_key];
  const include_json_stringify = preset_key === "custom" && typeof settings.json_stringify === "boolean";
  return {
    ...preset && typeof preset === "object" ? preset : {},
    // include all preset fields
    ...include_json_stringify ? { json_stringify: settings.json_stringify } : {},
    template_before: get_template_value(settings, defaults, "item_template_before", "template_before"),
    template_after: get_template_value(settings, defaults, "item_template_after", "template_after")
  };
}

// node_modules/obsidian-smart-env/src/actions/context-item/merge_template.js
var derive_item_name_from_key = (key = "") => {
  if (typeof key !== "string" || key.trim().length === 0) return "";
  const [filename_with_fragment] = key.split(/[\\/]/).slice(-1);
  const [source_name, ...block_parts] = (filename_with_fragment || "").split("#");
  const src_no_ext = source_name.includes(".") ? source_name.slice(0, source_name.lastIndexOf(".")) : source_name;
  if (block_parts.length > 0) {
    return `${src_no_ext}#${block_parts.join("#")}`;
  }
  return src_no_ext;
};
var get_item_name = (context_item) => {
  return derive_item_name_from_key(context_item.key);
};
async function merge_template(item_text, params = {}) {
  const MERGE_VARS = {
    "KEY": this.key,
    "ITEM_NAME": get_item_name(this),
    "TIME_AGO": convert_to_time_ago(this.mtime) || "Missing",
    "LINK_DEPTH": this.data.d || "0",
    "EXT": this.item_ref?.file_type || ""
  };
  const replace_vars = async (template) => {
    const re_var = /{{([\w_]+)}}/g;
    const number_of_var_matches = (template.match(re_var) || []).length;
    for (let i = 0; i < number_of_var_matches; i++) {
      template = template.replace(/{{(\w+)}}/g, (match, p1) => {
        return MERGE_VARS[p1] || "";
      });
    }
    return template;
  };
  const templates = get_item_templates(this.settings, default_settings2);
  if (params.json_stringify || templates.json_stringify) {
    item_text = JSON.stringify(item_text);
  }
  const before = await replace_vars(templates.template_before);
  const after = await replace_vars(templates.template_after);
  return ["", before, item_text, after, ""].join("\n");
}
var settings_config7 = {
  template_preset: {
    group: "Item templates",
    type: "dropdown",
    name: "Select template",
    description: "Wraps each context item with a pre-configured template.",
    options_callback: () => get_template_preset_options(),
    callback(template_value) {
      const is_pro = this?.env?.is_pro;
      if (!is_pro) return;
      if (template_value !== "custom") return;
      this.emit_event("context_item:custom_template_set");
    }
  },
  template_before: {
    group: "Item templates",
    type: "textarea",
    name: "Template Before",
    description: "Template to wrap before the context item content.",
    scope_class: "pro-setting"
  },
  template_after: {
    group: "Item templates",
    type: "textarea",
    name: "Template After",
    description: "Template to wrap after the context item content.",
    scope_class: "pro-setting"
  },
  item_explanation: {
    type: "html",
    group: "Item templates",
    value: `
        <b>Available variables:</b>
        <ul>
          <li><code>{{KEY}}</code> - Full path of the item</li>
          <li><code>{{ITEM_NAME}}</code> - Source file or block name without folder path or file extension</li>
          <li><code>{{TIME_AGO}}</code> - Time since the item was last modified</li>
          <li><code>{{LINK_DEPTH}}</code> - Depth level of the item</li>
          <li><code>{{EXT}}</code> - File extension of the item</li>
        </ul>
    `
  },
  json_stringify: {
    group: "Item templates",
    type: "toggle",
    name: "JSON Stringify",
    description: "Convert the item content to a JSON string (forces full content into single line in quotes).",
    scope_class: "pro-setting"
  }
};
var default_settings2 = {
  template_preset: "xml_structured",
  template_before: '<item loc="{{KEY}}" at="{{TIME_AGO}}">',
  template_after: "</item>"
};

// node_modules/obsidian-smart-env/node_modules/smart-utils/file_tree.js
function build_file_tree_string(paths = []) {
  if (!Array.isArray(paths) || paths.length === 0) return "";
  const root = {};
  for (const path of paths) {
    const isFolder = is_folder_path(path);
    const parts = path.split("/").filter(Boolean);
    let node = root;
    for (let i = 0; i < parts.length; i++) {
      const part = parts[i];
      const isLast = i === parts.length - 1;
      if (isLast) {
        if (isFolder) {
          node[part] = node[part] ?? { __isExplicitFolder: true };
        } else {
          node[part] = null;
        }
      } else {
        node = node[part] ??= {};
      }
    }
  }
  compress_single_child_dirs(root);
  return build_tree_string(root).trimEnd();
}
function is_folder_path(path) {
  return typeof path === "string" && path.endsWith("/");
}
function compress_single_child_dirs(node) {
  if (!node || typeof node !== "object") return;
  for (const key of Object.keys(node)) {
    const child = node[key];
    if (child && typeof child === "object") {
      if (child.__isExplicitFolder) {
        delete child.__isExplicitFolder;
        compress_single_child_dirs(child);
        continue;
      }
      const childKeys = Object.keys(child);
      if (childKeys.length === 1 && child[childKeys[0]] !== null && !child[childKeys[0]].__isExplicitFolder) {
        const mergedKey = `${key}/${childKeys[0]}`;
        node[mergedKey] = child[childKeys[0]];
        delete node[key];
        compress_single_child_dirs(node[mergedKey]);
      } else {
        compress_single_child_dirs(child);
      }
    }
  }
}
function build_tree_string(node, prefix = "") {
  let output = "";
  const entries = Object.entries(node).sort((a, b) => {
    const aIsDir = a[1] !== null;
    const bIsDir = b[1] !== null;
    if (aIsDir && !bIsDir) return -1;
    if (!aIsDir && bIsDir) return 1;
    return a[0].localeCompare(b[0]);
  });
  entries.forEach(([name, child], idx) => {
    const isLast = idx === entries.length - 1;
    const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
    if (child === null) {
      output += `${prefix}${connector}${name}
`;
    } else {
      output += `${prefix}${connector}${name}/
`;
      output += build_tree_string(child, prefix + (isLast ? "    " : "\u2502   "));
    }
  });
  return output;
}

// node_modules/obsidian-smart-env/src/actions/context/merge_template.js
async function merge_template2(context_items_text, params = {}) {
  const context_items = params.context_items || [];
  const MERGE_VARS = {
    "FILE_TREE": () => {
      return build_file_tree_string(context_items.map((c) => c.key));
    }
  };
  const replace_vars = async (template) => {
    const number_of_var_matches = (template.match(/{{(\w+)}}/g) || []).length;
    for (let i = 0; i < number_of_var_matches; i++) {
      template = template.replace(/{{(\w+)}}/gi, (match, p1) => {
        return MERGE_VARS[p1]?.() || "";
      });
    }
    return template;
  };
  const templates = get_context_templates(this.settings, default_settings3);
  const before = await replace_vars(templates.template_before);
  const after = await replace_vars(templates.template_after);
  return [before, context_items_text, after].join("\n");
}
var settings_config8 = {
  template_preset: {
    type: "dropdown",
    group: "Context templates",
    name: "Select template",
    description: "Wraps the full context with a pre-configured template.",
    options_callback: () => get_template_preset_options(),
    callback(template_value) {
      const is_pro = this?.env?.is_pro;
      if (!is_pro) return;
      if (template_value !== "custom") return;
      this.emit_event("context:custom_template_set");
    }
  },
  template_before: {
    type: "textarea",
    group: "Context templates",
    name: "Template Before",
    description: "Template to wrap before the context.",
    scope_class: "pro-setting"
  },
  template_after: {
    type: "textarea",
    group: "Context templates",
    name: "Template After",
    description: "Template to wrap after the context.",
    scope_class: "pro-setting"
  },
  context_explanation: {
    type: "html",
    group: "Context templates",
    value: `<b>Available variables:</b>
      <ul>
        <li><code>{{FILE_TREE}}</code> - Shows hierarchical view of all files</li>
      </ul>
    `
  }
};
var default_settings3 = {
  template_preset: "xml_structured",
  template_before: "<context>\n{{FILE_TREE}}",
  template_after: "</context>"
};

// node_modules/obsidian-smart-env/src/actions/context-suggest/blocks.js
function context_suggest_blocks(params = {}) {
  params?.modal?.setInstructions([
    { command: "Enter", purpose: "Add block to context" },
    { command: "\u2190", purpose: "Back to sources" }
  ]);
  let blocks = [];
  if (params.source_key) {
    const src = this.env.smart_sources.get(params.source_key);
    blocks = src.blocks;
  } else {
    blocks = Object.values(this.env.smart_blocks.items);
  }
  return blocks.sort((a, b) => {
    const a_line = Array.isArray(a.lines) && a.lines.length ? a.lines[0] : Infinity;
    const b_line = Array.isArray(b.lines) && b.lines.length ? b.lines[0] : Infinity;
    return a_line - b_line;
  }).map((block) => ({
    key: block.key,
    display: get_block_display_name2(block, { show_full_path: false }),
    select_action: () => {
      this.add_item(block.key);
    },
    arrow_left_action: ({ modal }) => {
      modal.update_suggestions("context_suggest_sources");
    }
  }));
}
var display_name = "Add blocks";

// node_modules/obsidian-smart-env/src/actions/context-suggest/contexts.js
var import_obsidian36 = require("obsidian");
var display_name2 = "Add named contexts";
var MOD_CHAR = import_obsidian36.Platform.isMacOS ? "\u2318" : "Ctrl";
function set_named_context_list_instructions(modal) {
  modal?.setInstructions?.([
    { command: "Enter / \u2192", purpose: "Browse context items" },
    { command: `${MOD_CHAR} + Enter`, purpose: "Add all items from context" }
  ]);
}
function set_named_context_item_instructions(modal, params = {}) {
  const context_name = params.context_name;
  modal?.setInstructions?.([
    { command: "Enter", purpose: `Add item from ${context_name || "context"}` },
    { command: "\u2190", purpose: "Back to named contexts" }
  ]);
}
function list_context_items2(env) {
  const collection = env?.smart_contexts;
  const items = collection?.items;
  if (!items || typeof items !== "object") return [];
  return Object.values(items).filter(Boolean);
}
function get_items_from_context(other_ctx) {
  const data = other_ctx?.data?.context_items || {};
  const entries = Object.entries(data);
  const out = [];
  for (let i = 0; i < entries.length; i += 1) {
    const [key, item_data] = entries[i];
    if (!key) continue;
    if (item_data?.exclude) continue;
    out.push({ key });
  }
  return out;
}
function build_named_context_item_payloads(ctx, params = {}) {
  const other_ctx = params.other_ctx;
  const context_name = params.context_name;
  const include_named_context = Boolean(params.include_named_context);
  const items = get_items_from_context(other_ctx);
  for (let i = 0; i < items.length; i += 1) {
    const item = items[i];
    if (!item || typeof item.key !== "string") continue;
    if (!include_named_context) continue;
    item.from_named_context = context_name;
  }
  return items;
}
function format_depth_label(depth) {
  if (!Number.isFinite(depth)) return "";
  return `depth ${depth}`;
}
function build_named_context_item_suggestions(ctx, params = {}) {
  const payloads = build_named_context_item_payloads(ctx, {
    ...params,
    include_named_context: false
  });
  set_named_context_item_instructions(params?.modal, { context_name: params.context_name });
  return payloads.filter((payload) => typeof payload?.key === "string" && payload.key.length).map((payload) => ({
    key: payload.key,
    display: payload.key,
    display_right: format_depth_label(payload.d),
    select_action: ({ modal } = {}) => {
      ctx.add_item(payload);
    },
    arrow_left_action: ({ modal } = {}) => {
      return context_suggest_contexts.call(ctx, { modal });
    }
  }));
}
async function context_suggest_contexts(params = {}) {
  const ctx = this;
  const env = ctx?.env;
  const modal = params?.modal;
  set_named_context_list_instructions(modal);
  const contexts = list_context_items2(env).filter((context_item) => {
    const name = context_item?.data?.name;
    return typeof name === "string" && name.trim().length > 0;
  }).sort((a, b) => {
    const name_a = String(a.data.name).trim().toLowerCase();
    const name_b = String(b.data.name).trim().toLowerCase();
    return name_a.localeCompare(name_b);
  });
  if (!contexts.length) {
    return [{ key: "contexts:none", display: "No named contexts found" }];
  }
  const suggestions = [];
  for (let i = 0; i < contexts.length; i += 1) {
    const other = contexts[i];
    const other_key = other?.key || other?.data?.key;
    const other_name = String(other?.data?.name || "").trim();
    const already_included = Boolean(
      ctx?.data?.context_items && Object.values(ctx.data.context_items).some(
        (item) => item?.from_named_context === other_name || item?.key === other_key
      )
    );
    if (already_included) continue;
    const item_count = other?.item_count || Object.keys(other?.data?.context_items || {}).length;
    suggestions.push({
      key: `named_context:${other_key}`,
      display: `${other_name} (${item_count})`,
      item: other,
      select_action: ({ modal: modal2 }) => {
        return [
          {
            key: other_name,
            display: `Add all: ${other_name} (${item_count})`,
            item: other,
            select_action: ({ modal: modal3 } = {}) => {
              ctx.add_item({
                key: `${other_name}`,
                named_context: true
              });
              return context_suggest_contexts.call(ctx, { modal: modal3 });
            },
            arrow_left_action: ({ modal: modal3 } = {}) => {
              return context_suggest_contexts.call(ctx, { modal: modal3 });
            }
          },
          ...build_named_context_item_suggestions(ctx, {
            other_ctx: other,
            context_name: other_name,
            modal: modal2
          })
        ];
      },
      arrow_right_action: ({ modal: modal2 }) => {
        return build_named_context_item_suggestions(ctx, {
          other_ctx: other,
          context_name: other_name,
          modal: modal2
        });
      },
      mod_select_action: ({ modal: modal2 } = {}) => {
        ctx.add_item({
          key: `${other_name}`,
          named_context: true
        });
        return context_suggest_contexts.call(ctx, { modal: modal2 });
      }
    });
  }
  return suggestions;
}

// node_modules/obsidian-smart-env/src/actions/context-suggest/sources.js
var import_obsidian37 = require("obsidian");

// node_modules/obsidian-smart-env/src/utils/smart-context/source_folder_utils.js
function normalize_folder_path(folder_path = "") {
  return String(folder_path ?? "").trim().replace(/\\+/g, "/").replace(/\/+$/g, "");
}
function reset_modal_input(modal) {
  if (!modal?.inputEl) return;
  modal.last_input_value = modal.inputEl.value;
  modal.inputEl.value = "";
}
function get_sources_list(ctx, params = {}) {
  const folder_path = params?.folder_path || "";
  const normalized_folder_path = normalize_folder_path(folder_path);
  if (!normalized_folder_path) {
    return Object.values(ctx?.env?.smart_sources?.items || {});
  }
  const starts_with_folder_path = `${normalized_folder_path}/`;
  return ctx?.env?.smart_sources?.filter((item) => {
    if (item.key === normalized_folder_path) return true;
    return item.key.startsWith(starts_with_folder_path);
  });
}

// node_modules/obsidian-smart-env/src/actions/context-suggest/sources.js
var MOD_CHAR2 = import_obsidian37.Platform.isMacOS ? "\u2318" : "Ctrl";
function build_source_suggestions(ctx, sources) {
  return sources.map((source) => ({
    key: source.key,
    display: source.key,
    select_action: () => {
      ctx.add_item(source.key);
    },
    mod_select_action: ({ modal } = {}) => {
      reset_modal_input(modal);
      return context_suggest_blocks.call(ctx, { source_key: source.key, modal });
    },
    arrow_right_action: ({ modal } = {}) => {
      reset_modal_input(modal);
      return context_suggest_blocks.call(ctx, { source_key: source.key, modal });
    }
  }));
}
function context_suggest_sources(params = {}) {
  const modal = params?.modal;
  if (modal) {
    modal.setInstructions([
      { command: "Enter", purpose: "Add source to context" },
      { command: `${MOD_CHAR2} + Enter / \u2192`, purpose: "Suggest source blocks" }
    ]);
  }
  const sources = get_sources_list(this, { folder_path: params?.folder_path || "" });
  return build_source_suggestions(this, sources);
}
var display_name3 = "Add sources";

// node_modules/obsidian-smart-env/src/actions/lookup-list/pre_process.js
async function pre_process(params) {
  const query = params.query;
  if (!query || typeof query !== "string" || query.trim().length === 0) {
    throw new Error("Invalid or empty query provided to lookup list.");
  }
  const embed_model = this.env.smart_sources.embed_model;
  if (!embed_model) {
    throw new Error("No embed model available in environment for lookup list.");
  }
  const embedding = await embed_model.embed(query);
  params.to_item = { ...embedding };
  if (!params.score_algo_key) params.score_algo_key = "similarity";
  return params;
}

// node_modules/obsidian-smart-env/src/actions/similarity.js
function similarity(params) {
  if (!this.vec) return { score: null, error: `Missing this.vec for ${this.key}` };
  if (!params.to_item?.vec) return { score: null, error: "Missing params.to_item.vec" };
  return {
    score: cos_sim(this.vec || [], params.to_item.vec || [])
  };
}
similarity.action_type = "score";
var display_name4 = "Cosine Similarity";
var display_description = "Ranks by cosine similarity between the current note and candidates.";
var settings_config9 = {
  similarity_algo_description: {
    group: "Score algorithm",
    type: "html",
    name: `${display_name4} algorithm`,
    value: `${display_description}`
  }
};

// node_modules/obsidian-smart-env/src/utils/open_source.js
var import_obsidian38 = require("obsidian");
async function open_source(item, event = null) {
  try {
    const env = item.env;
    const obsidian_app = env.obsidian_app;
    let target_path = item.key;
    if (target_path.endsWith("#")) target_path = target_path.slice(0, -1);
    let target_file;
    if (target_path.includes("#")) {
      const [file_path] = target_path.split("#");
      target_file = obsidian_app.metadataCache.getFirstLinkpathDest(file_path, "");
    } else {
      target_file = obsidian_app.metadataCache.getFirstLinkpathDest(target_path, "");
    }
    if (!target_file) {
      const message = `Unable to resolve file for ${target_path}`;
      console.warn(`[open_source] ${message}`);
      item.emit_event("sources:open_failed", {
        level: "warning",
        message,
        event_source: "open_source"
      });
      return;
    }
    let leaf;
    if (event) {
      const is_mod = import_obsidian38.Keymap.isModEvent(event);
      const is_alt = import_obsidian38.Keymap.isModifier(event, "Alt");
      if (is_mod && is_alt) {
        leaf = obsidian_app.workspace.splitActiveLeaf("vertical");
      } else if (is_mod) {
        leaf = obsidian_app.workspace.getLeaf(true);
      } else {
        leaf = obsidian_app.workspace.getMostRecentLeaf();
      }
    } else {
      leaf = obsidian_app.workspace.getMostRecentLeaf();
    }
    await leaf.openFile(target_file);
    if (typeof item?.line_start === "number") {
      const { editor } = leaf.view;
      const pos = { line: item.line_start, ch: 0 };
      editor.setCursor(pos);
      editor.scrollIntoView({ to: pos, from: pos }, true);
    }
    item.emit_event("sources:opened", { event_source: "open_source" });
  } catch (error) {
    console.error("Error in open_source:", error);
    item.emit_event("sources:open_failed", {
      level: "error",
      message: error?.message || "Failed to open source.",
      details: error?.stack || "",
      event_source: "open_source"
    });
  }
}

// node_modules/obsidian-smart-env/src/actions/source/open.js
async function source_open(event = null) {
  await open_source(this, event);
}

// node_modules/obsidian-smart-env/smart_env.config.js
var smart_env_config = {
  collections: {
    embedding_models: embedding_models_default2,
    event_logs: event_logs_default2,
    lookup_lists: lookup_lists_default,
    smart_blocks: smart_blocks_default,
    smart_contexts: smart_contexts_default3
  },
  items: {
    embedding_model: { class: EmbeddingModel, version: "1.0.0" },
    lookup_list: { class: LookupList, version: "1.0.0" },
    smart_block: { class: SmartBlock2, version: "1.0.0" },
    smart_context: { class: SmartContext2, version: "1.0.0" }
  },
  modules: {},
  components: {
    collection_settings: { render, version: "1.0.0" },
    context_item_leaf: { render: render2, version: "1.0.0" },
    default_notification: { render: render3, version: "1.0.0" },
    env_stats: { render: render4, version: "1.0.0" },
    env_status: { render: render5, version: "1.0.0" },
    lean_coffee_callout: { render: render6, version: "1.0.0" },
    milestone_notification: { render: render7, version: "1.0.0" },
    milestones: { render: render8, version: "1.0.0" },
    notifications_feed: { render: render9, version: "1.0.0" },
    settings_env_model: { render: render10, version: "1.0.0" },
    settings_env_model_type: { render: render11, version: "1.0.0" },
    settings_env_models: { render: render12, version: "1.0.0" },
    settings_env_sources: { render: render13, version: "1.0.0" },
    settings_model_actions: { render: render14, version: "1.0.0" },
    settings_smart_env: { render: render15, version: "1.0.0" },
    smart_context_actions: { render: render16, version: "1.0.0" },
    smart_context_item: { render: render17, version: "1.0.0" },
    smart_context_meta: { render: render18, version: "1.0.0" },
    smart_context_tree: { render: render19, version: "1.0.0" },
    smart_plugins_list: { render: render20, version: "1.0.0" },
    smart_plugins_list_item: { render: render21, version: "1.0.0" },
    smart_plugins_login: { render: render22, version: "1.0.0" },
    smart_plugins_referral: { render: render23, version: "1.0.0" },
    source_inspector: { render: render24, version: "1.0.0" },
    status_bar: { render: render25, version: "1.0.0" },
    suggest_display_right: { render: render26, version: "1.0.0" },
    supporter_callout: { render: render27, version: "1.0.0" },
    user_agreement_callout: { render: render28, version: "1.0.0" }
  },
  actions: {
    context_copy_to_clipboard: { action: copy_to_clipboard2, version: "1.0.0" },
    context_item_merge_template: { action: merge_template, settings_config: settings_config7, default_settings: default_settings2, version: "1.0.0" },
    context_merge_template: { action: merge_template2, settings_config: settings_config8, default_settings: default_settings3, version: "1.0.0" },
    context_suggest_blocks: { action: context_suggest_blocks, display_name, version: "1.0.0" },
    context_suggest_contexts: { action: context_suggest_contexts, display_name: display_name2, version: "1.0.0" },
    context_suggest_sources: { action: context_suggest_sources, display_name: display_name3, version: "1.0.0" },
    lookup_list_pre_process: { action: pre_process, pre_process, version: "1.0.0" },
    similarity: { action: similarity, settings_config: settings_config9, display_name: display_name4, display_description, version: "1.0.0" },
    source_open: { action: source_open, version: "1.0.0" }
  }
};

// node_modules/obsidian-smart-env/default.config.js
var smart_env_config2 = {
  env_path: "",
  modules: {
    smart_fs: {
      class: SmartFs,
      adapter: ObsidianFsAdapter
    },
    smart_view: {
      class: SmartView,
      adapter: SmartViewObsidianAdapter
    },
    smart_embed_model: {
      class: SmartEmbedModel,
      adapters: {
        transformers: SmartEmbedTransformersIframeAdapter,
        openai: SmartEmbedOpenAIAdapter,
        ollama: SmartEmbedOllamaAdapter,
        gemini: GeminiEmbedModelAdapter,
        lm_studio: LmStudioEmbedModelAdapter
      }
    },
    smart_chat_model: {
      class: SmartChatModel,
      // DEPRECATED FORMAT: will be changed (requires SmartModel adapters getters update)
      adapters: {
        anthropic: SmartChatModelAnthropicAdapter,
        azure: SmartChatModelAzureAdapter,
        custom: SmartChatModelCustomAdapter,
        google: SmartChatModelGoogleAdapter,
        gemini: SmartChatModelGeminiAdapter,
        groq: SmartChatModelGroqAdapter,
        lm_studio: SmartChatModelLmStudioAdapter,
        ollama: SmartChatModelOllamaAdapter,
        open_router: SmartChatModelOpenRouterAdapter,
        openai: SmartChatModelOpenaiAdapter,
        xai: SmartChatModelXaiAdapter,
        deepseek: SmartChatModelDeepseekAdapter
      },
      http_adapter: new SmartHttpRequest({
        adapter: SmartHttpObsidianRequestAdapter,
        obsidian_request_url: import_obsidian41.requestUrl
      })
    },
    http_adapter: {
      class: SmartHttpRequest,
      adapter: SmartHttpObsidianRequestAdapter,
      obsidian_request_url: import_obsidian41.requestUrl
    }
  },
  collections: {
    context_items: context_items_default,
    event_logs: event_logs_default2,
    smart_components: smart_components_default2,
    smart_sources: {
      collection_key: "smart_sources",
      class: SmartSources,
      data_adapter: AjsonMultiFileSourcesDataAdapter,
      source_adapters: {
        "md": ObsidianMarkdownSourceContentAdapter,
        "txt": ObsidianMarkdownSourceContentAdapter,
        "excalidraw.md": ExcalidrawSourceContentAdapter,
        "base": BasesSourceContentAdapter,
        "canvas": CanvasSourceContentAdapter,
        "rendered": RenderedSourceContentAdapter
        // "canvas": MarkdownSourceContentAdapter,
        // "default": MarkdownSourceContentAdapter,
      },
      content_parsers: [
        parse_blocks
      ],
      // process_embed_queue: false,
      process_embed_queue: true,
      // trigger embedding on load
      load_order: 100
      // load last
    }
    // smart_blocks: {
    //   collection_key: 'smart_blocks',
    //   class: SmartBlocks,
    //   data_adapter: AjsonMultiFileBlocksDataAdapter,
    //   block_adapters: {
    //     "md": MarkdownBlockContentAdapter,
    //     "txt": MarkdownBlockContentAdapter,
    //     "excalidraw.md": MarkdownBlockContentAdapter,
    //     // "canvas": MarkdownBlockContentAdapter,
    //   },
    // },
  },
  items: {
    smart_source: smart_source_default
    // smart_block,
  },
  default_settings,
  // begin obsidian-smart-env specific modules (need to update build_env_config.js to handle)
  modals: {
    context_selector: {
      class: ContextModal,
      default_suggest_action_keys: [
        "context_suggest_sources"
      ]
    },
    milestones_modal: {
      class: MilestonesModal
    },
    notifications_feed_modal: {
      class: NotificationsFeedModal
    },
    browse_plugins_modal: {
      class: BrowseSmartPlugins
    }
  }
};
merge_env_config(smart_env_config2, smart_env_config);
var default_config_default = smart_env_config2;

// node_modules/obsidian-smart-env/utils/add_icons.js
var import_obsidian42 = require("obsidian");
var svg_wrap_24 = (inner_svg) => {
  return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="100%" height="100%" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${inner_svg}</svg>`;
};
var smart_copy_note_svg = svg_wrap_24(`
  <rect x="8.76" y="6.6" width="12.96" height="15.12" rx="2.16"></rect>
  <path d="M4.44 17.4H3.36a2.16 2.16 0 0 1-2.16-2.16V4.44a2.16 2.16 0 0 1 2.16-2.16h10.8a2.16 2.16 0 0 1 2.16 2.16v2.16"></path>
  <path d="M12 10.92h6.48"></path>
  <path d="M12 14.16h6.48"></path>
  <path d="M12 17.4h4.32"></path>
`);
var smart_context_builder_svg = svg_wrap_24(`
  <rect x="2.1" y="3.2" width="13.2" height="17.6" rx="2.2"></rect>
  <path d="M5.4 7.6h6.6"></path>
  <path d="M5.4 12h6.6"></path>
  <path d="M5.4 16.4h4.4"></path>
  <path d="M19.7 9.8v4.4"></path>
  <path d="M17.5 12h4.4"></path>
`);
var smart_inline_connections_svg = svg_wrap_24(`
  <path d="M2.4 4.8h19.2"></path>
  <path d="M2.4 19.2h19.2"></path>
  <circle cx="12" cy="12" r="3.6"></circle>
  <circle cx="6" cy="12" r="1.2" fill="currentColor" stroke="none"></circle>
  <circle cx="18" cy="12" r="1.2" fill="currentColor" stroke="none"></circle>
`);
function add_smart_chat_icon() {
  (0, import_obsidian42.addIcon)("smart-chat", `<defs>
  <symbol id="smart-chat-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round">
    <path d="M2 4c0-1.1.9-2 2-2h16c1.1 0 2 .9 2 2v11c0 1.1-.9 2-2 2h-8l-5 4v-4H4c-1.1 0-2-.9-2-2Z" stroke-width="2"></path>
    <path d="M7 8c.5.3 1.3.3 1.8 0" stroke-width="2"></path>
    <path d="M15.2 8c.5.3 1.3.3 1.8 0" stroke-width="2"></path>
    <path d="M8 11.5c1 .8 2.5 1.2 4 1.2s3-.4 4-1.2" stroke-width="2"></path>
  </symbol>
</defs>
<use href="#smart-chat-icon" />`);
}
function add_smart_connections_icon() {
  (0, import_obsidian42.addIcon)("smart-connections", `<path d="M44.9,9.18 L75.51,29.59 L75.51,50 L44.9,90.82" stroke="currentColor" stroke-width="4.08" fill="none"/>
    <path d="M24.49,39.8 L50,60.2" stroke="currentColor" stroke-width="5.1" fill="none"/>
    <circle cx="44.9" cy="9.18" r="9.18" fill="currentColor"/>
    <circle cx="75.51" cy="29.59" r="9.18" fill="currentColor"/>
    <circle cx="75.51" cy="60.2" r="9.18" fill="currentColor"/>
    <circle cx="44.9" cy="90.82" r="9.18" fill="currentColor"/>
    <circle cx="24.49" cy="39.8" r="9.18" fill="currentColor"/>`);
}
function add_smart_lookup_icon() {
  (0, import_obsidian42.addIcon)("smart-lookup", `<defs>
  <clipPath id="sc-in-search-clip" clipPathUnits="userSpaceOnUse">
    <circle cx="10.9" cy="10.9" r="8.8"></circle>
  </clipPath>
  <symbol id="smart-lookup-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round">
    <g clip-path="url(#sc-in-search-clip)">
      <path d="M10.13 4.74L14.75 7.82L14.75 10.9L10.13 17.06" stroke="currentColor" stroke-width="0.56" fill="none"></path>
      <path d="M7.05 9.36L10.9 12.44" stroke="currentColor" stroke-width="0.7" fill="none"></path>
      <circle cx="10.13" cy="4.74" r="0.33" fill="currentColor"></circle>
      <circle cx="14.75" cy="7.82" r="0.33" fill="currentColor"></circle>
      <circle cx="14.75" cy="12.44" r="0.33" fill="currentColor"></circle>
      <circle cx="10.13" cy="17.06" r="0.33" fill="currentColor"></circle>
      <circle cx="7.05" cy="9.36" r="0.33" fill="currentColor"></circle>
    </g>
    <circle cx="10.9" cy="10.9" r="8.8"></circle>
    <path d="M21.9 21.9L17.17 17.17"></path>
  </symbol>
</defs>
<use href="#smart-lookup-icon" />`);
}
function add_smart_copy_context_icon() {
  (0, import_obsidian42.addIcon)("smart-copy-note", smart_copy_note_svg);
}
function add_smart_context_icon() {
  (0, import_obsidian42.addIcon)("smart-context-builder", smart_context_builder_svg);
}
function add_inline_connections_icon() {
  (0, import_obsidian42.addIcon)("smart-inline-connections", smart_inline_connections_svg);
}
var smart_footer_connections_svg = svg_wrap_24(`
  <path d="M2.72 5.04h18.56"></path>
  <path d="M2.72 12h18.56"></path>
  <circle cx="12" cy="18.96" r="3.48"></circle>
  <circle cx="6.2" cy="18.96" r="1.16" fill="currentColor" stroke="none"></circle>
  <circle cx="17.8" cy="18.96" r="1.16" fill="currentColor" stroke="none"></circle>
`);
function add_footer_connections_icon() {
  (0, import_obsidian42.addIcon)("smart-footer-connections", smart_footer_connections_svg);
}
function add_smart_dupe_detector_icon() {
  (0, import_obsidian42.addIcon)("smart-dupe-detector", `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="100%" height="100%" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <rect x="1.92" y="4.16" width="8.96" height="15.68" rx="2.24"></rect>
  <rect x="13.12" y="4.16" width="8.96" height="15.68" rx="2.24"></rect>
  <path d="M7.52 9.76H16.48"></path>
  <path d="M7.52 14.24H16.48"></path>
  <circle cx="6.4" cy="9.76" r="1.12" fill="currentColor" stroke="none"></circle>
  <circle cx="6.4" cy="14.24" r="1.12" fill="currentColor" stroke="none"></circle>
  <circle cx="17.6" cy="9.76" r="1.12" fill="currentColor" stroke="none"></circle>
  <circle cx="17.6" cy="14.24" r="1.12" fill="currentColor" stroke="none"></circle>
</svg>
`);
}
function add_smart_named_contexts_icon() {
  (0, import_obsidian42.addIcon)("smart-named-contexts", `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M19.84 22.08 12 17.6 4.16 22.08V4.16a2.24 2.24 0 0 1 2.24-2.24H17.6a2.24 2.24 0 0 1 2.24 2.24v17.92Z"></path>
  <path d="M7.52 7.52h8.96"></path>
  <path d="M7.52 10.88h8.96"></path>
  <path d="M7.52 14.24h6.72"></path>
</svg>
`);
}
var smart_graph_svg = `
  <path d="M28,22 L24,3.92" stroke="currentColor" stroke-width="1.95" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
  <path d="M28,22 L3.92,28" stroke="currentColor" stroke-width="1.95" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
  <path d="M28,22 L43,19" stroke="currentColor" stroke-width="1.95" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
  <path d="M28,22 L18,41" stroke="currentColor" stroke-width="1.95" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
  <path d="M72,29 L76,3.92" stroke="currentColor" stroke-width="1.95" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
  <path d="M72,29 L56,19" stroke="currentColor" stroke-width="1.95" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
  <path d="M72,29 L96.08,23" stroke="currentColor" stroke-width="1.95" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
  <path d="M72,29 L68,47" stroke="currentColor" stroke-width="1.95" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
  <path d="M71,72 L84,60" stroke="currentColor" stroke-width="1.95" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
  <path d="M71,72 L55,59" stroke="currentColor" stroke-width="1.95" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
  <path d="M71,72 L96.08,77" stroke="currentColor" stroke-width="1.95" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
  <path d="M71,72 L71,96.08" stroke="currentColor" stroke-width="1.95" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
  <path d="M43,19 L56,19" stroke="currentColor" stroke-width="1.95" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
  <path d="M68,47 L55,59" stroke="currentColor" stroke-width="1.95" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
  <circle cx="28" cy="22" r="5.6" fill="currentColor"/>
  <circle cx="24" cy="3.92" r="3.92" fill="currentColor"/>
  <circle cx="3.92" cy="28" r="3.92" fill="currentColor"/>
  <circle cx="43" cy="19" r="3.92" fill="currentColor"/>
  <circle cx="18" cy="41" r="3.92" fill="currentColor"/>
  <circle cx="35" cy="34" r="3.92" fill="currentColor"/>
  <circle cx="72" cy="29" r="5.6" fill="currentColor"/>
  <circle cx="76" cy="3.92" r="3.92" fill="currentColor"/>
  <circle cx="56" cy="19" r="3.92" fill="currentColor"/>
  <circle cx="96.08" cy="23" r="3.92" fill="currentColor"/>
  <circle cx="68" cy="47" r="3.92" fill="currentColor"/>
  <circle cx="86" cy="37" r="3.92" fill="currentColor"/>
  <circle cx="63" cy="33" r="3.92" fill="currentColor"/>
  <circle cx="71" cy="72" r="5.6" fill="currentColor"/>
  <circle cx="84" cy="60" r="3.92" fill="currentColor"/>
  <circle cx="55" cy="59" r="3.92" fill="currentColor"/>
  <circle cx="96.08" cy="77" r="3.92" fill="currentColor"/>
  <circle cx="71" cy="96.08" r="3.92" fill="currentColor"/>
  <circle cx="58" cy="87" r="3.92" fill="currentColor"/>
`;
function add_smart_graph_icon() {
  (0, import_obsidian42.addIcon)("smart-graph", smart_graph_svg);
}
function add_smart_icons() {
  add_smart_copy_context_icon();
  add_smart_context_icon();
  add_inline_connections_icon();
  add_footer_connections_icon();
  add_smart_dupe_detector_icon();
  add_smart_named_contexts_icon();
  add_smart_graph_icon();
}

// node_modules/obsidian-smart-env/node_modules/smart-notices/smart_notices.js
var import_obsidian43 = require("obsidian");

// node_modules/obsidian-smart-env/node_modules/smart-notices/notices.js
var NOTICES = {
  item_excluded: {
    en: "Cannot show Smart Connections for excluded entity: {{entity_key}}"
  },
  load_env: {
    en: "Mobile detected: to prevent performance issues, click to load Smart Environment when ready.",
    button: {
      en: `Load Smart Env`,
      callback: (env) => {
        env.load(true);
      }
    },
    timeout: 1e4
  },
  /** @deprecated in favor of in-component insctructions (2025-06-22) */
  missing_entity: {
    en: "No entity found for key: {{key}}"
  },
  notice_muted: {
    en: "Notice muted"
  },
  new_version_available: {
    en: "A new version is available! (v{{version}})",
    timeout: 15e3,
    button: {
      en: "Release notes",
      callback: (scope) => {
        window.open("https://github.com/brianpetro/obsidian-smart-connections/releases", "_blank");
      }
    }
  },
  new_early_access_version_available: {
    en: "A new early access version is available! (v{{version}})"
  },
  supporter_key_required: {
    en: "Supporter license key required for early access update"
  },
  revert_to_stable_release: {
    en: 'Click "Check for Updates" in the community plugins tab and complete the update for Smart Connections to finish reverting to the stable release.',
    timeout: 0
  },
  action_installed: {
    en: 'Installed action "{{name}}"'
  },
  action_install_error: {
    en: 'Error installing action "{{name}}": {{error}}',
    timeout: 0
  },
  embed_model_not_loaded: {
    en: "Embed model not loaded. Please wait for the model to load and try again."
  },
  embed_search_text_failed: {
    en: "Failed to embed search text."
  },
  error_in_embedding_search: {
    en: "Error in embedding search. See console for details."
  },
  copied_to_clipboard: {
    en: "Message: {{content}} copied successfully."
  },
  copy_failed: {
    en: "Unable to copy message to clipboard."
  },
  copied_chatgpt_url_to_clipboard: {
    en: "ChatGPT URL copied to clipboard."
  },
  loading_collection: {
    en: "Loading {{collection_key}}..."
  },
  done_loading_collection: {
    en: "{{collection_key}} loaded."
  },
  saving_collection: {
    en: "Saving {{collection_key}}..."
  },
  initial_scan: {
    en: "[{{collection_key}}] Starting initial scan...",
    timeout: 0
  },
  done_initial_scan: {
    en: "[{{collection_key}}] Initial scan complete.",
    timeout: 3e3
  },
  pruning_collection: {
    en: "Pruning {{collection_key}}..."
  },
  done_pruning_collection: {
    en: "Pruned {{count}} items from {{collection_key}}."
  },
  embedding_progress: {
    en: "Embedding progress: {{progress}} / {{total}}\n{{tokens_per_second}} tokens/sec using {{model_name}}",
    button: {
      en: "Pause",
      callback: (env) => {
        console.log("pausing");
        env.smart_sources.entities_vector_adapter.halt_embed_queue_processing();
      }
    },
    timeout: 0
  },
  embedding_complete: {
    en: "Embedding complete. {{total_embeddings}} embeddings created. {{tokens_per_second}} tokens/sec using {{model_name}}",
    timeout: 0
  },
  embedding_paused: {
    en: "Embedding paused. Progress: {{progress}} / {{total}}\n{{tokens_per_second}} tokens/sec using {{model_name}}",
    button: {
      en: "Resume",
      callback: (env) => {
        env.smart_sources.entities_vector_adapter.resume_embed_queue_processing(100);
      }
    },
    timeout: 0
  },
  embedding_error: {
    en: "Error embedding: {{error}}",
    timeout: 0
  },
  import_progress: {
    en: "Importing... {{progress}} / {{total}} sources",
    timeout: 0
  },
  done_import: {
    en: "Import complete. {{count}} sources imported in {{time_in_seconds}}s",
    timeout: 0
  },
  no_import_queue: {
    en: "No items in import queue"
  },
  clearing_all: {
    en: "Clearing all data...",
    timeout: 0
  },
  done_clearing_all: {
    en: "All data cleared and reimported",
    timeout: 3e3
  },
  image_extracting: {
    en: "Extracting text from Image(s)",
    timeout: 0
  },
  pdf_extracting: {
    en: "Extracting text from PDF(s)",
    timeout: 0
  },
  insufficient_settings: {
    en: "Insufficient settings for {{key}}, missing: {{missing}}",
    timeout: 0
  },
  unable_to_init_source: {
    en: "Unable to initialize source: {{key}}",
    timeout: 0
  },
  reload_sources: {
    en: "Reloaded sources in {{time_ms}}ms"
  }
};

// node_modules/obsidian-smart-env/node_modules/smart-notices/smart_notices.js
function define_default_create_methods(notices) {
  for (const key of Object.keys(notices)) {
    const notice_obj = notices[key];
    if (typeof notice_obj.create !== "function") {
      notice_obj.create = function(opts = {}) {
        let text = this.en ?? key;
        for (const [k, v] of Object.entries(opts)) {
          text = text.replace(new RegExp(`{{${k}}}`, "g"), String(v));
        }
        let button;
        if (!opts.button && this.button) {
          const btn_label = typeof this.button.en === "string" ? this.button.en : "OK";
          button = {
            text: btn_label,
            callback: typeof this.button.callback === "function" ? this.button.callback : () => {
            }
            // no-op
          };
        } else {
          button = opts.button;
        }
        let final_timeout = opts.timeout ?? this.timeout ?? 5e3;
        return {
          text,
          button,
          timeout: final_timeout,
          confirm: opts.confirm,
          // pass any user-provided confirm
          immutable: opts.immutable
          // pass any user-provided immutable
        };
      };
    }
  }
  return notices;
}
var SmartNotices = class {
  /**
   * @param {Object} scope - The main plugin instance
   */
  constructor(env, opts = {}) {
    env?.create_env_getter(this);
    this.active = {};
    this.adapter = opts.adapter || this.env.config.modules.smart_notices.adapter;
    define_default_create_methods(NOTICES);
  }
  /** plugin settings for notices (muted, etc.) */
  get settings() {
    if (!this.env?.settings?.smart_notices) {
      this.env.settings.smart_notices = {};
    }
    if (!this.env?.settings?.smart_notices?.muted) {
      this.env.settings.smart_notices.muted = {};
    }
    return this.env?.settings?.smart_notices;
  }
  /**
   * Displays a notice by key or custom message.
   * Usage:
   *   notices.show('load_env', { scope: this });
   *
   * @param {string} id - The notice key or custom ID
   * @param {object} opts - Additional user opts
   * @deprecated in favor of event system with levels
   */
  show(id, opts = {}) {
    let message = null;
    if (typeof opts === "string") {
      message = opts;
    } else {
      opts = opts || {};
    }
    const normalized_id = this._normalize_notice_key(id);
    if (this.settings?.muted?.[normalized_id]) {
      if (opts.confirm?.callback) {
        opts.confirm.callback();
      }
      return;
    }
    const notice_entry = NOTICES[id];
    let derived = {
      text: message || id,
      timeout: opts.timeout ?? 5e3,
      button: opts.button,
      immutable: opts.immutable,
      confirm: opts.confirm
    };
    if (notice_entry?.create) {
      const result = notice_entry.create({ ...opts });
      derived.text = message || result.text;
      derived.timeout = result.timeout;
      derived.button = result.button;
      derived.immutable = result.immutable;
      derived.confirm = result.confirm;
    }
    const content_fragment = this._build_fragment(normalized_id, derived.text, derived);
    if (this.active[normalized_id]?.noticeEl?.isConnected) {
      return this.active[normalized_id].setMessage(content_fragment, derived.timeout);
    }
    return this._render_notice(normalized_id, content_fragment, derived);
  }
  /**
   * Normalizes the notice key to a safe string.
   */
  _normalize_notice_key(key) {
    return key.replace(/[^a-zA-Z0-9_-]/g, "_");
  }
  /**
   * Creates and tracks the notice instance
   */
  _render_notice(normalized_id, content_fragment, { timeout }) {
    this.active[normalized_id] = new this.adapter(content_fragment, timeout);
    return this.active[normalized_id];
  }
  /**
   * Builds a DocumentFragment with notice text & possible buttons
   */
  _build_fragment(id, text, { button, confirm: confirm2, immutable }) {
    const frag = document.createDocumentFragment();
    frag.createEl("p", {
      cls: "sc-notice-head",
      text: `[Smart Env v${this.env.constructor.version}]`
    });
    const content = frag.createEl("p", { cls: "sc-notice-content", text });
    const actions = frag.createEl("div", { cls: "sc-notice-actions" });
    if (confirm2?.text && typeof confirm2.callback === "function") {
      this._add_button(confirm2, actions);
    }
    if (button?.text && typeof button.callback === "function") {
      this._add_button(button, actions);
    }
    if (!immutable) {
      this._add_mute_button(id, actions);
    }
    return frag;
  }
  /**
   * Creates a <button> appended to the container
   */
  _add_button(btnConfig, container) {
    const btn = document.createElement("button");
    this.env.smart_view.safe_inner_html(btn, btnConfig.text);
    btn.addEventListener("click", (e) => {
      if (btnConfig.stay_open) {
        e.preventDefault();
        e.stopPropagation();
      }
      btnConfig.callback?.(this.env);
    });
    container.appendChild(btn);
  }
  /**
   * Mute button
   */
  _add_mute_button(id, container) {
    const btn = document.createElement("button");
    (0, import_obsidian43.setIcon)(btn, "bell-off");
    btn.addEventListener("click", () => {
      if (!this.settings.muted) this.settings.muted = {};
      this.settings.muted[id] = true;
      if (NOTICES["notice muted"]) {
        this.show("notice muted", null, { timeout: 2e3 });
      }
    });
    container.appendChild(btn);
  }
  /**
   * Hides & clears all active notices
   */
  unload() {
    for (const id in this.active) {
      this.remove(id);
    }
  }
  /**
   * Removes an active notice by key
   */
  remove(id) {
    const normalized_id = this._normalize_notice_key(id);
    this.active[normalized_id]?.hide();
    delete this.active[normalized_id];
  }
};

// node_modules/obsidian-smart-env/utils/sc_oauth.js
var import_obsidian44 = require("obsidian");
var CLIENT_ID = "smart-plugins-op";
var CLIENT_SECRET = "smart-plugins-op-secret";
function set_local_storage_token({ access_token, refresh_token }, oauth_storage_prefix) {
  localStorage.setItem(oauth_storage_prefix + "token", access_token);
  if (refresh_token) {
    localStorage.setItem(oauth_storage_prefix + "refresh", refresh_token);
  }
}
async function exchange_code_for_tokens(code, plugin) {
  const oauth_storage_prefix = build_oauth_storage_prefix(plugin.app.vault.getName());
  const url = `${get_smart_server_url()}/auth/oauth_exchange2`;
  const resp = await (0, import_obsidian44.requestUrl)({
    url,
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      client_id: CLIENT_ID,
      client_secret: CLIENT_SECRET,
      code
    })
  });
  if (resp.status !== 200) {
    throw new Error(`OAuth exchange error ${resp.status} ${resp.text}`);
  }
  const { access_token, refresh_token } = resp.json;
  if (!access_token) {
    throw new Error("No access_token in response");
  }
  set_local_storage_token({ access_token, refresh_token }, oauth_storage_prefix);
}
var OAUTH_SUFFIX = "_smart_plugins_oauth_";
function build_oauth_storage_prefix(vault_name) {
  const safe_name = String(vault_name || "").toLowerCase().replace(/[^a-z0-9]/g, "_");
  return `${safe_name}${OAUTH_SUFFIX}`;
}

// node_modules/obsidian-smart-env/utils/replace_folder_tree_var.js
function replace_folder_tree_var(prompt) {
  const env = this;
  let paths = env.smart_sources?.fs?.folder_paths ?? [];
  paths = paths.map((p) => p.endsWith("/") ? p : p + "/");
  const tree = build_file_tree_string([...new Set(paths)]);
  return prompt.replace(/{{\s*folder_tree\s*}}/gi, tree);
}

// node_modules/obsidian-smart-env/utils/replace_folders_top_var.js
function replace_folders_top_var(prompt) {
  const env = this;
  let paths = env.smart_sources?.fs?.folder_paths ?? [];
  paths = paths.map((p) => (p.split("/")[0] || "") + "/");
  const tree = build_file_tree_string([...new Set(paths)]);
  return prompt.replace(/{{\s*folders_top\s*}}/gi, tree);
}

// node_modules/obsidian-smart-env/utils/replace_recent_n_var.js
function replace_recent_n_var(prompt) {
  console.log("replace_recent_n_var", prompt);
  const env = this;
  return prompt.replace(/{{\s*recent_(\d+)\s*}}/gi, (_, count) => {
    const n = parseInt(count, 10) || 0;
    const files = Object.values(env.smart_sources?.fs?.files ?? {}).sort((a, b) => b.stat.mtime - a.stat.mtime).slice(0, n).map((f) => f.path).join("\n  - ");
    console.log("replace_recent_n_var", n, files);
    return files ? `
  - ${files}` : "";
  }).trim();
}

// node_modules/obsidian-smart-env/utils/replace_vault_tags_var.js
function replace_vault_tags_var(prompt) {
  const appRef = this.app;
  const tags = appRef?.metadataCache?.getTags?.() || {};
  const vault_tags = Object.keys(tags).map((tag) => tag.replace("#", "")).join("\n  - ");
  return prompt.replace(/{{\s*(?:vault_tags|tags)\s*}}/gi, `
  - ${vault_tags}
`).trim();
}

// node_modules/obsidian-smart-env/utils/register_completion_variable_adapter_replacements.js
function register_completion_variable_adapter_replacements(variable_adapter_class) {
  variable_adapter_class.register(
    (txt) => /{{\s*folder_tree\s*}}/i.test(txt),
    replace_folder_tree_var,
    "{{ folder_tree }}"
    // Example variable
  );
  variable_adapter_class.register(
    (txt) => /{{\s*folders_top\s*}}/i.test(txt),
    replace_folders_top_var,
    "{{ folders_top }}"
    // Example variable
  );
  variable_adapter_class.register(
    (txt) => /{{\s*(?:tags|vault_tags)\s*}}/i.test(txt),
    replace_vault_tags_var,
    "{{ tags }}"
    // Example variable
  );
  variable_adapter_class.register(
    (txt) => /{{\s*recent_(\d+)\s*}}/i.test(txt),
    replace_recent_n_var,
    "{{ recent_10 }}"
    // Example variable
  );
}

// node_modules/obsidian-smart-env/migrations/remove_smart_plugins_plugin.js
async function remove_smart_plugins_plugin({ app: app2, plugin_ids = [] } = {}) {
  if (!app2) return;
  const adapter = app2.vault?.adapter;
  for (const plugin_id of plugin_ids) {
    const disabled = await disable_plugin_if_present(app2.plugins, plugin_id);
    if (disabled) console.warn(`Disabled legacy plugin: ${plugin_id}`);
    const removed = await remove_plugin_folder(adapter, plugin_id);
    if (removed) console.warn(`Removed legacy plugin: ${plugin_id}`);
  }
}
async function disable_plugin_if_present(app_plugins, plugin_id) {
  if (!app_plugins) return;
  const has_plugin = Boolean(
    app_plugins.plugins?.[plugin_id] || app_plugins.enabledPlugins?.has?.(plugin_id) || app_plugins.manifests && plugin_id in app_plugins.manifests
  );
  if (!has_plugin) return;
  if (app_plugins.plugins?.[plugin_id]) {
    await app_plugins.unloadPlugin?.(plugin_id);
  }
  if (app_plugins.disablePluginAndSave) {
    await app_plugins.disablePluginAndSave(plugin_id);
  }
  if (app_plugins.enabledPlugins?.has?.(plugin_id)) {
    app_plugins.enabledPlugins.delete(plugin_id);
  }
  if (app_plugins.manifests && plugin_id in app_plugins.manifests) {
    delete app_plugins.manifests[plugin_id];
  }
  await app_plugins.loadManifests?.();
  return true;
}
async function remove_plugin_folder(adapter, plugin_id) {
  if (!adapter?.exists) return;
  const plugin_path = `.obsidian/plugins/${plugin_id}`;
  const exists = await adapter.exists(plugin_path);
  if (!exists) return;
  if (adapter.rmdir) {
    await adapter.rmdir(plugin_path, true);
    return;
  }
  if (adapter.list && adapter.remove) {
    const stack = [plugin_path];
    while (stack.length) {
      const current_path = stack.pop();
      const listing = await adapter.list(current_path);
      for (const file of listing?.files || []) {
        await adapter.remove(`${current_path}/${file}`);
      }
      for (const folder of listing?.folders || []) {
        stack.push(`${current_path}/${folder}`);
      }
    }
    await adapter.remove(plugin_path);
    return true;
  }
}

// node_modules/obsidian-smart-env/src/views/env_status_view.js
var import_obsidian47 = require("obsidian");

// node_modules/obsidian-smart-env/views/smart_item_view.js
var import_obsidian46 = require("obsidian");

// node_modules/obsidian-smart-env/utils/wait_for_env_to_load.js
var import_obsidian45 = require("obsidian");
async function wait_for_env_to_load(scope, opts = {}) {
  const { wait_for_states = ["loaded"] } = opts;
  const container = scope.container || scope.containerEl;
  if (!wait_for_states.includes(scope.env?.state)) {
    let clicked_load_env = false;
    while (scope.env.state === "init" && import_obsidian45.Platform.isMobile && !clicked_load_env) {
      if (container) {
        container.empty();
        scope.env.smart_view.safe_inner_html(container, "<button>Load Smart Environment</button>");
        container.querySelector("button").addEventListener("click", () => {
          clicked_load_env = true;
          if (typeof scope.env.start_mobile_env_load === "function") {
            scope.env.start_mobile_env_load({ source: "wait_for_env_to_load" });
            return;
          }
          scope.env.load(true);
        });
      } else {
        console.log("Waiting for env to load (mobile)...");
      }
      await new Promise((r) => setTimeout(r, 2e3));
    }
    while (!wait_for_states.includes(scope.env.state)) {
      if (container) {
        const loading_msg = scope.env?.obsidian_is_syncing ? "Waiting for Obsidian Sync to finish..." : "Loading Obsidian Smart Environment...";
        container.empty();
        scope.env.smart_view.safe_inner_html(container, loading_msg);
      } else {
        console.log("Waiting for env to load...");
      }
      await new Promise((r) => setTimeout(r, 2e3));
    }
  }
}

// node_modules/obsidian-smart-env/views/smart_item_view.js
var SmartItemView = class extends import_obsidian46.ItemView {
  /**
   * Creates an instance of SmartItemView.
   * @param {any} leaf
   * @param {any} plugin
   */
  constructor(leaf, plugin) {
    super(leaf);
    this.app = plugin.app;
    this.plugin = plugin;
  }
  /**
   * The unique view type. Must be implemented in subclasses.
   * @returns {string}
   */
  static get view_type() {
    throw new Error("view_type must be implemented in subclass");
  }
  /**
   * The display text for this view. Must be implemented in subclasses.
   * @returns {string}
   */
  static get display_text() {
    throw new Error("display_text must be implemented in subclass");
  }
  /**
   * The icon name for this view.
   * @returns {string}
   */
  static get icon_name() {
    return "smart-connections";
  }
  /**
   * Whether the view should wait for the environment to be loaded before rendering.
   * Override in subclasses that must stay visible during environment load.
   * @returns {boolean}
   */
  static get wait_for_env() {
    return true;
  }
  /**
   * Registers this ItemView subclass against a plugin instance and
   * installs ergonomic accessors, an open helper, and an `${view_type}:open` listener.
   *
   * Usage from a plugin class:
   *   SubClass.register_item_view(this);
   *
   * This will:
   * - call plugin.registerView(view_type, ...)
   * - add a command "Open: <display_text> view"
   * - define a getter on plugin: plugin[method_name] -> the view instance
   * - define a method on plugin: plugin["open_" + method_name]() -> opens the view
   * - listen for env events named `${view_type}:open` and open the view when emitted
   *
   * @param {import('obsidian').Plugin} plugin
   * @returns {{method_name:string, open_method_name:string, event_name:string}}
   */
  static register_item_view(plugin) {
    const View = (
      /** @type {typeof SmartItemView} */
      this
    );
    if (plugin.app.viewRegistry?.viewByType?.[View.view_type]) {
      plugin.app.viewRegistry.unregisterView(View.view_type);
      console.warn(`View type "${View.view_type}" was already registered. Overwriting with new registration.`);
    }
    plugin.registerView(View.view_type, (leaf) => new View(leaf, plugin));
    plugin.addCommand({
      id: View.view_type,
      name: "Open: " + View.display_text + " view",
      callback: () => {
        View.open(plugin.app.workspace);
      }
    });
    const method_name = View.view_type.replace(/^smart-/, "").replace(/-/g, "_");
    const open_method_name = "open_" + method_name;
    if (!Object.getOwnPropertyDescriptor(plugin, method_name)) {
      Object.defineProperty(plugin, method_name, {
        configurable: true,
        enumerable: false,
        get: () => View.get_view(plugin.app.workspace)
      });
    }
    plugin[open_method_name] = (params = {}) => {
      if (!plugin.app.viewRegistry?.viewByType?.[View.view_type]) {
        console.warn(`View type "${View.view_type}" not registered when calling ${open_method_name}. Registering now. This should NOT happen frequently.`);
        plugin.registerView(View.view_type, (leaf) => new View(leaf, plugin));
      }
      View.open(plugin.app.workspace, params);
    };
    const event_name = `${method_name}:open`;
    const handler = (payload = {}) => {
      const active = typeof payload?.active === "boolean" ? payload.active : true;
      View.open(plugin.app.workspace, { ...payload, active });
    };
    const unsubscribe = plugin?.env?.events.on(event_name, handler);
    if (typeof plugin.register === "function" && typeof unsubscribe === "function") {
      plugin.register(() => unsubscribe());
    }
    return { method_name, open_method_name, event_name };
  }
  /**
   * Retrieves the Leaf instance for this view type if it exists.
   * @param {import("obsidian").Workspace} workspace
   * @returns {import("obsidian").WorkspaceLeaf | undefined}
   */
  static get_leaf(workspace) {
    return workspace.getLeavesOfType(this.view_type)[0];
  }
  /**
   * Retrieves the view instance if it exists.
   * @param {import("obsidian").Workspace} workspace
   * @returns {SmartItemView | undefined}
   */
  static get_view(workspace) {
    const leaf = this.get_leaf(workspace);
    return leaf ? leaf.view : void 0;
  }
  /**
   * Opens the view. If `this.default_open_location` is "root",
   * it opens (or reveals) in a root leaf; otherwise in a sidebar leaf.
   *
   * @param {import("obsidian").Workspace} workspace
   * @param {boolean|object} [params={}]
   */
  static open(workspace, params = {}) {
    if (typeof params === "boolean") {
      params = { active: params };
    }
    const {
      active = true,
      state = null
    } = params;
    const existing_leaf = this.get_leaf(workspace);
    const open_location = this.default_open_location;
    let leaf;
    if (open_location === "root") {
      leaf = existing_leaf || workspace.getLeaf(false);
    } else if (open_location === "left") {
      leaf = existing_leaf || workspace.getLeftLeaf(false);
      if (workspace.leftSplit?.collapsed) {
        workspace.leftSplit.toggle();
      }
    } else {
      leaf = existing_leaf || workspace.getRightLeaf(false);
      if (workspace.rightSplit?.collapsed) {
        workspace.rightSplit.toggle();
      }
    }
    const view_state = { type: this.view_type, active };
    if (state && typeof state === "object") {
      view_state.state = state;
    }
    Promise.resolve(leaf?.setViewState?.(view_state)).then(() => {
      if (active) {
        try {
          workspace.revealLeaf?.(leaf);
        } catch (error) {
          console.warn(`Failed to reveal item view "${this.view_type}"`, error);
        }
      }
      setTimeout(() => {
        this.get_view(workspace)?.render_view(params);
      }, 100);
    }).catch((error) => {
      console.error(`Failed to open item view "${this.view_type}"`, error);
    });
  }
  static is_open(workspace) {
    return this.get_leaf(workspace)?.view instanceof this;
  }
  // instance
  getViewType() {
    return this.constructor.view_type;
  }
  getDisplayText() {
    return this.constructor.display_text;
  }
  getIcon() {
    return this.constructor.icon_name;
  }
  async onOpen() {
    this.app.workspace.onLayoutReady(this.initialize.bind(this));
  }
  async initialize() {
    await this.render_mobile_status_bar();
    const should_wait_for_env = this.constructor.wait_for_env !== false;
    const wait_for_states = ["loaded"];
    if (should_wait_for_env) {
      await wait_for_env_to_load(this, { wait_for_states });
    }
    this.container?.empty?.();
    this.register_plugin_events();
    this.app.workspace.registerHoverLinkSource(this.constructor.view_type, { display: this.getDisplayText(), defaultMod: true });
    this.render_view();
  }
  async render_mobile_status_bar() {
    if (!import_obsidian46.Platform.isMobile) return;
    if (!this.env?.smart_view) return;
    const status_bar_container = this.containerEl.querySelector(".status-bar-mobile") ?? this.containerEl.createDiv({ cls: "status-bar-mobile" });
    status_bar_container.empty?.();
    const status_bar_item = status_bar_container.createDiv({ cls: "status-bar-item" });
    try {
      const status_bar = await render25.call(this.env.smart_view, this.env);
      if (status_bar) status_bar_item.appendChild(status_bar);
    } catch (error) {
      console.error("Failed to render mobile Smart Env status bar", error);
    }
  }
  register_plugin_events() {
  }
  render_view(params = {}) {
    throw new Error("render_view must be implemented in subclass");
  }
  get container() {
    return this.containerEl.children[1];
  }
  get env() {
    return this.plugin.env;
  }
  async open_settings() {
    await this.app.setting.open();
    await this.app.setting.openTabById(this.plugin.manifest.id);
  }
};

// node_modules/obsidian-smart-env/src/views/env_status_view.js
var EnvStatusView = class extends SmartItemView {
  static view_type = "smart-env-status-view";
  static display_text = "Smart Environment Status";
  static get icon_name() {
    return "gauge";
  }
  static get default_open_location() {
    return import_obsidian47.Platform.isMobile ? "root" : "right";
  }
  static get wait_for_env() {
    return false;
  }
  async onOpen() {
    this.titleEl?.setText?.("Smart Environment");
    await super.onOpen();
  }
  register_plugin_events() {
    if (this._env_status_view_cleanup_registered) return;
    this._env_status_view_cleanup_registered = true;
    this.register(() => {
      this.stop_renderer_upgrade_polling();
      this._active_renderer_key = null;
      this._last_render_params = null;
      this._render_component_promise = null;
      this._env_status_view_cleanup_registered = false;
    });
  }
  render_view(params = {}) {
    this._last_render_params = params;
    this.render_component_view(params);
  }
  async render_component_view(params = {}) {
    const container = this.container;
    if (!container) return;
    if (this._render_component_promise) return this._render_component_promise;
    this._render_component_promise = render_env_status(this, params).then(({ component_el, renderer_key }) => {
      if (!component_el) return;
      if (this.container !== container) return;
      empty_element(container);
      container.appendChild(component_el);
      this._active_renderer_key = renderer_key;
      if (renderer_key === "smart_components") {
        this.stop_renderer_upgrade_polling();
      } else {
        this.start_renderer_upgrade_polling();
      }
    }).catch((error) => {
      console.error("Failed to render env_status component", error);
    }).finally(() => {
      this._render_component_promise = null;
    });
    return this._render_component_promise;
  }
  start_renderer_upgrade_polling() {
    if (this._env_status_renderer_upgrade_interval) return;
    this._env_status_renderer_upgrade_interval = setInterval(() => {
      if (!can_render_via_smart_components(this.env)) return;
      if (this._active_renderer_key === "smart_components") {
        this.stop_renderer_upgrade_polling();
        return;
      }
      this.render_component_view(this._last_render_params || {});
    }, 500);
  }
  stop_renderer_upgrade_polling() {
    if (!this._env_status_renderer_upgrade_interval) return;
    clearInterval(this._env_status_renderer_upgrade_interval);
    this._env_status_renderer_upgrade_interval = null;
  }
  async onClose() {
    this.stop_renderer_upgrade_polling();
  }
};
function can_render_via_smart_components(env) {
  return Boolean(env?.config?.components?.env_status) && typeof env?.smart_components?.render_component === "function";
}
async function render_env_status(view, params = {}) {
  const component_params = {
    ...params,
    live_updates: true,
    event: params.event,
    event_key: params.event_key
  };
  if (can_render_via_smart_components(view.env)) {
    try {
      const component_el = await view.env.smart_components.render_component("env_status", view.env, component_params);
      return {
        component_el,
        renderer_key: "smart_components"
      };
    } catch (error) {
      console.error("Failed to render env_status via smart_components, falling back to direct component render", error);
    }
  }
  return {
    component_el: await render5.call(view.env.smart_view, view.env, component_params),
    renderer_key: "direct"
  };
}
function empty_element(element) {
  if (!element) return;
  if (typeof element.empty === "function") {
    element.empty();
    return;
  }
  element.replaceChildren?.();
}

// node_modules/obsidian-smart-env/src/views/smart_env_settings_tab.js
var import_obsidian50 = require("obsidian");

// node_modules/obsidian-smart-env/src/utils/render_pre_env_load.js
var import_obsidian48 = require("obsidian");
function render_pre_env_load(scope) {
  const container = scope.containerEl;
  const env = scope.env;
  if (env.state !== "loaded") {
    if (env.state === "loading") {
      container.createEl("p", { text: "Smart Environment is loading\u2026" });
      const status_btn = container.createEl("button", { text: "Show loading status" });
      status_btn.addEventListener("click", () => {
        env.open_env_status_view();
      });
    } else {
      container.createEl("p", { text: "Smart Environment not yet initialized." });
      const load_btn = container.createEl("button", { text: "Load Smart Environment" });
      load_btn.addEventListener("click", async () => {
        load_btn.disabled = true;
        load_btn.textContent = "Loading Smart Environment\u2026";
        if (import_obsidian48.Platform.isMobile && typeof env.start_mobile_env_load === "function") {
          await env.start_mobile_env_load({ source: "settings_tab" });
          return;
        }
        await env.load(true);
      });
    }
  }
}

// node_modules/obsidian-smart-env/src/utils/render_plugin_store_setting.js
var import_obsidian49 = require("obsidian");
function render_plugin_store_setting(scope, container) {
  if (!container) return null;
  container.empty?.();
  const setting = new import_obsidian49.Setting(container).setName("Browse Smart Plugins").setDesc("Discover Core (free) and Pro Smart Plugins to supercharge your Obsidian AI experience.");
  setting.addButton((btn) => {
    btn.setButtonText("Browse Smart Plugins");
    btn.onClick(() => {
      scope.env?.events?.emit?.("smart_plugins:browse", {
        event_source: `${scope.id}-settings`
      });
    });
  });
  return setting;
}

// node_modules/obsidian-smart-env/src/views/settings.css
var settings_default = `/* 1) Host elements that should get a PRO badge */\r
:is(\r
  .pro-setting .setting-item-name\r
) {\r
  position: relative; /* safe default, keeps ::after anchored */\r
}\r
\r
/* 2) The PRO badge itself */\r
:is(\r
  .pro-setting .setting-item-name:not(:empty)\r
)::after {\r
  content: "PRO";\r
\r
  /* layout */\r
  display: inline-flex;\r
  align-items: center;\r
  justify-content: center;\r
  margin-left: 0.4em;\r
  padding: 0.08em 0.55em;\r
  border-radius: 999px;\r
  white-space: nowrap;\r
  vertical-align: middle;\r
\r
  /* typography */\r
  font-size: 0.7em;\r
  font-weight: 600;\r
  letter-spacing: 0.14em;\r
  text-transform: uppercase;\r
  line-height: 1;\r
\r
  /* color system: only Obsidian variables */\r
  background-color: var(--color-accent);\r
  background-image: linear-gradient(\r
    135deg,\r
    var(--color-accent),\r
    var(--interactive-accent-hover)\r
  );\r
  color: var(--text-on-accent, var(--background-primary));\r
  border: 1px solid var(--background-modifier-border);\r
\r
  /* subtle separation & depth, theme-aware */\r
  box-shadow:\r
    0 0 0 1px var(--background-primary),\r
    0 1px 3px rgba(0, 0, 0, 0.35);\r
  transform: translateY(-0.03em);\r
}\r
\r
/* 3) Interactive refinement: follow Obsidian's accent hover behavior */\r
:is(\r
  .pro-setting .setting-item-name\r
):hover::after {\r
  background-color: var(--interactive-accent-hover);\r
  filter: brightness(1.05);\r
}\r
\r
.smart-plugin-settings-header .actions-container {\r
  display: flex;\r
  flex-wrap: wrap;\r
  gap: var(--pill-padding-y);\r
}\r
\r
.setting-component:has(.dropdown-no-options) {\r
  display: none;\r
}\r
\r
/* wrap Obsidian native styles within smart plugin settings main class */\r
.smart-plugin-settings-main, .smart-plugin-settings-env {\r
  .setting-group {\r
    margin-top: var(--size-4-6);\r
    margin-bottom: var(--size-4-6);\r
  }\r
  /* polyfill */\r
  .setting-group .setting-items {\r
    background-color: var(--setting-items-background, var(--background-primary-alt));\r
    padding: var(--setting-items-padding, var(--size-4-5));\r
    border-radius: var(--setting-items-radius, var(--radius-l));\r
    border: var(--setting-items-border-width, 0) solid var(--setting-items-border-color, var(--background-modifier-border));\r
  }\r
}\r
\r
/* show icon for all smart-* plugins except the main smart environment settings tab */\r
.vertical-tab-header-group-items[data-section="community-plugins"] [data-setting-id^="smart-"]:not([data-setting-id^="smart-environment"]) .vertical-tab-nav-item-icon {\r
  display: flex;\r
}`;

// node_modules/obsidian-smart-env/src/views/smart_env_settings_tab.js
var SmartEnvSettingTab = class extends import_obsidian50.PluginSettingTab {
  constructor(app2, plugin, icon = "smart-connections") {
    super(app2, plugin, icon);
    this.plugin = plugin;
    this.header_container = null;
    this.plugin_container = null;
    this.global_settings_container = null;
    this.plugin?.env?.create_env_getter?.(this);
    this.plugin = plugin;
    this.name = "  Smart Environment";
    this.id = "smart-environment";
    if (!this.icon && icon) this.icon = icon;
    this.smart_view.apply_style_sheet(settings_default);
  }
  get smart_view() {
    return this.env?.smart_view;
  }
  display() {
    this.render();
  }
  async render_component(name, scope, params = {}) {
    return await this.env.smart_components.render_component(name, scope, params);
  }
  async render() {
    this.containerEl.empty();
    if (!this.env || ["init", "loading"].includes(this.env.state)) {
      render_pre_env_load(this);
      await this.env.constructor.wait_for({ loaded: true });
    }
    this.containerEl.empty();
    if (Object.values(this.env.plugin_states).some((state) => state === "deferred")) {
      this.containerEl.createDiv({ text: "Smart Plugins are waiting to be loaded. Restart Obsidian to finish loading the Smart Environment." });
      const button = this.containerEl.createEl("button", { text: "Restart Obsidian" });
      button.addEventListener("click", () => {
        window.location.reload();
      });
    }
    this.header_container = this.containerEl.createDiv({ cls: "smart-plugin-settings-header" });
    this.plugin_container = this.containerEl.createDiv({ cls: "smart-plugin-settings-main" });
    this.pro_plugins_container = this.containerEl.createDiv({ cls: "smart-plugin-settings-pro-plugins" });
    this.header_container.createEl("p", { text: "Manage all global Smart Environment settings from one tab. These settings apply to all Smart Plugins." });
    const settings_smart_env = await this.render_component("settings_smart_env", this.env);
    if (settings_smart_env) this.plugin_container.appendChild(settings_smart_env);
    render_plugin_store_setting(this, this.pro_plugins_container);
  }
};

// node_modules/obsidian-smart-env/package.json
var package_default2 = {
  name: "obsidian-smart-env",
  author: "Brian Joseph Petro (\u{1F334} Brian)",
  license: "SEE LICENSE IN LICENSE",
  version: "2.4.4",
  type: "module",
  description: "Implements Smart Environment best practices for Obsidian.",
  main: "index.js",
  repository: {
    type: "git",
    url: "brianpetro/jsbrains"
  },
  bugs: {
    url: "https://github.com/brianpetro/jsbrains/issues"
  },
  scripts: {
    build: "node build/build.js",
    test: "npx ava --verbose"
  },
  homepage: "https://jsbrains.org",
  dependencies: {
    obsidian: "^1.11.0",
    "smart-blocks": "file:../jsbrains/smart-blocks",
    "smart-chat-model": "file:../jsbrains/smart-chat-model",
    "smart-collections": "file:../jsbrains/smart-collections",
    "smart-completions": "file:../jsbrains/smart-completions",
    "smart-contexts": "file:../jsbrains/smart-contexts",
    "smart-embed-model": "file:../jsbrains/smart-embed-model",
    "smart-entities": "file:../jsbrains/smart-entities",
    "smart-environment": "file:../jsbrains/smart-environment",
    "smart-file-system": "file:../jsbrains/smart-fs",
    "smart-http-request": "file:../jsbrains/smart-http-request",
    "smart-model": "file:../jsbrains/smart-model",
    "smart-models": "file:../jsbrains/smart-models",
    "smart-notices": "file:../jsbrains/smart-notices",
    "smart-settings": "file:../jsbrains/smart-settings",
    "smart-sources": "file:../jsbrains/smart-sources",
    "smart-types": "file:../jsbrains/smart-types",
    "smart-utils": "file:../jsbrains/smart-utils",
    "smart-view": "file:../jsbrains/smart-view"
  },
  devDependencies: {
    "@xenova/transformers": "latest",
    archiver: "^7.0.1",
    ava: "^6.3.0",
    axios: "^1.13.2",
    dotenv: "^17.2.3",
    "js-tiktoken": "^1.0.19",
    readline: "^1.3.0"
  },
  workspaces: [
    "../jsbrains/*"
  ]
};

// node_modules/obsidian-smart-env/smart_env.js
var MIN_COMPATIBLE_SMART_ENV_VERSION = "2.4.0";
var SmartEnv2 = class extends SmartEnv {
  static version = package_default2.version;
  constructor(opts = {}) {
    super(opts);
    this.plugin_states = {};
  }
  /**
   * Override to prevent loading outdated plugins
   * ---
   * This also stinks, replace in v3 (2026-03-31)
   */
  get config() {
    const signature = this.compute_collections_version_signature();
    if (this._config && signature === this._collections_version_signature) {
      return this._config;
    }
    this._collections_version_signature = signature;
    this._config = {};
    const sorted_configs = Object.entries(this.smart_env_configs).sort(([a_key], [b_key]) => {
      if (!this.primary_main_key) return 0;
      if (a_key === this.primary_main_key) return -1;
      if (b_key === this.primary_main_key) return 1;
      return 0;
    });
    for (const [key, rec] of sorted_configs) {
      if (!rec?.main) {
        console.warn(`SmartEnv: '${key}' unloaded, skipping`);
        delete this.smart_env_configs[key];
        continue;
      }
      if (!rec?.opts) {
        console.warn(`SmartEnv: '${key}' opts missing, skipping`);
        continue;
      }
      if (!is_supported_smart_env_version(rec.opts.version)) {
        delete this.smart_env_configs[key];
        continue;
      }
      merge_env_config(
        this._config,
        deep_clone_config(normalize_opts(rec.opts))
      );
    }
    return this._config;
  }
  /**
   * Creates and initializes a SmartEnv instance tailored for Obsidian.
   * @deprecated 2026-03-31 This code is so stinky I cringe. It will be replaced in next major version. Continue using but expect replacement. Do not depend on returned SmartEnv instance.
   * @param {Object} plugin - The Obsidian plugin instance.
   * @param {Object} [env_config] - Required environment configuration object.
   * @returns {Promise<SmartEnv>} The initialized SmartEnv instance.
   */
  static create(plugin, env_config) {
    handle_outdated_plugins();
    if (!plugin) throw new Error("SmartEnv.create: 'plugin' parameter is required.");
    if (!env_config) throw new Error("SmartEnv.create: 'env_config' parameter is required.");
    env_config.version = this.version;
    add_smart_chat_icon();
    add_smart_connections_icon();
    add_smart_lookup_icon();
    add_smart_icons();
    const opts = merge_env_config(env_config, default_config_default);
    opts.env_path = "";
    const existing_env = this.global_env;
    if (existing_env && (existing_env.state === "loaded" || is_global_env_locked2(this.global_ref))) {
      handle_env_load_attempt_after_loaded(existing_env);
      this.create_env_getter(plugin);
      return existing_env;
    }
    if (existing_env && existing_env.state !== "init") {
      const is_newer_env = compare_versions(this.version, existing_env.constructor?.version) > 0;
      if (is_newer_env) {
        console.warn(
          "SmartEnv: Superseding environment before load lock because a newer SmartEnv version is available",
          `${this.version} > ${existing_env.constructor?.version}`
        );
      }
    }
    return super.create(plugin, opts).then((env) => {
      clearTimeout(env.load_timeout);
      env.load_timeout = null;
      if (!env._startup_load_registration) {
        env._startup_load_registration = true;
        plugin.registerEvent(plugin.app.workspace.onLayoutReady(async () => {
          if (env.state !== "init") {
            console.warn(`Skipping SmartEnv (v${env.constructor.version}) load on layout ready; env.state: "${env.state}".`);
            return;
          }
          const continue_load = await env.before_load();
          if (continue_load === false) {
            console.warn("SmartEnv before_load returned false, skipping load.");
            return;
          }
          await env.load();
        }));
      }
      return env;
    });
  }
  async before_load() {
    this.run_migrations();
    this.register_notification_dispatchers();
    this.register_env_item_views();
    this.register_env_settings_tab();
    if (typeof this._onboarding_events_teardown !== "function") {
      this._onboarding_events_teardown = register_first_of_event_notifications(this);
    }
    if (!this.plugin.app.workspace.protocolHandlers.has("smart-plugins/callback")) {
      this.plugin.registerObsidianProtocolHandler("smart-plugins/callback", async (params) => {
        await this.handle_smart_plugins_oauth_callback(params);
      });
    }
    if (import_obsidian51.Platform.isDesktop) this.register_status_bar();
    if (import_obsidian51.Platform.isMobile && this.state !== "loaded") {
      const frag = this.smart_view.create_doc_fragment(
        "<div><p>Smart Environment loading deferred on mobile.</p><button>Load Smart Environment</button></div>"
      );
      frag.querySelector("button").addEventListener("click", () => {
        this.start_mobile_env_load({ source: "mobile_deferred_notice" });
      });
      new import_obsidian51.Notice(frag, 0);
      return false;
    }
    return true;
  }
  async after_load() {
    this.smart_sources?.register_source_watchers?.(this.smart_sources);
    this.register_workspace_source_events();
    if (this._config.collections.smart_completions?.completion_adapters?.SmartCompletionVariableAdapter) {
      register_completion_variable_adapter_replacements(this._config.collections.smart_completions.completion_adapters.SmartCompletionVariableAdapter);
    }
    this.register_configured_modals();
    this.refresh_status_bar();
    if (!this._registered_browse_smart_plugins_command) {
      this._registered_browse_smart_plugins_command = this.plugin.manifest?.id + ":browse-smart-plugins";
      this.plugin.addCommand({
        id: "browse-smart-plugins",
        name: "Browse Smart Plugins",
        callback: () => {
          this.events.emit("smart_plugins:browse", {
            event_source: "command_palette"
          });
        }
      });
    }
    if (!this._registered_env_status_view_command) {
      this._registered_env_status_view_command = this.plugin.manifest?.id + ":env-status-view";
      this.plugin.addCommand({
        id: "env-status-view",
        name: "Open Environment Status View",
        callback: () => {
          this.open_env_status_view();
        }
      });
    }
    this.mains.forEach((main_key) => {
      const plugin_id = this.smart_env_configs[main_key]?.main?.manifest?.id || "unknown-plugin";
      this.plugin_states[plugin_id] = "loaded";
    });
    if (!this._registered_ctx_menu_actions) {
      register_copy_menu_actions(this);
      register_context_menu_actions(this);
      this._registered_ctx_menu_actions = true;
    }
  }
  handle_env_load_attempt_after_loaded() {
    handle_env_load_attempt_after_loaded(this);
  }
  unload() {
    console.warn("Unloading SmartEnv");
    if (typeof this._onboarding_events_teardown === "function") {
      this._onboarding_events_teardown();
      this._onboarding_events_teardown = null;
    }
    this._workspace_source_events_registered = false;
    return super.unload?.();
  }
  /**
   * Register event dispatchers used by native notice buttons and event-first flows.
   *
   * @returns {void}
   */
  register_notification_dispatchers() {
    if (this._notification_dispatchers_registered) return;
    this._notification_dispatchers_registered = true;
    this.events.on("milestones_modal:open", () => {
      this.open_milestones_modal?.();
    });
    this.events.on("notifications_feed_modal:open", () => {
      this.open_notifications_feed_modal?.();
    });
    this.events.on("smart_plugins:browse", () => {
      this.browse_smart_plugins?.();
    });
    this.events.on("smart_env:load_mobile_requested", () => {
      this.start_mobile_env_load({ source: "native_notice_button" });
    });
  }
  /**
   * Register environment-owned item views once per plugin.
   * @returns {void}
   */
  register_env_item_views() {
    const plugin = this.main;
    if (!plugin?.registerView) return;
    if (!(this._registered_env_item_views instanceof Set)) {
      this._registered_env_item_views = /* @__PURE__ */ new Set();
    }
    const plugin_key = plugin.manifest?.id || "unknown-plugin";
    const view_classes = [EnvStatusView];
    view_classes.forEach((ViewClass) => {
      const registration_key = `${plugin_key}:${ViewClass.view_type}`;
      if (this._registered_env_item_views.has(registration_key)) return;
      try {
        ViewClass.register_item_view(plugin);
        this._registered_env_item_views.add(registration_key);
      } catch (error) {
        console.error(`Failed to register item view "${ViewClass.view_type}"`, error);
      }
    });
  }
  /**
   * Open the mobile-friendly status view item view.
   *
   * @param {object} [params={}]
   * @returns {void}
   */
  open_env_status_view(params = {}) {
    this.register_env_item_views();
    EnvStatusView.open(this.obsidian_app.workspace, params);
  }
  get env_status_view_command_id() {
    const command_id = this._registered_env_item_views.find((id) => id.endsWith(EnvStatusView.view_type));
    console.log({ command_id });
    return command_id || this.plugin.manifest?.id + ":" + EnvStatusView.view_type;
  }
  /**
   * Centralized mobile load flow used by native notices, settings tabs, and views.
   * Opens the persistent progress surface first, then starts loading if needed.
   *
   * @param {object} [params={}]
   * @param {boolean} [params.open_progress_view=true]
   * @returns {Promise<SmartEnv>|SmartEnv}
   */
  async start_mobile_env_load(params = {}) {
    const {
      open_progress_view = true
    } = params;
    if (open_progress_view) {
      this.open_env_status_view({ active: true });
    }
    if (this.state === "loaded") {
      this.refresh_status_bar();
      return this;
    }
    if (this.state === "loading" && this._load_promise) {
      return this._load_promise;
    }
    await this.load();
    return this;
  }
  /**
   * Register every modal declared in config that exposes a static register_modal helper.
   * @returns {void}
   */
  register_configured_modals() {
    const modal_entries = Object.entries(this._config?.modals || {});
    if (!this._registered_modal_keys) {
      this._registered_modal_keys = /* @__PURE__ */ new Set();
    }
    for (const [modal_key, modal_config] of modal_entries) {
      const ModalClass = modal_config?.class;
      if (typeof ModalClass?.register_modal !== "function") continue;
      if (this._registered_modal_keys.has(modal_key)) continue;
      try {
        ModalClass.register_modal(this.main);
        this._registered_modal_keys.add(modal_key);
      } catch (error) {
        console.error(`Failed to register modal "${modal_key}"`, error);
      }
    }
  }
  register_workspace_source_events() {
    if (this._workspace_source_events_registered) return;
    const plugin = this.main;
    if (!plugin?.registerEvent) return;
    this._workspace_source_events_registered = true;
    plugin.registerEvent(
      plugin.app.workspace.on("active-leaf-change", (leaf) => {
        this.smart_sources?.debounce_re_import_queue?.();
        const current_path = leaf.view?.file?.path;
        this.emit_source_opened(current_path, "active-leaf-change");
      })
    );
    plugin.registerEvent(
      plugin.app.workspace.on("file-open", (file) => {
        this.smart_sources?.debounce_re_import_queue?.();
        const current_path = file?.path;
        this.emit_source_opened(current_path, "file-open");
      })
    );
  }
  emit_source_opened(current_path, event_source = null) {
    if (this._current_opened_source === current_path) return;
    const current_source = this.smart_sources.get(current_path);
    if (current_source) {
      this._current_opened_source = current_path;
      current_source.emit_event("sources:opened", { event_source });
    }
  }
  queue_source_re_import(source) {
    this.smart_sources?.queue_source_re_import?.(source);
  }
  debounce_re_import_queue() {
    this.smart_sources?.debounce_re_import_queue?.();
  }
  async run_re_import() {
    await this.smart_sources?.run_re_import?.();
  }
  register_status_bar() {
    const add_status_bar_item = this.main?.addStatusBarItem?.bind(this.main);
    if (typeof add_status_bar_item !== "function") return;
    const status_container = this.main?.app?.statusBar?.containerEl;
    const existing_status_item = status_container?.querySelector?.(".smart-env-status-container")?.closest?.(".status-bar-item");
    if (existing_status_item && this.status_elm !== existing_status_item) {
      existing_status_item.remove?.();
    }
    if (!this.status_elm || !this.status_elm.isConnected) {
      this.status_elm = add_status_bar_item();
    }
    this.refresh_status_bar();
  }
  refresh_status_bar() {
    if (!this.status_elm) return;
    render25.call(this.smart_view, this).then((container) => {
      this.status_elm.empty?.();
      this.status_elm.appendChild(container);
    }).catch((error) => {
      console.error("Failed to render Smart Env status bar", error);
    });
  }
  register_menu_action(menu_key, fn) {
    if (!this._registered_menu_actions) {
      this._registered_menu_actions = {};
    }
    if (!this._registered_menu_actions[menu_key]) {
      this._registered_menu_actions[menu_key] = /* @__PURE__ */ new Set();
    }
    if (this._registered_menu_actions[menu_key].has(fn)) return;
    this._registered_menu_actions[menu_key].add(fn);
  }
  build_menu(menu_key, menu, scope) {
    const registered_actions = this._registered_menu_actions?.[menu_key] ?? /* @__PURE__ */ new Set();
    registered_actions.forEach((menu_action) => {
      menu_action(menu, scope);
    });
  }
  /**
   * @deprecated 2026-03-17 remove by next major release (keeping for backward compatibility during migration period)
   */
  get notices() {
    if (!this._notices) {
      this._notices = new SmartNotices(this, {
        adapter: import_obsidian51.Notice
      });
    }
    return this._notices;
  }
  // Smart Plugins
  /**
   * Handles the OAuth callback from the Smart Plugins server.
   * @param {Object} params - The URL parameters from the OAuth callback.
   */
  async handle_smart_plugins_oauth_callback(params) {
    const code = params.code;
    if (!code) {
      this.events.emit("smart_plugins_oauth_failed", {
        level: "error",
        message: "No OAuth code provided in URL. Login failed.",
        event_source: "handle_smart_plugins_oauth_callback"
      });
      return;
    }
    try {
      await exchange_code_for_tokens(code, this.plugin);
      this.events.emit("smart_plugins_oauth_completed");
    } catch (err) {
      console.error("OAuth callback error", err);
      this.events.emit("smart_plugins_oauth_failed", {
        level: "error",
        message: `OAuth callback error: ${err.message}`,
        details: err.stack || "",
        event_source: "handle_smart_plugins_oauth_callback"
      });
    }
  }
  /**
   * Serializes the environment and, when in a browser, triggers a download.
   * @param {string} [filename='smart_env.json']
   * @returns {string} stringified JSON
   */
  export_json(filename = "smart_env.json") {
    const json = JSON.stringify(this.to_json(), null, 2);
    if (typeof document !== "undefined") {
      download_json(json, filename);
    }
    return json;
  }
  // WAIT FOR OBSIDIAN SYNC
  async ready_to_load_collections() {
    await new Promise((r) => setTimeout(r, 3e3));
    await this.wait_for_obsidian_sync();
  }
  async wait_for_obsidian_sync() {
    while (this.obsidian_is_syncing) {
      console.log("Smart Connections: Waiting for Obsidian Sync to finish");
      await new Promise((r) => setTimeout(r, 1e3));
      if (!this.plugin) throw new Error("Plugin disabled while waiting for obsidian sync, reload required.");
    }
  }
  get obsidian_is_syncing() {
    const obsidian_sync_instance = this.plugin?.app?.internalPlugins?.plugins?.sync?.instance;
    if (!obsidian_sync_instance) return false;
    if (obsidian_sync_instance?.syncStatus.startsWith("Uploading")) return false;
    if (obsidian_sync_instance?.syncStatus.startsWith("Fully synced")) return false;
    return obsidian_sync_instance?.syncing;
  }
  get obsidian_app() {
    return this.plugin?.app ?? window.app;
  }
  open_notifications_feed_modal(params = {}) {
    const NotificationsModalClass = this.config.modals.notifications_feed_modal.class;
    const modal = new NotificationsModalClass(this.obsidian_app, this, params);
    modal.open();
  }
  open_milestones_modal() {
    const MilestonesModalClass = this.config.modals.milestones_modal.class;
    const modal = new MilestonesModalClass(this.obsidian_app, this);
    modal.open();
  }
  browse_smart_plugins() {
    const BrowseSmartPluginsClass = this.config?.modals?.browse_plugins_modal?.class;
    if (typeof BrowseSmartPluginsClass !== "function") return;
    const modal = new BrowseSmartPluginsClass(this.obsidian_app, this);
    modal.open();
  }
  run_migrations() {
    remove_smart_plugins_plugin({ app: this.plugin.app, plugin_ids: ["smart-plugins"] });
    remove_smart_plugins_plugin({ app: this.plugin.app, plugin_ids: ["smart-editor"] });
    remove_smart_plugins_plugin({ app: this.plugin.app, plugin_ids: ["smart-sources"] });
    remove_smart_plugins_plugin({ app: this.plugin.app, plugin_ids: ["smart-claude"] });
    remove_smart_plugins_plugin({ app: this.plugin.app, plugin_ids: ["smart-gemini"] });
    remove_smart_plugins_plugin({ app: this.plugin.app, plugin_ids: ["smart-deepseek"] });
    remove_smart_plugins_plugin({ app: this.plugin.app, plugin_ids: ["smart-perplexity"] });
    remove_smart_plugins_plugin({ app: this.plugin.app, plugin_ids: ["smart-grok"] });
    remove_smart_plugins_plugin({ app: this.plugin.app, plugin_ids: ["smart-aistudio"] });
  }
  // Detect Existing Smart Environment Settings Tab
  get env_settings_tab() {
    const app2 = this.plugin.app || window.app;
    return app2.setting.pluginTabs.find((t) => t.id === "smart-environment");
  }
  register_env_settings_tab() {
    if (this.env_settings_tab) return;
    this.plugin.addSettingTab(new SmartEnvSettingTab(this.plugin.app, this.plugin));
  }
};
function download_json(json, filename) {
  const blob = new Blob([json], { type: "application/json" });
  const url = URL.createObjectURL(blob);
  const anchor = document.createElement("a");
  anchor.href = url;
  anchor.download = filename;
  document.body.appendChild(anchor);
  anchor.click();
  document.body.removeChild(anchor);
  URL.revokeObjectURL(url);
}
function handle_env_load_attempt_after_loaded(env) {
  console.warn("Received attempt to load another SmartEnv after one has already loaded");
  const deferred_smart_plugins = Object.keys(env.plugin.app.plugins.plugins || {}).reduce((acc, plugin_id) => {
    if (plugin_id.startsWith("smart-") && env.plugin_states?.[plugin_id] !== "loaded" && env.main?.manifest?.id !== plugin_id) {
      acc[plugin_id] = "deferred";
    }
    return acc;
  }, {});
  env.plugin_states = {
    ...env.plugin_states,
    ...deferred_smart_plugins
  };
  const notice_message = `Smart Plugins waiting to load. Restart Obsidian to load ${Object.keys(deferred_smart_plugins).join(", ")} into the Smart Environment.`;
  if (is_supported_smart_env_version(env.constructor.version)) {
    env.events.emit("smart_env:restart_required", {
      level: "attention",
      message: notice_message,
      event_source: "SmartEnv.create",
      btn_text: "Restart Obsidian",
      btn_callback: "app:reload",
      timeout: 1e4
      // 10 seconds
    });
  } else {
    const notice_frag = document.createDocumentFragment();
    notice_frag.createEl("p", { text: notice_message });
    notice_frag.createEl("button", { text: "Restart Obsidian" }).addEventListener("click", (e) => {
      app.commands.executeCommandById("app:reload");
      e.target.textContent = "Reloading...";
    });
    new import_obsidian51.Notice(notice_frag, 0);
  }
  console.log("Unloading deferred Smart Plugins:", Object.keys(deferred_smart_plugins));
  Object.keys(deferred_smart_plugins).forEach((plugin_id) => {
    const plugin_instance = env.plugin.app.plugins.plugins[plugin_id];
    if (plugin_instance) {
      setTimeout(() => {
        try {
          console.log(`Unloading deferred plugin "${plugin_id}"`, plugin_instance);
          plugin_instance._loaded = true;
          plugin_instance.unload();
        } catch (error) {
          console.warn(`Error unloading deferred plugin "${plugin_id}" during load attempt of new SmartEnv. This should resolve itself after restart.`, error);
        }
      }, 3e3);
    } else {
      console.warn(`Plugin "${plugin_id}" not found during unload attempt of deferred plugins. This should resolve itself after restart.`);
    }
  });
}
function handle_outdated_plugins() {
  const smart_plugin_ids = Array.from(app.plugins.enabledPlugins).filter((id) => id.startsWith("smart-"));
  const all_enabled_initialized = smart_plugin_ids.every((plugin_id) => {
    return app.plugins.plugins[plugin_id];
  });
  if (!all_enabled_initialized) {
    setTimeout(() => {
      handle_outdated_plugins();
    }, 1);
    return;
  }
  const outdated_smart_plugins = smart_plugin_ids.reduce((acc, plugin_id) => {
    const plugin_instance = app.plugins.plugins[plugin_id];
    if (plugin_instance?.SmartEnv?.version && !is_supported_smart_env_version(plugin_instance.SmartEnv.version)) {
      plugin_instance.onload = () => {
        console.warn("prevented onload of outdated plugin", plugin_id);
      };
      acc[plugin_id] = "outdated";
    }
    return acc;
  }, {});
  if (Object.keys(outdated_smart_plugins).length === 0) return;
  console.warn("Outdated Smart Plugins detected:", Object.keys(outdated_smart_plugins));
  const notice_frag = document.createDocumentFragment();
  notice_frag.createEl("p", { text: `Detected outdated plugins: ${Object.keys(outdated_smart_plugins).join(", ")} in Smart Environment.` });
  notice_frag.createEl("p", { text: `Please update ${Object.keys(outdated_smart_plugins).join(", ")} then restart Obsidian to load all Smart Plugins.` });
  notice_frag.createEl("button", { text: "Check for Updates" }).addEventListener("click", () => {
    window.smart_env?.events.emit("smart_plugins:browse", {
      event_source: "outdated_env_notice_button"
    });
  });
  new import_obsidian51.Notice(notice_frag, 0);
  Object.keys(outdated_smart_plugins).forEach((plugin_id) => {
    const plugin_instance = app.plugins.plugins[plugin_id];
    if (plugin_instance) {
      setTimeout(() => {
        try {
          console.log(`Unloading outdated plugin "${plugin_id}"`, plugin_instance);
          plugin_instance._loaded = true;
          plugin_instance.unload();
        } catch (error) {
          console.warn(`Error unloading outdated plugin "${plugin_id}" during load attempt of new SmartEnv. This should resolve itself after restart.`, error);
        }
      }, 3e3);
    } else {
      console.warn(`Plugin "${plugin_id}" not found during unload attempt of outdated plugins. This should resolve itself after restart.`);
    }
  });
}
function is_global_env_locked2(global_ref) {
  return Object.getOwnPropertyDescriptor(global_ref, "smart_env")?.configurable === false;
}
function is_supported_smart_env_version(version4) {
  return compare_versions(version4, MIN_COMPATIBLE_SMART_ENV_VERSION) >= 0;
}

// node_modules/obsidian-smart-env/src/views/smart_plugin_settings_tab.js
var import_obsidian52 = require("obsidian");
var SmartPluginSettingsTab = class extends import_obsidian52.PluginSettingTab {
  constructor(app2, plugin, icon = "smart-connections") {
    super(app2, plugin, icon);
    this.plugin = plugin;
    this.header_container = null;
    this.plugin_container = null;
    this.global_settings_container = null;
    this.plugin?.env?.create_env_getter?.(this);
    if (!this.icon && icon) this.icon = icon;
    this.name = this.name.replace("Smart ", " ");
  }
  get smart_view() {
    return this.env?.smart_view;
  }
  async display() {
    await this.render();
  }
  async render() {
    this.containerEl.empty();
    if (!this.env || ["init", "loading"].includes(this.env.state)) {
      render_pre_env_load(this);
      await this.env.constructor.wait_for({ loaded: true });
    }
    if (this.env.plugin_states?.[this.plugin?.manifest?.id] === "deferred") {
      this.containerEl.createDiv({ text: "Restart Obsidian to load this plugin into the Smart Environment." });
      const button = this.containerEl.createEl("button", { text: "Restart Obsidian" });
      button.addEventListener("click", () => {
        window.location.reload();
      });
      return;
    }
    this.prepare_layout();
    await this.render_header(this.header_container);
    await this.render_plugin_settings(this.plugin_container);
    await this.render_global_settings(this.global_settings_container);
  }
  prepare_layout() {
    this.smart_view.apply_style_sheet(settings_default);
    this.containerEl.empty();
    this.header_container = this.containerEl.createDiv({ cls: "smart-plugin-settings-header" });
    this.plugin_container = this.containerEl.createDiv({ cls: "smart-plugin-settings-main" });
    this.global_settings_container = this.containerEl.createDiv({ cls: "smart-plugin-settings-env" });
    this.pro_plugins_container = this.containerEl.createDiv({ cls: "smart-plugin-settings-pro-plugins" });
  }
  /**
   * @abstract
   */
  async render_header(container) {
  }
  /**
   * @abstract
   */
  async render_plugin_settings(container) {
  }
  async render_global_settings(container) {
    if (!container) return;
    container.empty?.();
    if (!this.env) return;
    const settings_item_div = container.createDiv({ cls: "setting-item" });
    const info_div = settings_item_div.createDiv({ cls: "setting-item-info" });
    info_div.createDiv({ cls: "setting-item-name", text: "Smart Environment" });
    info_div.createDiv({
      cls: "setting-item-description",
      text: "Manage global settings in the dedicated Smart Environment settings tab."
    });
    const control_div = settings_item_div.createDiv({ cls: "setting-item-control" });
    const button = control_div.createEl("button", { text: "Open settings" });
    button.addEventListener("click", () => {
      this.app.setting.openTabById("smart-environment");
    });
    render_plugin_store_setting(this, this.pro_plugins_container);
  }
  async render_component(name, scope, params = {}) {
    return await this.env.smart_components.render_component(name, scope, params);
  }
};

// node_modules/obsidian-smart-env/smart_plugin.js
var import_obsidian53 = require("obsidian");
var SmartPlugin = class extends import_obsidian53.Plugin {
  SmartEnv = SmartEnv2;
  /**
   * override in subclass to provide commands.
   * use property key to override commands in further subclasses.
   */
  get commands() {
    return {};
  }
  register_commands() {
    Object.values(this.commands).forEach((cmd) => {
      this.addCommand(cmd);
    });
  }
  /**
   * override in subclass to provide ribbon icons.
   * use property key to override ribbon icons in further subclasses.
   */
  get ribbon_icons() {
    return {};
  }
  register_ribbon_icons() {
    const icons = Object.values(this.ribbon_icons);
    for (let i = 0; i < icons.length; i++) {
      const ri = icons[i];
      this.addRibbonIcon(ri.icon_name, ri.description, ri.callback);
    }
  }
  get item_views() {
    return {};
  }
  register_item_views() {
    const views = Object.values(this.item_views);
    for (let i = 0; i < views.length; i++) {
      const ViewClass = views[i];
      if (typeof ViewClass.register_item_view === "function") {
        ViewClass.register_item_view(this);
      }
    }
  }
  /**
   * user version and first seen handling
   */
  async is_new_user() {
    const data = await this.loadData() || {};
    if (!data.installed_at) {
      data.installed_at = Date.now();
      await this.saveData(data);
      return true;
    }
    return false;
  }
  /**
   * Returns the last saved plugin version or an empty string.
   * @returns {Promise<string>}
   */
  async get_last_known_version() {
    const data = await this.loadData() || {};
    return data.last_version || "";
  }
  /**
   * Persists the provided plugin version as last shown.
   * @param {string} version
   * @returns {Promise<void>}
   */
  async set_last_known_version(version4) {
    const data = await this.loadData() || {};
    data.last_version = version4;
    await this.saveData(data);
  }
  /**
   * Determines if release notes should be shown for `current_version`.
   * @param {string} current_version
   * @returns {Promise<boolean>}
   */
  async is_new_plugin_version(current_version) {
    return await this.get_last_known_version() !== current_version;
  }
  async check_for_updates() {
    if (this.ReleaseNotesView && await this.is_new_plugin_version(this.manifest.version)) {
      console.log("opening release notes modal");
      try {
        this.ReleaseNotesView.open(this.app.workspace, this.manifest.version);
      } catch (e) {
        console.error("Failed to open ReleaseNotesView", e);
      }
      await this.set_last_known_version(this.manifest.version);
    }
  }
  /**
   * @deprecated use SmartEnv.notices instead
   */
  get notices() {
    if (this.env?.notices) return this.env.notices;
    if (!this._notices) this._notices = new SmartNotices(this.env, import_obsidian53.Notice);
    return this._notices;
  }
};

// node_modules/smart-collections/utils/collection_instance_name_from.js
function collection_instance_name_from2(class_name) {
  if (class_name.endsWith("Item")) {
    return class_name.replace(/Item$/, "").replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase();
  }
  return class_name.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase().replace(/y$/, "ie") + "s";
}

// node_modules/smart-utils/deep_merge.js
function deep_merge2(target = {}, source = {}) {
  for (const key in source) {
    if (!Object.prototype.hasOwnProperty.call(source, key)) continue;
    if (is_plain_object5(source[key]) && is_plain_object5(target[key])) {
      deep_merge2(target[key], source[key]);
    } else {
      target[key] = source[key];
    }
  }
  return target;
}
function is_plain_object5(o) {
  return o && typeof o === "object" && !Array.isArray(o);
}

// node_modules/smart-collections/utils/helpers.js
function create_uid2(data) {
  const str = JSON.stringify(data);
  let hash = 0;
  if (str.length === 0) return hash;
  for (let i = 0; i < str.length; i++) {
    const char = str.charCodeAt(i);
    hash = (hash << 5) - hash + char;
    hash = hash & hash;
    if (hash < 0) hash = hash * -1;
  }
  return hash.toString() + str.length;
}

// node_modules/smart-utils/camel_case_to_snake_case.js
function camel_case_to_snake_case2(str = "") {
  return str.replace(/([A-Z])/g, (m) => `_${m.toLowerCase()}`).replace(/^_/, "").replace(/2$/, "");
}

// node_modules/smart-collections/utils/deep_equal.js
function deep_equal2(obj1, obj2, visited = /* @__PURE__ */ new WeakMap()) {
  if (obj1 === obj2) return true;
  if (obj1 === null || obj2 === null || obj1 === void 0 || obj2 === void 0) return false;
  if (typeof obj1 !== typeof obj2 || Array.isArray(obj1) !== Array.isArray(obj2)) return false;
  if (Array.isArray(obj1)) {
    if (obj1.length !== obj2.length) return false;
    return obj1.every((item, index) => deep_equal2(item, obj2[index], visited));
  }
  if (typeof obj1 === "object") {
    if (visited.has(obj1)) return visited.get(obj1) === obj2;
    visited.set(obj1, obj2);
    const keys1 = Object.keys(obj1);
    const keys2 = Object.keys(obj2);
    if (keys1.length !== keys2.length) return false;
    return keys1.every((key) => deep_equal2(obj1[key], obj2[key], visited));
  }
  return obj1 === obj2;
}

// node_modules/smart-collections/utils/get_item_display_name.js
function get_item_display_name2(key, show_full_path) {
  if (show_full_path) {
    return key.split("/").join(" > ").replace(".md", "");
  }
  return key.split("/").pop().replace(".md", "");
}

// node_modules/smart-collections/utils/create_actions_proxy.js
function create_actions_proxy2(ctx, actions_source) {
  const input = actions_source || {};
  const is_plain_object7 = (val) => typeof val === "object" && val !== null && !Array.isArray(val);
  const is_function = (val) => typeof val === "function";
  const is_class_export = (val) => is_function(val) && /^class\s/.test(Function.prototype.toString.call(val));
  const is_action_object = (val) => is_plain_object7(val) && is_function(val.action);
  const is_action_candidate = (val) => is_function(val) || is_action_object(val) || is_class_export(val);
  const ignored_meta_keys = /* @__PURE__ */ new Set(["length", "name", "prototype"]);
  const clone_with_descriptors = (obj) => {
    if (!is_plain_object7(obj)) return obj;
    const out = Object.create(Object.getPrototypeOf(obj) || null);
    for (const key of Reflect.ownKeys(obj)) {
      const descriptor = Object.getOwnPropertyDescriptor(obj, key);
      if (!descriptor) continue;
      const next = { ...descriptor };
      if ("value" in next && is_plain_object7(next.value)) {
        next.value = clone_with_descriptors(next.value);
      }
      try {
        Object.defineProperty(out, key, next);
      } catch {
        out[key] = next.value;
      }
    }
    return out;
  };
  const should_bucket_actions = (val) => {
    if (!is_plain_object7(val)) return false;
    if (is_action_object(val)) return false;
    const keys = Reflect.ownKeys(val);
    if (keys.length === 0) return false;
    let found_candidate = false;
    for (const key of keys) {
      const descriptor = Object.getOwnPropertyDescriptor(val, key);
      if (!descriptor) continue;
      if ("value" in descriptor) {
        const entry = descriptor.value;
        if (is_action_candidate(entry)) {
          found_candidate = true;
          continue;
        }
        if (is_plain_object7(entry)) {
          if (should_bucket_actions(entry)) {
            found_candidate = true;
            continue;
          }
          return false;
        }
        if (typeof entry === "undefined") continue;
        return false;
      }
      return false;
    }
    return found_candidate;
  };
  const clone_descriptor = (descriptor) => {
    if (!descriptor) return descriptor;
    if (!("value" in descriptor)) return { ...descriptor };
    const cloned = is_plain_object7(descriptor.value) ? clone_with_descriptors(descriptor.value) : descriptor.value;
    return { ...descriptor, value: cloned };
  };
  const build_sources = (src) => {
    const global_source2 = /* @__PURE__ */ Object.create(null);
    const scoped_sources2 = /* @__PURE__ */ new Map();
    for (const key of Reflect.ownKeys(src)) {
      const descriptor = Object.getOwnPropertyDescriptor(src, key);
      if (!descriptor) continue;
      if ("value" in descriptor && should_bucket_actions(descriptor.value)) {
        scoped_sources2.set(key, clone_with_descriptors(descriptor.value));
        continue;
      }
      try {
        Object.defineProperty(global_source2, key, clone_descriptor(descriptor));
      } catch {
        global_source2[key] = descriptor.value;
      }
    }
    return { global_source: global_source2, scoped_sources: scoped_sources2 };
  };
  const { global_source, scoped_sources } = build_sources(input);
  const has_own = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
  const cache = /* @__PURE__ */ Object.create(null);
  const copy_metadata = (source, target, omit = []) => {
    if (!source || !target) return;
    const skips = /* @__PURE__ */ new Set([...ignored_meta_keys, ...omit]);
    for (const key of Reflect.ownKeys(source)) {
      if (skips.has(key)) continue;
      const descriptor = Object.getOwnPropertyDescriptor(source, key);
      if (!descriptor) continue;
      try {
        Object.defineProperty(target, key, descriptor);
      } catch {
        target[key] = descriptor.value;
      }
    }
  };
  const instantiate_class = (Ctor) => {
    const instance = new Ctor(ctx);
    const candidate = instance.action || instance.run || instance.execute || instance.call;
    if (is_function(candidate)) {
      const bound = candidate.bind(instance);
      copy_metadata(Ctor, bound);
      copy_metadata(instance, bound);
      bound.instance = instance;
      return bound;
    }
    copy_metadata(Ctor, instance);
    return instance;
  };
  const bind_or_clone = (val) => {
    if (is_class_export(val)) {
      return instantiate_class(val);
    }
    if (is_action_object(val)) {
      const bound = val.action.bind(ctx);
      copy_metadata(val, bound, ["action"]);
      return bound;
    }
    if (is_function(val)) {
      const bound = val.bind(ctx);
      copy_metadata(val, bound);
      return bound;
    }
    if (is_plain_object7(val)) {
      return clone_with_descriptors(val);
    }
    return val;
  };
  const scope_actions_for = () => {
    const scope_key = ctx?.constructor?.key;
    if (typeof scope_key === "undefined" || scope_key === null) return null;
    const bucket = scoped_sources.get(scope_key);
    return bucket && is_plain_object7(bucket) ? bucket : null;
  };
  const cache_result = (target, prop, value) => {
    target[prop] = value;
    return value;
  };
  const compute_and_cache = (target, prop) => {
    const scoped = scope_actions_for();
    if (scoped && has_own(scoped, prop)) {
      return cache_result(target, prop, bind_or_clone(scoped[prop]));
    }
    if (has_own(global_source, prop)) {
      return cache_result(target, prop, bind_or_clone(global_source[prop]));
    }
    return cache_result(target, prop, void 0);
  };
  const union_keys = () => {
    const scoped = scope_actions_for();
    const keys = new Set(Reflect.ownKeys(cache));
    for (const key of Reflect.ownKeys(global_source)) {
      keys.add(key);
    }
    if (scoped) {
      for (const key of Reflect.ownKeys(scoped)) {
        keys.add(key);
      }
    }
    return Array.from(keys);
  };
  const descriptor_for = (target, prop) => ({
    configurable: true,
    enumerable: true,
    value: target[prop]
  });
  return new Proxy(cache, {
    get: (target, prop) => {
      if (prop === Symbol.toStringTag) return "ActionsProxy";
      if (prop in target) return target[prop];
      return compute_and_cache(target, prop);
    },
    has: (target, prop) => {
      if (prop in target) return true;
      const scoped = scope_actions_for();
      if (scoped && has_own(scoped, prop)) return true;
      return has_own(global_source, prop);
    },
    ownKeys: () => union_keys(),
    getOwnPropertyDescriptor: (target, prop) => {
      if (has_own(target, prop)) {
        return descriptor_for(target, prop);
      }
      const scoped = scope_actions_for();
      if (scoped && has_own(scoped, prop)) {
        if (!has_own(target, prop)) {
          compute_and_cache(target, prop);
        }
        return descriptor_for(target, prop);
      }
      if (has_own(global_source, prop)) {
        if (!has_own(target, prop)) {
          compute_and_cache(target, prop);
        }
        return descriptor_for(target, prop);
      }
      return void 0;
    },
    defineProperty: (target, prop, descriptor) => {
      if ("value" in descriptor) {
        target[prop] = descriptor.value;
        return true;
      }
      return false;
    },
    set: (target, prop, value) => {
      target[prop] = value;
      return true;
    },
    deleteProperty: (target, prop) => {
      if (has_own(target, prop)) {
        delete target[prop];
      }
      return true;
    }
  });
}

// node_modules/smart-collections/item.js
var CollectionItem2 = class _CollectionItem {
  static version = 2e-3;
  /**
   * Default properties for an instance of CollectionItem.
   * Override in subclasses to define different defaults.
   * @returns {Object}
   */
  static get defaults() {
    return {
      data: {}
    };
  }
  /**
   * @param {Object} env - The environment/context.
   * @param {Object|null} [data=null] - Initial data for the item.
   */
  constructor(env, data = null) {
    env.create_env_getter(this);
    this.config = this.env?.config;
    this.merge_defaults();
    if (data) deep_merge2(this.data, data);
    if (!this.data.class_name) this.data.class_name = this.collection.item_class_name;
  }
  /**
   * Loads an item from data and initializes it.
   * @param {Object} env
   * @param {Object} data
   * @returns {CollectionItem}
   */
  static load(env, data) {
    const item = new this(env, data);
    item.init();
    return item;
  }
  /**
   * Merge default properties from the entire inheritance chain.
   * @private
   */
  merge_defaults() {
    let current_class = this.constructor;
    while (current_class) {
      for (let key in current_class.defaults) {
        const default_val = current_class.defaults[key];
        if (typeof default_val === "object") {
          this[key] = { ...default_val, ...this[key] };
        } else {
          this[key] = this[key] === void 0 ? default_val : this[key];
        }
      }
      current_class = Object.getPrototypeOf(current_class);
    }
  }
  /**
   * Generates or retrieves a unique key for the item.
   * Key syntax supports:
   * - `[i]` for sequences
   * - `/` for super-sources (groups, directories, clusters)
   * - `#` for sub-sources (blocks)
   * @returns {string} The unique key
   */
  get_key() {
    return create_uid2(this.data);
  }
  /**
   * Updates the item data and returns true if changed.
   * @param {Object} data
   * @returns {boolean} True if data changed.
   */
  update_data(data) {
    const sanitized_data = this.sanitize_data(data);
    const current_data = { ...this.data };
    deep_merge2(current_data, sanitized_data);
    const changed = !deep_equal2(this.data, current_data);
    if (!changed) return false;
    this.data = current_data;
    return true;
  }
  /**
   * Sanitizes data for saving. Ensures no circular references.
   * @param {*} data
   * @returns {*} Sanitized data.
   */
  sanitize_data(data) {
    if (data instanceof _CollectionItem) return data.ref;
    if (Array.isArray(data)) return data.map((val) => this.sanitize_data(val));
    if (typeof data === "object" && data !== null) {
      return Object.keys(data).reduce((acc, key) => {
        acc[key] = this.sanitize_data(data[key]);
        return acc;
      }, {});
    }
    return data;
  }
  /**
   * Initializes the item. Override as needed.
   * @param {Object} [input_data] - Additional data that might be provided on creation.
   */
  init(input_data) {
  }
  /**
   * Queues this item for saving.
   */
  queue_save() {
    this._queue_save = true;
  }
  /**
   * Saves this item using its data adapter.
   * @returns {Promise<void>}
   */
  async save() {
    try {
      await this.data_adapter.save_item(this);
      this.init();
    } catch (err) {
      this._queue_save = true;
      console.error(err, err.stack);
    }
  }
  /**
   * Queues this item for loading.
   */
  queue_load() {
    this._queue_load = true;
  }
  /**
   * Loads this item using its data adapter.
   * @returns {Promise<void>}
   */
  async load() {
    try {
      await this.data_adapter.load_item(this);
      this.init();
    } catch (err) {
      this._load_error = err;
      this.on_load_error(err);
    }
  }
  /**
   * Handles load errors by re-queuing for load.
   * Override if needed.
   * @param {Error} err
   */
  on_load_error(err) {
    this.queue_load();
  }
  /**
   * Validates the item before saving. Checks for presence and validity of key.
   * @deprecated should be better handled 2025-12-17 (wrong scope?)
   * @returns {boolean}
   */
  validate_save() {
    if (!this.key) return false;
    if (this.key.trim() === "") return false;
    if (this.key === "undefined") return false;
    return true;
  }
  /**
   * Marks this item as deleted. This does not immediately remove it from memory,
   * but queues a save that will result in the item being removed from persistent storage.
   */
  delete() {
    this.deleted = true;
    this.queue_save();
  }
  /**
   * Filters items in the collection based on provided options.
   * functional filter (returns true or false) for filtering items in collection; called by collection class
   * @param {Object} filter_opts - Filtering options.
   * @param {string} [filter_opts.exclude_key] - A single key to exclude.
   * @param {string[]} [filter_opts.exclude_keys] - An array of keys to exclude. If exclude_key is provided, it's added to this array.
   * @param {string} [filter_opts.exclude_key_starts_with] - Exclude keys starting with this string.
   * @param {string[]} [filter_opts.exclude_key_starts_with_any] - Exclude keys starting with any of these strings.
   * @param {string} [filter_opts.exclude_key_includes] - Exclude keys that include this string.
   * @param {string[]} [filter_opts.exclude_key_includes_any] - Exclude keys that include any of these strings.
   * @param {string} [filter_opts.exclude_key_ends_with] - Exclude keys ending with this string.
   * @param {string[]} [filter_opts.exclude_key_ends_with_any] - Exclude keys ending with any of these strings.
   * @param {string} [filter_opts.key_ends_with] - Include only keys ending with this string.
   * @param {string} [filter_opts.key_starts_with] - Include only keys starting with this string.
   * @param {string[]} [filter_opts.key_starts_with_any] - Include only keys starting with any of these strings.
   * @param {string} [filter_opts.key_includes] - Include only keys that include this string.
   * @returns {boolean} True if the item passes the filter, false otherwise.
   */
  filter(filter_opts = {}) {
    const {
      exclude_key,
      exclude_keys = exclude_key ? [exclude_key] : [],
      exclude_key_starts_with,
      exclude_key_starts_with_any,
      exclude_key_includes,
      exclude_key_includes_any,
      exclude_key_ends_with,
      exclude_key_ends_with_any,
      key_ends_with,
      key_starts_with,
      key_starts_with_any,
      key_includes,
      key_includes_any
    } = filter_opts;
    if (exclude_keys?.includes(this.key)) return false;
    if (exclude_key_starts_with && this.key.startsWith(exclude_key_starts_with)) return false;
    if (exclude_key_starts_with_any && exclude_key_starts_with_any.some((prefix) => this.key.startsWith(prefix))) return false;
    if (exclude_key_includes && this.key.includes(exclude_key_includes)) return false;
    if (exclude_key_includes_any && exclude_key_includes_any.some((include) => this.key.includes(include))) return false;
    if (exclude_key_ends_with && this.key.endsWith(exclude_key_ends_with)) return false;
    if (exclude_key_ends_with_any && exclude_key_ends_with_any.some((suffix) => this.key.endsWith(suffix))) return false;
    if (key_ends_with && !this.key.endsWith(key_ends_with)) return false;
    if (key_starts_with && !this.key.startsWith(key_starts_with)) return false;
    if (key_starts_with_any && !key_starts_with_any.some((prefix) => this.key.startsWith(prefix))) return false;
    if (key_includes && !this.key.includes(key_includes)) return false;
    if (key_includes_any && !key_includes_any.some((include) => this.key.includes(include))) return false;
    return true;
  }
  filter_and_score(params = {}) {
    if (this.filter(params.filter) === false) return null;
    return this.score(params);
  }
  score(params = {}) {
    const score_action = this.actions[params.score_algo_key];
    if (typeof score_action !== "function") throw new Error(`Missing score action: ${params.score_algo_key}`);
    return {
      ...score_action(params) || {},
      item: this
    };
  }
  get actions() {
    if (!this._actions) {
      this._actions = create_actions_proxy2(this, {
        ...this.env.config.actions || {},
        // main actions scope for actions/ exports
        ...this.env.opts.items?.[this.item_type_key]?.actions || {}
        // DEPRECATED OR KEEP?
      });
    }
    return this._actions;
  }
  /**
   * Derives the collection key from the class name.
   * @returns {string}
   */
  static get collection_key() {
    let name = this.name;
    if (name.match(/\d$/)) name = name.slice(0, -1);
    return collection_instance_name_from2(name);
  }
  /**
   * @returns {string} The collection key for this item.
   */
  get collection_key() {
    let name = this.constructor.name;
    if (name.match(/\d$/)) name = name.slice(0, -1);
    return collection_instance_name_from2(name);
  }
  /**
   * Retrieves the parent collection from the environment.
   * @returns {Collection}
   */
  get collection() {
    return this.env[this.collection_key];
  }
  /**
   * @returns {string} The item's key.
   */
  get key() {
    return this.data?.key || this.get_key();
  }
  get item_type_key() {
    let name = this.constructor.name;
    if (name.match(/\d$/)) name = name.slice(0, -1);
    return camel_case_to_snake_case2(name);
  }
  /**
   * Emits an event with item metadata.
   *
   * @param {string} event_key
   * @param {Object} [payload={}]
   * @returns {void}
   */
  emit_event(event_key, payload = {}) {
    this.env.events?.emit(event_key, { collection_key: this.collection_key, item_key: this.key, ...payload });
  }
  emit_info_event(event_key, payload = {}) {
    this.emit_event(event_key, { level: "info", ...payload });
  }
  emit_error_event(event_key, payload = {}) {
    this.emit_event(event_key, { level: "error", ...payload });
  }
  on_event(event_key, callback) {
    return this.env.events?.on(event_key, (payload) => {
      if (payload?.item_key && payload.item_key !== this.key) return;
      callback(payload);
    });
  }
  once_event(event_key, callback) {
    return this.env.events?.once(event_key, (payload) => {
      if (payload?.item_key && payload.item_key !== this.key) return;
      callback(payload);
    });
  }
  /**
   * @returns {Object} The data adapter for this item's collection.
   */
  get data_adapter() {
    return this.collection.data_adapter;
  }
  /**
   * @returns {Object} The filesystem adapter.
   */
  get data_fs() {
    return this.collection.data_fs;
  }
  /**
   * Access to collection-level settings.
   * @returns {Object}
   */
  get settings() {
    if (!this.env.settings[this.collection_key]) this.env.settings[this.collection_key] = {};
    return this.env.settings[this.collection_key];
  }
  set settings(settings) {
    this.env.settings[this.collection_key] = settings;
    this.env.smart_settings.save();
  }
  /**
   * A simple reference object for this item.
   * @deprecated 2025-11-11 lacks adoption
   * @returns {{collection_key: string, key: string}}
   */
  get ref() {
    return { collection_key: this.collection_key, key: this.key };
  }
  /**
   * @deprecated use env.smart_components~~env.smart_view~~ instead
   */
  get smart_view() {
    if (!this._smart_view) this._smart_view = this.env.init_module("smart_view");
    return this._smart_view;
  }
  /**
   * Retrieves the display name of the collection item.
   * @readonly
   * @deprecated Use `get_item_display_name(key, show_full_path)` instead (keep UI logic out of collections).
   * @returns {string} The display name.
   */
  get name() {
    return get_item_display_name2(
      this.key,
      this.env.settings.smart_view_filter?.show_full_path
    );
  }
};

// node_modules/smart-collections/collection.js
var AsyncFunction2 = Object.getPrototypeOf(async function() {
}).constructor;
var Collection2 = class {
  static version = 1e-3;
  /**
   * Constructs a new Collection instance.
   *
   * @param {Object} env - The environment context containing configurations and adapters.
   * @param {Object} [opts={}] - Optional configuration.
   * @param {string} [opts.collection_key] - Custom key to override default collection name.
   * @param {string} [opts.data_dir] - Custom data directory path.
   */
  constructor(env, opts = {}) {
    env.create_env_getter(this);
    this.opts = opts;
    if (opts.collection_key) this.collection_key = opts.collection_key;
    this.env[this.collection_key] = this;
    this.config = this.env.config;
    this.items = {};
    this.loaded = null;
    this._loading = false;
    this.load_time_ms = null;
    this.settings_container = null;
  }
  /**
   * Initializes a new collection in the environment. Override in subclass if needed.
   *
   * @param {Object} env
   * @param {Object} [opts={}]
   * @returns {Promise<void>}
   */
  static async init(env, opts = {}) {
    env[this.collection_key] = new this(env, opts);
    await env[this.collection_key].init();
    env.collections[this.collection_key] = "init";
  }
  /**
   * The unique collection key derived from the class name.
   * @returns {string}
   */
  static get collection_key() {
    let name = this.name;
    if (name.match(/\d$/)) name = name.slice(0, -1);
    return name.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase();
  }
  /**
   * Instance-level init. Override in subclasses if necessary.
   * @returns {Promise<void>}
   */
  async init() {
  }
  /**
   * Creates or updates an item in the collection.
   * - If `data` includes a key that matches an existing item, that item is updated.
   * - Otherwise, a new item is created.
   * After updating or creating, the item is validated. If validation fails, the item is logged and returned without being saved.
   * If validation succeeds for a new item, it is added to the collection and marked for saving.
   *
   * If the item’s `init()` method is async, a promise is returned that resolves once init completes.
   * 
   * NOTE: wrapping in try/catch seems to fail to catch errors thrown in async init functions when awaiting create_or_update
   *
   * @param {Object} [data={}] - Data for creating/updating an item.
   * @returns {Promise<Item>|Item} The created or updated item. May return a promise if `init()` is async.
   */
  create_or_update(data = {}) {
    const existing_item = this.find_by(data);
    const item = existing_item ? existing_item : new this.item_type(this.env);
    item._queue_save = !existing_item;
    const data_changed = item.update_data(data);
    if (!existing_item && !item.validate_save()) {
      return item;
    }
    if (!existing_item) {
      this.set(item);
    }
    if (existing_item && !data_changed) return existing_item;
    if (item.init instanceof AsyncFunction2) {
      return new Promise((resolve) => {
        item.init(data).then(() => resolve(item));
      });
    }
    item.init(data);
    return item;
  }
  /**
   * Finds an item by partial data match (first checks key). If `data.key` provided,
   * returns the item with that key; otherwise attempts a match by merging data.
   *
   * @param {Object} data - Data to match against.
   * @returns {Item|null}
   */
  find_by(data) {
    if (data.key) return this.get(data.key);
    const temp = new this.item_type(this.env);
    const temp_data = JSON.parse(JSON.stringify(data, temp.sanitize_data(data)));
    deep_merge2(temp.data, temp_data);
    return temp.key ? this.get(temp.key) : null;
  }
  /**
   * Filters items based on provided filter options or a custom function.
   *
   * @param {Object|Function} [filter_opts={}] - Filter options or a predicate function.
   * @returns {Item[]} Array of filtered items.
   */
  filter(filter_opts = {}) {
    if (typeof filter_opts === "function") {
      return Object.values(this.items).filter(filter_opts);
    }
    const results = [];
    const { first_n } = filter_opts;
    for (const item of Object.values(this.items)) {
      if (first_n && results.length >= first_n) break;
      if (item.filter(filter_opts)) results.push(item);
    }
    return results;
  }
  /**
   * Alias for `filter()`
   * @param {Object|Function} filter_opts
   * @returns {Item[]}
   */
  list(filter_opts) {
    return this.filter(filter_opts);
  }
  /**
   * Retrieves an item by key.
   * @param {string} key
   * @returns {Item|undefined}
   */
  get(key) {
    return this.items[key];
  }
  /**
   * Retrieves multiple items by an array of keys.
   * @param {string[]} keys
   * @returns {Item[]}
   */
  get_many(keys = []) {
    if (!Array.isArray(keys)) {
      console.error("get_many called with non-array keys:", keys);
      return [];
    }
    return keys.map((key) => this.get(key)).filter(Boolean);
  }
  /**
   * Retrieves a random item from the collection, optionally filtered by options.
   * @param {Object} [opts]
   * @returns {Item|undefined}
   */
  get_rand(opts = null) {
    if (opts) {
      const filtered = this.filter(opts);
      return filtered[Math.floor(Math.random() * filtered.length)];
    }
    const keys = this.keys;
    return this.items[keys[Math.floor(Math.random() * keys.length)]];
  }
  /**
   * Adds or updates an item in the collection.
   * @param {Item} item
   */
  set(item) {
    if (!item.key) throw new Error("Item must have a key property");
    this.items[item.key] = item;
  }
  /**
   * Updates multiple items by their keys.
   * @param {string[]} keys
   * @param {Object} data
   */
  update_many(keys = [], data = {}) {
    this.get_many(keys).forEach((item) => item.update_data(data));
  }
  /**
   * Clears all items from the collection.
   */
  clear() {
    this.items = {};
  }
  /**
   * @returns {string} The collection key, can be overridden by opts.collection_key
   */
  get collection_key() {
    return this._collection_key ? this._collection_key : this.constructor.collection_key;
  }
  set collection_key(key) {
    this._collection_key = key;
  }
  /**
   * Lazily initializes and returns the data adapter instance for this collection.
   * @returns {Object} The data adapter instance.
   */
  get data_adapter() {
    if (!this._data_adapter) {
      const AdapterClass = this.get_adapter_class("data");
      this._data_adapter = new AdapterClass(this);
    }
    return this._data_adapter;
  }
  /**
   * @private
   * @param {string} type
   * @returns {Function}
   */
  get_adapter_class(type) {
    const config = this.env.opts.collections?.[this.collection_key];
    const adapter_key = type + "_adapter";
    const adapter_module = config?.[adapter_key] ?? this.env.opts.collections?.smart_collections?.[adapter_key];
    if (typeof adapter_module === "function") return adapter_module;
    if (typeof adapter_module?.collection === "function") return adapter_module.collection;
    throw new Error(`No '${type}' adapter class found for ${this.collection_key} or smart_collections`);
  }
  /**
   * Data directory strategy for this collection. Defaults to 'multi'.
   * @deprecated should be handled in adapters (2025-12-09)
   * @returns {string}
   */
  get data_dir() {
    return this.collection_key;
  }
  /**
   * File system adapter from the environment.
   * @returns {Object}
   */
  get data_fs() {
    return this.env.data_fs;
  }
  /**
   * Derives the corresponding item class name based on this collection's class name.
   * @returns {string}
   */
  get item_class_name() {
    let name = this.constructor.name;
    if (name.match(/\d$/)) name = name.slice(0, -1);
    if (name.endsWith("ies")) return name.slice(0, -3) + "y";
    else if (name.endsWith("s")) return name.slice(0, -1);
    return name + "Item";
  }
  /**
   * Derives a readable item name from the item class name.
   * @returns {string}
   */
  get item_name() {
    return this.item_class_name.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase();
  }
  /**
   * Retrieves the item type (constructor) from the environment.
   * @deprecated replace with item_class with strict adherence to conventions (2025-10-28)
   * @returns {Function} Item constructor.
   */
  get item_type() {
    if (!this._item_type) this._item_type = this.resolve_item_type();
    return this._item_type;
  }
  // TEMP resolver (2025-11-03): until better handled on merging configs at obsidian-smart-env startup
  /**
   * @private
   * @returns {Function}
   */
  resolve_item_type() {
    const available = [
      this.env.config?.items?.[this.item_name],
      this.opts.item_type
      // Is this necessary? (2026-04-11 needs review - ideally should be able to rely on env.config for this)
    ].filter(Boolean).sort((a, b) => {
      const a_version = a?.class?.version || a.version || 0;
      const b_version = b?.class?.version || b.version || 0;
      return b_version - a_version;
    });
    if (available.length === 0) {
      throw new Error(`No item_type found for collection '${this.collection_key}' with item_name '${this.item_name}' or class_name '${this.item_class_name}'`);
    }
    return available[0].class || available[0];
  }
  /**
   * Returns an array of all keys in the collection.
   * @returns {string[]}
   */
  get keys() {
    return Object.keys(this.items);
  }
  /**
   * @deprecated use data_adapter instead (2024-09-14)
   */
  get adapter() {
    return this.data_adapter;
  }
  /**
   * @method process_save_queue
   * @description 
   * Saves items flagged for saving (_queue_save) back to AJSON or SQLite. This ensures persistent storage 
   * of any updates made since last load/import. This method also writes changes to disk (AJSON files or DB).
   */
  async process_save_queue(opts = {}) {
    if (opts.force) {
      Object.values(this.items).forEach((item) => item._queue_save = true);
    }
    await this.data_adapter.process_save_queue(opts);
  }
  /**
   * @alias process_save_queue
   * @returns {Promise<void>}
   */
  async save(opts = {}) {
    await this.process_save_queue(opts);
  }
  /**
   * @method process_load_queue
   * @description 
   * Loads items that have been flagged for loading (_queue_load). This may involve 
   * reading from AJSON/SQLite or re-importing from markdown if needed. 
   * Called once initial environment is ready and collections are known.
   */
  async process_load_queue() {
    await this.data_adapter.process_load_queue();
  }
  /**
   * Retrieves processed settings configuration.
   * @returns {Object}
   */
  get settings_config() {
    return this.process_settings_config({});
  }
  /**
   * Processes given settings config, adding prefixes and handling conditionals.
   * @deprecated removing settings_config from collections (2025-11-24)
   *
   * @private
   * @param {Object} _settings_config
   * @param {string} [prefix='']
   * @returns {Object}
   */
  process_settings_config(_settings_config, prefix = "") {
    const add_prefix = (key) => prefix && !key.includes(`${prefix}.`) ? `${prefix}.${key}` : key;
    return Object.entries(_settings_config).reduce((acc, [key, val]) => {
      let new_val = { ...val };
      if (new_val.conditional) {
        if (!new_val.conditional(this)) return acc;
        delete new_val.conditional;
      }
      if (new_val.callback) new_val.callback = add_prefix(new_val.callback);
      if (new_val.btn_callback) new_val.btn_callback = add_prefix(new_val.btn_callback);
      if (new_val.options_callback) new_val.options_callback = add_prefix(new_val.options_callback);
      const new_key = add_prefix(this.process_setting_key(key));
      acc[new_key] = new_val;
      return acc;
    }, {});
  }
  /**
   * Processes an individual setting key. Override if needed.
   * @param {string} key
   * @returns {string}
   */
  process_setting_key(key) {
    return key;
  }
  /**
   * Default settings for this collection. Override in subclasses as needed.
   * @returns {Object}
   */
  get default_settings() {
    return {};
  }
  /**
   * Current settings for the collection.
   * Initializes with default settings if none exist.
   * @returns {Object}
   */
  get settings() {
    if (!this.env.settings[this.collection_key]) {
      this.env.settings[this.collection_key] = this.default_settings;
    }
    return this.env.settings[this.collection_key];
  }
  /**
   * Unloads collection data from memory.
   */
  unload() {
    this.clear();
    this.unloaded = true;
    this.env.collections[this.collection_key] = null;
  }
  /**
   * Emits an event with collection metadata.
   *
   * @param {string} event_key
   * @param {Object} [payload={}]
   * @returns {void}
   */
  emit_event(event_key, payload = {}) {
    this.env.events?.emit(event_key, { collection_key: this.collection_key, ...payload });
  }
  emit_info_event(event_key, payload = {}) {
    this.emit_event(event_key, { level: "info", ...payload });
  }
  emit_warning_event(event_key, payload = {}) {
    this.emit_event(event_key, { level: "warning", ...payload });
  }
  emit_error_event(event_key, payload = {}) {
    this.emit_event(event_key, { level: "error", ...payload });
  }
  on_event(event_key, callback) {
    return this.env.events?.on(event_key, (payload) => {
      if (payload?.collection_key && payload.collection_key !== this.collection_key) return;
      callback(payload);
    });
  }
  /**
   * Lazily binds action functions to the collection instance.
   *
   * @returns {Object} Bound action functions keyed by name.
   */
  get actions() {
    if (!this.constructor.key) this.constructor.key = this.collection_key;
    if (!this._actions) {
      const actions_modules = {
        ...this.env?.config?.actions || {},
        ...this.env?.config?.collections?.[this.collection_key]?.actions || {},
        ...this.env?.opts?.collections?.[this.collection_key]?.actions || {},
        ...this.opts?.actions || {}
      };
      this._actions = create_actions_proxy2(this, actions_modules);
    }
    return this._actions;
  }
  /**
   * Clears cached actions proxy and rebuilds on next access.
   * @returns {Object} Rebuilt proxy with latest source snapshot.
   */
  refresh_actions() {
    this._actions = null;
    return this.actions;
  }
  // debounce running process save queue
  queue_save() {
    if (this._debounce_queue_save) clearTimeout(this._debounce_queue_save);
    this._debounce_queue_save = setTimeout(() => {
      this.process_save_queue();
    }, 750);
  }
  // BEGIN DEPRECATED
  /**
   * @deprecated use env.smart_components~~env.smart_view~~ instead (2026-02-11)
   * @returns {Object} smart_view instance
   */
  get smart_view() {
    if (!this._smart_view) this._smart_view = this.env.init_module("smart_view");
    return this._smart_view;
  }
};

// node_modules/smart-entities/utils/frontmatter_filter.js
var to_string2 = (value) => `${value ?? ""}`.trim();
var to_lower2 = (value) => to_string2(value).toLowerCase();
var to_lines = (value = "") => {
  if (Array.isArray(value)) return value;
  return to_string2(value).split("\n");
};
function parse_frontmatter_filter_lines(value = "") {
  return to_lines(value).map((line) => to_string2(line)).filter((line) => line.length).map((line) => {
    const separator_index = line.indexOf(":");
    if (separator_index === -1) {
      return { key: to_lower2(line), value: null };
    }
    const key = to_lower2(line.slice(0, separator_index));
    const entry_value = to_string2(line.slice(separator_index + 1));
    return { key, value: entry_value.length ? to_lower2(entry_value) : null };
  }).filter((entry) => entry.key.length);
}

// node_modules/smart-utils/results_acc.js
function results_acc2(_acc, result, ct = 10) {
  if (_acc.results.size < ct) {
    _acc.results.add(result);
    if (_acc.results.size === ct && _acc.min === Number.POSITIVE_INFINITY) {
      let { minScore, minObj } = find_min2(_acc.results);
      _acc.min = minScore;
      _acc.minResult = minObj;
    }
  } else if (result.score > _acc.min) {
    _acc.results.add(result);
    _acc.results.delete(_acc.minResult);
    let { minScore, minObj } = find_min2(_acc.results);
    _acc.min = minScore;
    _acc.minResult = minObj;
  }
}
function find_min2(results) {
  let minScore = Number.POSITIVE_INFINITY;
  let minObj = null;
  for (const obj of results) {
    if (obj.score < minScore) {
      minScore = obj.score;
      minObj = obj;
    }
  }
  return { minScore, minObj };
}

// node_modules/smart-utils/sort_by_score.js
function sort_by_score2(a, b) {
  const epsilon = 1e-9;
  const score_diff = a.score - b.score;
  if (Math.abs(score_diff) < epsilon) return 0;
  return score_diff > 0 ? -1 : 1;
}
function sort_by_score_descending2(a, b) {
  return sort_by_score2(a, b);
}

// src/utils/merge_pinned_results.js
function merge_pinned_results(base_results, params) {
  if (!params.pinned?.length) return base_results;
  const pinned_keys = new Set(params.pinned_keys || params.pinned.map((item) => item.key));
  const pinned_results = params.pinned.map((item) => ({
    item,
    ...item.score?.(params) || {}
  }));
  const filtered_results = base_results.filter((result) => {
    const key = result?.item?.key;
    return key ? !pinned_keys.has(key) : true;
  });
  return [...pinned_results, ...filtered_results];
}

// migrations/migrate_hidden_connections.js
var BLOCK_KEY_MARKER = "#";
var COLLECTION_SEPARATOR = ":";
var SOURCE_COLLECTION = "smart_sources";
var BLOCK_COLLECTION = "smart_blocks";
function is_plain_object6(value) {
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}
function select_hidden_entries(hidden_connections) {
  if (!is_plain_object6(hidden_connections)) return [];
  return Object.entries(hidden_connections).filter(([, timestamp]) => timestamp !== void 0 && timestamp !== null);
}
function get_collection_prefix(key) {
  return key.includes(BLOCK_KEY_MARKER) ? BLOCK_COLLECTION : SOURCE_COLLECTION;
}
function ensure_prefixed_key(key) {
  if (typeof key !== "string") return key;
  if (key.includes(COLLECTION_SEPARATOR)) return key;
  return `${get_collection_prefix(key)}${COLLECTION_SEPARATOR}${key}`;
}
function migrate_hidden_connections(source) {
  const hidden_entries = select_hidden_entries(source?.data?.hidden_connections);
  if (!hidden_entries.length) return false;
  if (!is_plain_object6(source.data.connections)) {
    source.data.connections = {};
  }
  let migrated = false;
  hidden_entries.forEach(([key, timestamp]) => {
    const prefixed_key = ensure_prefixed_key(key);
    if (typeof prefixed_key !== "string") return;
    const connection_state = source.data.connections[prefixed_key] || {};
    if (connection_state.hidden === void 0 || connection_state.hidden === null) {
      connection_state.hidden = timestamp;
      migrated = true;
    }
    source.data.connections[prefixed_key] = connection_state;
  });
  if (migrated) {
    delete source.data.hidden_connections;
  }
  return migrated || hidden_entries.length > 0;
}

// src/items/connections_list.js
var ConnectionsList = class extends CollectionItem2 {
  static key = "connections_list";
  static get defaults() {
    return { data: {} };
  }
  get_key() {
    return `${this.data.collection_key}:${this.data.item_key}`;
  }
  async pre_process(params) {
    migrate_hidden_connections(this.item);
    if (typeof this.actions.connections_list_pre_process === "function") {
      await this.actions.connections_list_pre_process(params);
    }
    if (typeof this.env.config?.actions?.[params.score_algo_key]?.pre_process === "function") {
      await this.env.config.actions[params.score_algo_key].pre_process.call(this.item, params);
    }
  }
  /**
   * Produce ranked connections for the current source item.
   * @param {object} params
   * @note cannot call with different params until promise resolves
   * @returns {Promise<Array>}
   */
  async get_results(params = {}) {
    if (this._results_promise) return this._results_promise;
    const p = this._get_results(params);
    this._results_promise = p;
    this._results_promise.finally(() => {
      if (this._results_promise === p) {
        this._results_promise = null;
      }
    });
    return this._results_promise;
  }
  async _get_results(params = {}) {
    await this.pre_process(params);
    if (this.env.log_perf) this.start_ms = Date.now();
    let results = this.filter_and_score(params);
    if (this.env.log_perf) {
      this.end_ms = Date.now();
      console.log(`filter_and_score(${params.score_algo_key}) took ${this.end_ms - this.start_ms} ms (Date.now)`);
    }
    results = await this.post_process(results, params);
    results = merge_pinned_results(results, params);
    results = results.map((r) => Object.assign(r, { connections_list: this }));
    this.results = results;
    return results;
  }
  filter_and_score(params = {}) {
    const collection = this.env[params.results_collection_key];
    const score_errors = [];
    const { results: raw_results } = Object.values(collection.items).reduce((acc, target) => {
      const scored = target.filter_and_score(params);
      if (!scored?.score) {
        if (scored?.error) score_errors.push(scored.error);
        return acc;
      }
      results_acc2(acc, scored, params.limit);
      return acc;
    }, { min: 0, results: /* @__PURE__ */ new Set() });
    const results = Array.from(raw_results).sort(sort_by_score_descending2);
    if (!results.length) return results;
    while (!results.some((r) => r.score > 0.5)) {
      results.forEach((r) => r.score *= 2);
    }
    return results;
  }
  async post_process(results, params = {}) {
    if (!results?.length) {
      console.warn("No results to post-process, received:", results);
      return [];
    }
    const action_key = this.settings.connections_post_process;
    const post_process_action = this.actions[action_key];
    let processed_results = results;
    if (typeof post_process_action === "function") {
      const response = await post_process_action(results, params);
      if (Array.isArray(response)) {
        processed_results = response.filter(Boolean);
        if (!processed_results.length) processed_results = results;
      } else if (response !== void 0 && response !== null) {
        console.warn(`connections post_process '${action_key}' returned non-array`, response);
      }
    } else if (action_key && action_key !== "none") {
      console.warn(`Post-process action "${action_key}" not found, falling back to base results.`);
    }
    return processed_results;
  }
  get item() {
    return this.env[this.data.collection_key]?.items[this.data.item_key];
  }
  get connections_list_component_key() {
    const stored_key = this.data.connections_list_component_key || this.settings?.connections_list_component_key;
    if (this.env.config.components[stored_key]) return stored_key;
    return "connections_list_v4";
  }
};

// migrations/migrate_connections_lists_settings.js
var MIGRATED_SETTING_KEYS = [
  "inline_connections",
  "inline_connections_score_threshold",
  "footer_connections"
];
function migrate_connections_lists_settings(env) {
  const settings = env?.settings;
  if (!settings) return false;
  const legacy = settings.connections_pro;
  if (!legacy) return false;
  const target = settings.connections_lists ||= {};
  let migrated = false;
  for (const key of MIGRATED_SETTING_KEYS) {
    if (!(key in legacy)) continue;
    if (!(key in target)) {
      target[key] = legacy[key];
      migrated = true;
    }
    delete legacy[key];
  }
  if ("rank_model" in legacy) {
    if (!target.actions) target.actions = {};
    if (!("rank_connections" in target.actions)) {
      target.actions.rank_connections = legacy.rank_model;
      migrated = true;
    }
    delete legacy.rank_model;
  }
  return migrated;
}

// src/utils/insert_settings_after.js
function insert_settings_after(anchor_key, config, merge_object) {
  const config_entries = Object.entries({ ...config });
  const anchor_i = config_entries.findIndex(([key]) => key === anchor_key);
  if (anchor_i !== -1) {
    if (merge_object && Object.keys(merge_object).length > 0) {
      const entries = Object.entries(merge_object);
      config_entries.splice(anchor_i + 1, 0, ...entries);
    }
  }
  return Object.fromEntries(config_entries);
}

// src/collections/connections_lists.js
var ConnectionsLists = class extends Collection2 {
  static version = 1;
  process_load_queue() {
  }
  // no persisting data (for now)
  constructor(env, opts = {}) {
    migrate_connections_lists_settings(env);
    super(env, opts);
  }
  static get default_settings() {
    return {
      results_collection_key: "smart_sources",
      score_algo_key: "similarity",
      connections_post_process: "none",
      results_limit: 20,
      connections_view_location: "right",
      exclude_frontmatter_blocks: true,
      connections_list_component_key: "connections_list_v4",
      connections_list_item_component_key: "connections_list_item_v3",
      frontmatter_filter_include: "",
      frontmatter_filter_exclude: "",
      components: {
        connections_list_v4: {
          show_graph: true
        },
        connections_list_item_v3: {
          render_markdown: true,
          show_full_path: false
        }
      }
    };
  }
  get settings_config() {
    return settings_config10(this);
  }
  new_item(item) {
    const connections_list = new this.item_type(this.env, {
      collection_key: item.collection_key,
      item_key: item.key
    });
    this.set(connections_list);
    Object.defineProperty(item, "connections", {
      get: () => connections_list,
      configurable: true
    });
    return connections_list;
  }
  get_connections_list_item_options() {
    return Object.entries(this.env.config.components || {}).filter(([key, fn]) => key.startsWith("connections_list_item_")).map(([value, fn]) => ({ value, name: fn.display_name || value, description: fn.display_description }));
  }
  get score_algo_key() {
    const stored_key = this.settings?.score_algo_key;
    if (this.env.config?.actions?.[stored_key]) return stored_key;
    return "similarity";
  }
  get results_collection_key() {
    const stored_key = this.settings?.results_collection_key;
    if (this.env[stored_key]) return stored_key;
    return "smart_sources";
  }
  get frontmatter_inclusions() {
    return parse_frontmatter_filter_lines(this.settings.frontmatter_filter_include);
  }
  get frontmatter_exclusions() {
    return parse_frontmatter_filter_lines(this.settings.frontmatter_filter_exclude);
  }
  get connections_list_component_settings_config() {
    if (!this.settings?.connections_list_component_key || !this.env.is_pro && ["none", "connections_list_v4_2", "connections_list_v3"].includes(this.settings.connections_list_component_key)) {
      this.settings.connections_list_component_key = "connections_list_v4";
    }
    if (!this.settings?.components?.connections_list_v4) {
      if (!this.settings.components) this.settings.components = {};
      this.settings.components.connections_list_v4 = { ...this.constructor.default_settings.components.connections_list_v4 };
    }
    const component_key = this.settings.connections_list_component_key;
    if (!component_key || component_key === "none") return null;
    const component_module = this.env.config.components?.[component_key];
    const config = typeof component_module?.settings_config === "function" ? component_module.settings_config(this) : component_module?.settings_config;
    if (!config) return null;
    return Object.fromEntries(
      Object.entries(config).map(([k, v]) => {
        return [`components.${component_key}.${k}`, v];
      })
    );
  }
};
function settings_config10(scope) {
  let config = {
    "results_collection_key": {
      name: "Connection results type",
      type: "dropdown",
      description: "Choose whether results should be sources or blocks.",
      option_1: "smart_sources|Sources",
      // DEPRECATED
      option_2: "smart_blocks|Blocks",
      // DEPRECATED
      options_callback: () => {
        const options = [
          { value: "smart_sources", name: "Sources" }
        ];
        if (scope.env.smart_blocks) {
          options.push({ value: "smart_blocks", name: "Blocks" });
        }
        return options;
      }
    },
    "results_limit": {
      name: "Results limit",
      type: "number",
      description: "Adjust the number of connections displayed in the connections view (default 20)."
    },
    "connections_view_location": {
      group: "Display",
      name: "Connections sidebar location",
      type: "dropdown",
      description: "Choose which sidebar opens when showing the Connections view.",
      option_1: "right|Right sidebar",
      // DEPRECATED
      option_2: "left|Left sidebar",
      // DEPRECATED
      options_callback: () => {
        return [
          { value: "right", name: "Right sidebar" },
          { value: "left", name: "Left sidebar" }
        ];
      }
    },
    "inline_connections": {
      group: "Inline connections",
      name: "Show inline connections",
      type: "toggle",
      scope_class: "pro-setting",
      description: "Shows connections for each block within the note. Hover connections icon to see list of connections."
    },
    "footer_connections": {
      group: "Footer connections",
      name: "Show footer connections",
      type: "toggle",
      description: "Show connections at the bottom of each note."
    },
    filters_helper: {
      group: "Connections filters",
      type: "html",
      value: [
        '<p class="setting-item-description"><strong>Filter tips:</strong> Use comma-separated folder or file path fragments such as <code>Projects/Clients</code>. Values are trimmed automatically and compared using case-sensitive substring matches.</p>',
        '<p class="setting-item-description"><strong>Result vs ingestion:</strong> Connections filters only hide results after Smart Environment builds its dataset. To stop notes from being indexed, adjust Smart Environment include/exclude settings in the Environment window or plugin settings.</p>',
        '<p class="setting-item-description"><strong>Precedence:</strong> Entries in the exclude filter always win when they match, even if the same path fragment appears in the include filter.</p>'
      ].join("")
    },
    "exclude_inlinks": {
      group: "Connections filters",
      name: "Exclude inlinks (backlinks)",
      type: "toggle",
      scope_class: "pro-setting",
      description: "Exclude notes that already link to the current note from the connections results."
    },
    "exclude_outlinks": {
      group: "Connections filters",
      name: "Exclude outlinks",
      type: "toggle",
      scope_class: "pro-setting",
      description: "Exclude notes that are already linked from within the current note from appearing in the connections results."
    },
    "include_filter": {
      group: "Connections filters",
      name: "Include filter",
      type: "text",
      scope_class: "pro-setting",
      description: "Comma-separated path fragments that must appear in the note path. Matches use case-sensitive substring checks; trim spaces or wrap folder names like `Daily/`. This only affects the results list; Smart Environment still embeds matching notes unless excluded there."
    },
    "exclude_filter": {
      group: "Connections filters",
      name: "Exclude filter",
      type: "text",
      scope_class: "pro-setting",
      description: "Comma-separated path fragments to omit from results. Exclusions run before includes, so any matching fragment removes the note even if it also appears in `Include filter`. Use Smart Environment include/exclude settings to stop notes from being embedded altogether."
    },
    "frontmatter_filter_include": {
      group: "Connections filters",
      name: "Frontmatter include filter",
      type: "text",
      scope_class: "pro-setting",
      description: "Newline-delimited frontmatter matchers (ex. status or status:open). Case-insensitive key and value matching."
    },
    "frontmatter_filter_exclude": {
      group: "Connections filters",
      name: "Frontmatter exclude filter",
      type: "text",
      scope_class: "pro-setting",
      description: "Newline-delimited frontmatter matchers removed from results. Exclude entries take precedence over include entries."
    },
    // hide frontmatter blocks from connections results
    "exclude_frontmatter_blocks": {
      group: "Connections filters",
      name: "Hide frontmatter blocks in results",
      type: "toggle",
      scope_class: "pro-setting",
      description: "Show only sources in the connections results (no frontmatter blocks)."
    }
  };
  if (!scope.env.smart_blocks.settings.embed_blocks) {
    config.results_collection_key = {
      type: "html",
      value: '<p>Enable "Embed blocks" in Smart Blocks settings to use block connections.</p>',
      name: "Connection results type"
    };
  }
  if (scope.connections_list_component_settings_config) {
    config = insert_settings_after("results_limit", config, scope.connections_list_component_settings_config);
  }
  return config;
}
var connections_lists_default = {
  class: ConnectionsLists,
  collection_key: "connections_lists",
  item_type: ConnectionsList,
  settings_config: settings_config10
};

// src/components/connections_codeblock.css
var connections_codeblock_default = ".connections-codeblock {\r\n  .connections-top-bar {\r\n    display: flex;\r\n    gap: 0.5rem 1rem;\r\n\r\n    .connections-actions {\r\n      display: flex;\r\n      flex-direction: row;\r\n      flex-wrap: wrap;\r\n      align-items: center;\r\n      gap: 0.5rem;\r\n      span {\r\n        font-weight: 600;\r\n        justify-self: end;\r\n      }\r\n    }\r\n  }\r\n  .connections-list {\r\n    padding-top: 0.85rem;\r\n\r\n    > .sc-collapsed ul {\r\n      display: none;\r\n    }\r\n    > .sc-collapsed span svg {\r\n      transform: rotate(-90deg);\r\n    }\r\n    > .sc-result-pinned {\r\n      box-shadow: 0 0 0 1px var(--interactive-accent);\r\n      border-radius: var(--radius-s);\r\n      background-color: var(--background-modifier-hover);\r\n    }\r\n  }\r\n}";

// node_modules/obsidian-smart-env/src/modals/story.js
var import_obsidian54 = require("obsidian");
var StoryModal = class _StoryModal extends import_obsidian54.Modal {
  constructor(plugin, { title, url }) {
    super(plugin.app);
    this.plugin = plugin;
    this.title = title;
    this.url = url;
  }
  static open(plugin, story_url) {
    const modal = new _StoryModal(plugin, story_url);
    modal.open();
  }
  onOpen() {
    this.titleEl.setText(this.title);
    this.modalEl.addClass("sc-story-modal");
    this.modalEl.style.width = "80%";
    this.modalEl.style.height = "80%";
    const container = this.contentEl.createEl("div", {
      cls: "sc-story-container"
    });
    container.style.display = "flex";
    container.style.flexDirection = "column";
    container.style.height = "100%";
    if (import_obsidian54.Platform.isMobile) {
      const btn = container.createEl("button", { text: "Open in browser" });
      btn.addEventListener("click", () => {
        window.open(this.url, "_external");
        this.close();
      });
      return;
    } else {
      const webview = container.createEl("webview", {
        attr: { src: this.url, allowpopups: "" }
      });
      webview.style.width = "100%";
      webview.style.height = "100%";
      webview.addEventListener("did-navigate", (event) => {
        const new_url = event.url || webview.getAttribute("src");
        if (new_url && new_url !== this.url) {
          window.open(new_url, "_external");
          this.close();
        }
      });
    }
  }
  onClose() {
    this.contentEl.empty();
  }
};

// src/utils/connections_context_items.js
function build_connections_context_items(params = {}) {
  const { source_item, results = [] } = params;
  const items = [];
  const seen_keys = /* @__PURE__ */ new Set();
  const append_item = (item, score) => {
    const key = item?.key;
    if (!key || seen_keys.has(key)) return;
    seen_keys.add(key);
    items.push({ key, score });
  };
  if (source_item) append_item(source_item, source_item.score ?? 1);
  results.forEach((result) => append_item(result?.item, result?.score));
  return items;
}

// src/utils/format_connections_as_links.js
function format_connections_as_links(results = []) {
  if (!Array.isArray(results) || !results.length) return "";
  return results.map(({ item }) => format_connection_item(item)).filter(Boolean).join("\n");
}
function format_connection_item(item) {
  if (!item?.key) return "";
  const link = get_wikilink(item);
  if (!link) return "";
  const lines = get_lines_label(item);
  if (!lines) return link;
  return `${link.replace(/\#\{\d+\}/, "")} (${lines})`;
}
function get_lines_label(item) {
  if (!item?.key?.endsWith("}")) return "";
  if (!Array.isArray(item.lines) || !item.lines.length) return "";
  return `Lines: ${item.lines.join("-")}`;
}
function get_wikilink(item) {
  if (!item?.key) return "";
  const [source_key, ...block_parts] = item.key.split("#");
  const filename = source_key.split("/").pop().replace(/\.md$/i, "");
  if (block_parts.length) {
    const block_ref = block_parts.pop();
    return `- [[${filename}#${block_ref}]]`;
  }
  return `- [[${filename}]]`;
}

// src/utils/connections_list_item_state.js
function build_prefixed_connection_key(collection_key, item_key) {
  if (typeof item_key !== "string" || !item_key.length) return item_key;
  if (item_key.includes(":")) return item_key;
  if (typeof collection_key !== "string" || !collection_key.length) return item_key;
  return `${collection_key}:${item_key}`;
}
function apply_hidden_state(connections, prefixed_key, hidden_at = Date.now()) {
  if (!connections || typeof connections !== "object") return connections;
  if (typeof prefixed_key !== "string" || !prefixed_key.length) return connections;
  const state = connections[prefixed_key] || {};
  state.hidden = hidden_at;
  connections[prefixed_key] = state;
  return connections;
}
function apply_pinned_state(connections, prefixed_key, pinned_at = Date.now()) {
  if (!connections || typeof connections !== "object") return connections;
  if (typeof prefixed_key !== "string" || !prefixed_key.length) return connections;
  const state = connections[prefixed_key] || {};
  state.pinned = pinned_at;
  connections[prefixed_key] = state;
  return connections;
}
function remove_pinned_state(connections, prefixed_key) {
  if (!connections || typeof connections !== "object") return connections;
  if (typeof prefixed_key !== "string" || !prefixed_key.length) return connections;
  const state = connections[prefixed_key];
  if (!state || typeof state !== "object") return connections;
  delete state.pinned;
  if (!Object.keys(state).length) delete connections[prefixed_key];
  return connections;
}
function remove_all_hidden_states(connections) {
  if (!connections || typeof connections !== "object") return false;
  let changed = false;
  Object.entries(connections).forEach(([key, state]) => {
    if (!state || typeof state !== "object") return;
    if (state.hidden === void 0 || state.hidden === null) return;
    delete state.hidden;
    if (!Object.keys(state).length) delete connections[key];
    changed = true;
  });
  return changed;
}
function remove_all_pinned_states(connections) {
  if (!connections || typeof connections !== "object") return false;
  let changed = false;
  Object.entries(connections).forEach(([key, state]) => {
    if (!state || typeof state !== "object") return;
    if (state.pinned === void 0 || state.pinned === null) return;
    delete state.pinned;
    if (!Object.keys(state).length) delete connections[key];
    changed = true;
  });
  return changed;
}
function count_hidden_connections(connections) {
  if (!connections || typeof connections !== "object") return 0;
  return Object.values(connections).reduce((count, state) => {
    if (state && typeof state === "object" && state.hidden !== void 0 && state.hidden !== null) {
      return count + 1;
    }
    return count;
  }, 0);
}
function count_pinned_connections(connections) {
  if (!connections || typeof connections !== "object") return 0;
  return Object.values(connections).reduce((count, state) => {
    if (state && typeof state === "object" && state.pinned !== void 0 && state.pinned !== null) {
      return count + 1;
    }
    return count;
  }, 0);
}
function is_connection_pinned(connections, prefixed_key) {
  if (!connections || typeof connections !== "object") return false;
  if (typeof prefixed_key !== "string" || !prefixed_key.length) return false;
  const state = connections[prefixed_key];
  if (!state || typeof state !== "object") return false;
  return state.pinned !== void 0 && state.pinned !== null;
}
function is_connection_hidden(connections, prefixed_key) {
  if (!connections || typeof connections !== "object") return false;
  if (typeof prefixed_key !== "string" || !prefixed_key.length) return false;
  const state = connections[prefixed_key];
  if (!state || typeof state !== "object") return false;
  return state.hidden !== void 0 && state.hidden !== null;
}

// src/utils/filter_hidden_results.js
function filter_hidden_results(results = [], connections_state = {}) {
  if (!Array.isArray(results) || !results.length) return [];
  if (!connections_state || typeof connections_state !== "object") return results;
  return results.filter((result) => {
    const item = result?.item;
    if (!item) return false;
    const prefixed_key = build_prefixed_connection_key(item.collection_key, item.key);
    const state = connections_state[prefixed_key];
    if (!state || typeof state !== "object") return true;
    const hidden = state.hidden !== void 0 && state.hidden !== null;
    const pinned = state.pinned !== void 0 && state.pinned !== null;
    return !(hidden && !pinned);
  });
}

// src/components/connections_codeblock.js
async function build_html27(connections_list, opts = {}) {
  const top_bar_buttons = [
    {
      title: "Refresh connections",
      icon: "refresh-cw",
      attrs: 'data-action="refresh-connections"'
    },
    {
      title: "Expand all",
      icon: "unfold-vertical",
      attrs: 'data-action="expand-all"'
    },
    {
      title: "Collapse all",
      icon: "fold-vertical",
      attrs: 'data-action="collapse-all"'
    },
    {
      title: "Send results to Smart Context",
      icon: "briefcase",
      attrs: 'data-action="send-to-smart-context"'
    },
    {
      title: "Copy as list of links",
      icon: "copy",
      attrs: 'data-action="copy-as-links"'
    },
    {
      title: "Connections settings",
      icon: "settings",
      attrs: 'data-action="open-settings"'
    },
    {
      title: "Help & getting started",
      icon: "help-circle",
      attrs: 'data-action="open-help"'
    }
  ].map((btn) => `
    <button
      aria-label="${btn.title}"
      ${btn.attrs ?? ""}
    >
      ${this.get_icon_html(btn.icon)}
    </button>
  `).join("");
  const html = `<div class="connections-codeblock connections-item-view sc-connections-view">
    <div class="connections-top-bar">
      <div class="connections-actions">
        ${top_bar_buttons}
        <span>Smart Connections</span>
      </div>
    </div>
    <div class="connections-list-container"></div>
    <div class="connections-bottom-bar"></div>
  </div>`;
  return html;
}
async function render29(connections_list, opts = {}) {
  const html = await build_html27.call(this, connections_list, opts);
  const frag = this.create_doc_fragment(html);
  this.apply_style_sheet(connections_codeblock_default);
  const container = frag.firstElementChild;
  post_process25.call(this, connections_list, container, opts);
  return frag;
}
async function post_process25(connections_list, container, opts = {}) {
  const list_container = container.querySelector(".connections-list-container");
  const env = connections_list.env;
  const app2 = env.plugin.app || window.app;
  const render_list = async () => {
    console.log("Rendering connections list in codeblock view");
    const connections_list_component_key = opts.connections_list_component_key || connections_list.connections_list_component_key || "connections_list_v4";
    const list = await env.smart_components.render_component(
      connections_list_component_key,
      connections_list,
      opts
    );
    this.empty(list_container);
    list_container.appendChild(list);
  };
  if (!container._has_listeners) {
    container._has_listeners = true;
    const refresh_button = container.querySelector('[data-action="refresh-connections"]');
    refresh_button?.addEventListener("click", async () => {
      const refresh_entity = connections_list.item;
      if (refresh_entity) {
        await refresh_entity.read();
        refresh_entity.queue_import();
        await refresh_entity.collection.process_source_import_queue?.();
        render_list();
      } else {
        console.warn("No entity found for refresh");
      }
    });
    const expand_all_button = container.querySelector('[data-action="expand-all"]');
    expand_all_button?.addEventListener("click", () => {
      container.querySelectorAll(".sc-result").forEach((elm) => {
        elm.classList.remove("sc-collapsed");
      });
    });
    const collapse_all_button = container.querySelector('[data-action="collapse-all"]');
    collapse_all_button?.addEventListener("click", () => {
      container.querySelectorAll(".sc-result").forEach((elm) => {
        elm.classList.add("sc-collapsed");
      });
    });
    const context_button = container.querySelector('[data-action="send-to-smart-context"]');
    context_button?.addEventListener("click", async () => {
      const raw_results = await get_results_fallback(connections_list, opts);
      if (!raw_results.length) {
        env?.events?.emit?.("connections:send_to_context_empty", {
          level: "warning",
          message: "No connection results to send to Smart Context",
          event_source: "connections_codeblock.send_to_smart_context"
        });
        return;
      }
      const connections_state = connections_list?.item?.data?.connections || {};
      const visible_results = filter_hidden_results(raw_results, connections_state);
      const context_items = build_connections_context_items({
        source_item: connections_list?.item,
        results: visible_results
      });
      if (!context_items.length) {
        env?.events?.emit?.("connections:send_to_context_empty", {
          level: "warning",
          message: "No visible connection results to send to Smart Context",
          event_source: "connections_codeblock.send_to_smart_context"
        });
        return;
      }
      const smart_context = env.smart_contexts.new_context();
      smart_context.add_items(context_items);
      smart_context.emit_event("context_selector:open");
      connections_list.emit_event("connections:sent_to_context");
    });
    const copy_links_button = container.querySelector('[data-action="copy-as-links"]');
    copy_links_button?.addEventListener("click", async () => {
      const raw_results = await get_results_fallback(connections_list, opts);
      if (!raw_results.length) {
        env?.events?.emit?.("connections:copy_list_empty", {
          level: "warning",
          message: "No connection results to copy",
          event_source: "connections_codeblock.copy_as_links"
        });
        return;
      }
      const connections_state = connections_list?.item?.data?.connections || {};
      const visible_results = filter_hidden_results(raw_results, connections_state);
      const links_payload = format_connections_as_links(visible_results);
      if (!links_payload) {
        env?.events?.emit?.("connections:copy_list_empty", {
          level: "warning",
          message: "No visible connection results to copy",
          event_source: "connections_codeblock.copy_as_links"
        });
        return;
      }
      await copy_to_clipboard(links_payload, {
        env,
        event_source: "connections_codeblock.copy_as_links",
        success_event_key: "connections:list_copied",
        error_event_key: "connections:list_copy_failed",
        unavailable_event_key: "connections:list_copy_unavailable"
      });
      connections_list.emit_event("connections:copied_list");
    });
    const settings_button = container.querySelector('[data-action="open-settings"]');
    settings_button?.addEventListener("click", () => {
      app2.setting.open();
      app2.setting.openTabById("smart-connections");
    });
    const open_help = () => {
      StoryModal.open(env.plugin, {
        title: "Getting Started With Smart Connections",
        url: "https://smartconnections.app/story/smart-connections-getting-started/?utm_source=connections-view-help#page=understanding-connections-1"
      });
    };
    const help_button = container.querySelector('[data-action="open-help"]');
    help_button?.addEventListener("click", open_help);
  }
  render_list();
  return container;
}
async function get_results_fallback(connections_list, opts = {}) {
  const cached = Array.isArray(connections_list?.results) ? connections_list.results : [];
  if (cached.length) return cached;
  try {
    const results = await connections_list.get_results({ ...opts });
    return Array.isArray(results) ? results : [];
  } catch (err) {
    console.error("Failed to fetch connections results", err);
    return [];
  }
}

// src/components/connections_footer_view.css
var connections_footer_view_default = ".connections-footer-view-shell {\n  .connections-list {\n    padding-top: 0.85rem;\n\n    > .sc-collapsed ul {\n      display: none;\n    }\n    > .sc-collapsed span svg {\n      transform: rotate(-90deg);\n    }\n    > .sc-result-pinned {\n      box-shadow: 0 0 0 1px var(--interactive-accent);\n      border-radius: var(--radius-s);\n      background-color: var(--background-modifier-hover);\n    }\n  }\n}\n";

// src/components/connections_footer_view.js
var FOOTER_FOLDED_STORAGE_KEY = "sc_footer_connections_folded";
function get_footer_connections_folded() {
  if (typeof localStorage === "undefined") return false;
  return localStorage.getItem(FOOTER_FOLDED_STORAGE_KEY) === "true";
}
function set_footer_connections_folded(folded) {
  if (typeof localStorage === "undefined") return;
  localStorage.setItem(FOOTER_FOLDED_STORAGE_KEY, String(folded));
}
function apply_footer_fold_state(header_container, list_container, folded) {
  if (!header_container || !list_container) return;
  if (folded) {
    header_container.setAttribute("aria-label", "Click to expand");
    header_container.classList.add("is-collapsed");
    list_container.style.display = "none";
    return;
  }
  header_container.setAttribute("aria-label", "Click to collapse");
  header_container.classList.remove("is-collapsed");
  list_container.style.display = "block";
}
async function build_html28(view, opts = {}) {
  const html = `<div class="embedded-backlinks">
    <div class="backlink-pane" style="position: relative;">
      <div class="tree-item-self is-clickable" aria-label="Click to collapse">
        <div class="tree-item-inner">Smart Connections</div>
      </div>
      <div class="search-result-container">
        <div class="sc-connections-wrapper">
          <div class="connections-view connections-footer-view connections-footer-view-shell sc-connections-view">
            <div class="connections-list-container"></div>
            <div class="connections-bottom-bar"></div>
          </div>
        </div>
      </div>
    </div>
  </div>`;
  return html;
}
async function render30(view, opts = {}) {
  const html = await build_html28.call(this, view, opts);
  const frag = this.create_doc_fragment(html);
  this.apply_style_sheet(connections_footer_view_default);
  const container = frag.firstElementChild;
  await post_process26.call(this, view, container, opts);
  return container;
}
async function post_process26(view, container, opts = {}) {
  const list_container = container.querySelector(".connections-list-container");
  const header_container = container.querySelector(".is-clickable");
  const env = view.env;
  const connections_item = opts.connections_item;
  if (!list_container) return container;
  header_container?.addEventListener("click", (event) => {
    event.preventDefault();
    event.stopPropagation();
    const next_folded = !get_footer_connections_folded();
    set_footer_connections_folded(next_folded);
    apply_footer_fold_state(header_container, list_container, next_folded);
  });
  apply_footer_fold_state(header_container, list_container, get_footer_connections_folded());
  if (!connections_item) {
    return container;
  }
  const connections_list = connections_item.connections || env.connections_lists.new_item(connections_item);
  const list = await env.smart_components.render_component("connections_list_v4", connections_list, { ...opts });
  this.empty(list_container);
  list_container.appendChild(list);
  return container;
}

// src/components/connections-graph/v1.css
var v1_default = ".connections-graph {\n  position: relative;\n  width: 100%;\n  block-size: auto;\n  container-type: inline-size;\n}\n\n.sc-graph-svg {\n  width: 100%;\n  aspect-ratio: 1 / 1; /* keep square; height follows width */\n  height: auto;\n  display: block;\n  border-radius: var(--radius-m);\n  border: 1px solid var(--background-modifier-border);\n  background: var(--background-secondary);\n  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--background-modifier-hover) 50%, transparent);\n}\n\n/* Details mount */\n.sc-graph-details {\n  margin-block-start: 10px;\n}\n\n/* Nodes (small, Obsidian-like) */\n.sc-graph-node .sc-graph-node-dot {\n  fill: var(--background-primary);\n  stroke: var(--color-base-100);\n  stroke-width: 1.25px;\n  transition: stroke-width 120ms ease, stroke 120ms ease, fill 120ms ease;\n}\n\n.sc-graph-node-center .sc-graph-node-dot {\n  fill: color-mix(in srgb, var(--interactive-accent) 18%, var(--background-primary));\n  stroke: var(--interactive-accent);\n  stroke-width: 1.5px;\n}\n\n.sc-graph-node-hover .sc-graph-node-dot {\n  fill: var(--background-modifier-hover);\n  stroke: var(--text-accent);\n  stroke-width: 2px;\n}\n\n/* Score as native SVG text */\n.sc-score-text {\n  font-size: 0.8em;\n  fill: var(--text-muted);\n  opacity: 0.95;\n  pointer-events: none;\n  user-select: none;\n}\n\n.sc-graph-node-hover .sc-score-text {\n  display: none;\n}\n\n/* Label text */\n.sc-node-label {\n  font-size: 0.85em;\n  font-weight: 500;\n  fill: var(--nav-item-color);\n  opacity: 0;\n  pointer-events: none;\n  user-select: none;\n  paint-order: stroke fill;\n  stroke: color-mix(in srgb, var(--background-primary) 65%, transparent);\n  stroke-width: 2px;\n  transition: opacity 120ms ease-in-out;\n}\n\n.sc-graph-node-hover .sc-node-label {\n  opacity: 0.96;\n  fill: var(--nav-item-color-active);\n}\n\n/* Explicitly hide any label rendered for the center (defense-in-depth) */\n.connections-graph .sc-graph-node-center .sc-node-label {\n  display: none !important;\n}\n\n/* Pinned state */\n.sc-graph-node.sc-result-pinned .sc-graph-node-dot {\n  fill: color-mix(in srgb, var(--interactive-accent) 12%, var(--background-primary));\n  stroke: var(--interactive-accent);\n  stroke-width: 2px;\n  filter: drop-shadow(0 0 0.25rem color-mix(in srgb, var(--interactive-accent) 50%, transparent));\n}\n\n.sc-graph-node.sc-result-hidden .sc-graph-node-dot {\n  fill: color-mix(in srgb, var(--background-modifier-border) 40%, transparent);\n  stroke: color-mix(in srgb, var(--text-muted) 65%, transparent);\n  stroke-width: 1px;\n  opacity: 0.6;\n}\n\n/* Hidden nodes: scores always visible but muted */\n.sc-graph-node.sc-result-hidden .sc-score-text {\n  opacity: 0.4;\n  fill: var(--text-faint);\n}\n\n/* Hidden nodes: labels only appear on hover, with a softer style */\n.sc-graph-node.sc-result-hidden.sc-graph-node-hover .sc-node-label {\n  opacity: 0.85;\n  fill: var(--text-faint);\n}\n\n.sc-node-drag-handle-inner {\n  width: 100%;\n  height: 100%;\n  background: transparent;\n  cursor: grab;\n  pointer-events: auto;\n}\n\n.sc-node-drag-handle-inner:active {\n  cursor: grabbing;\n}\n";

// node_modules/obsidian-smart-env/src/utils/get_item_display_name.js
var DISPLAY_SEPARATOR = " \u203A ";
function get_item_display_name3(item, settings = {}) {
  if (!item?.key) return "";
  const show_full_path = settings.show_full_path ?? true;
  if (show_full_path) {
    return item.key.replace(/#/g, DISPLAY_SEPARATOR).replace(/\//g, DISPLAY_SEPARATOR);
  }
  const pcs = [];
  const [source_key, ...block_parts] = item.key.split("#");
  const filename = source_key.split("/").pop();
  pcs.push(filename);
  if (block_parts.length) {
    pcs.push(block_parts.pop());
  }
  return pcs.join(DISPLAY_SEPARATOR);
}

// node_modules/smart-utils/cos_sim.js
function cos_sim2(vector1 = [], vector2 = []) {
  if (vector1.length !== vector2.length) {
    throw new Error("Vectors must have the same length");
  }
  let dot_product = 0;
  let magnitude1 = 0;
  let magnitude2 = 0;
  const epsilon = 1e-8;
  for (let i = 0; i < vector1.length; i++) {
    dot_product += vector1[i] * vector2[i];
    magnitude1 += vector1[i] * vector1[i];
    magnitude2 += vector2[i] * vector2[i];
  }
  magnitude1 = Math.sqrt(magnitude1);
  magnitude2 = Math.sqrt(magnitude2);
  if (magnitude1 < epsilon || magnitude2 < epsilon) return 0;
  return dot_product / (magnitude1 * magnitude2);
}

// node_modules/obsidian-smart-env/utils/parse_item_key_to_wikilink.js
function parse_item_key_to_wikilink(key) {
  if (!key) return "";
  const [file_path, ...parts] = key.split("#");
  const file_name = file_path.split("/").pop().replace(/\.md$/, "");
  if (!parts.length) return `[[${file_name}]]`;
  const heading = parts.filter((part) => !part.startsWith("{")).pop();
  if (!heading) return `[[${file_name}]]`;
  return `[[${file_name}#${heading}]]`;
}

// node_modules/obsidian-smart-env/src/utils/register_item_drag.js
function handle_connection_drag(obsidian_app, item, params, event) {
  const drag_manager = obsidian_app.dragManager;
  const link_text = parse_item_key_to_wikilink(item.key);
  const drag_data = drag_manager.dragLink(event, link_text);
  drag_manager.onDragStart(event, drag_data);
  if (params.drag_event_key) {
    item.emit_event(params.drag_event_key);
  } else {
    item.emit_event("connections:drag_result");
  }
}
function register_item_drag(container, item, params = {}) {
  const env = item.env;
  const app2 = env.obsidian_app;
  container.setAttribute("draggable", "true");
  container.addEventListener("dragstart", handle_connection_drag.bind(null, app2, item, params));
}

// src/components/connections-graph/v1.util.js
function hash_to_unit(str = "") {
  let h = 2166136261 >>> 0;
  for (let i = 0; i < str.length; i++) {
    h ^= str.charCodeAt(i);
    h = Math.imul(h, 16777619);
  }
  h += h << 13;
  h ^= h >>> 7;
  h += h << 3;
  h ^= h >>> 17;
  h += h << 5;
  return (h >>> 0) / 4294967295;
}
function seeded_angle(key = "", offset = -Math.PI / 2) {
  const seed = hash_to_unit(key);
  return offset + seed * Math.PI * 2;
}
function normalize_scores(scores = []) {
  const vals = scores.map((s) => Number.isFinite(s) ? +s : null);
  const present = vals.filter((v) => v !== null);
  if (!present.length) {
    return { norm: vals.map(() => 0.5), min: 0, max: 1 };
  }
  const min = Math.min(...present);
  const max = Math.max(...present);
  const den = max - min || 1;
  const norm2 = vals.map((v) => v === null ? 0.5 : (v - min) / den);
  return { norm: norm2, min, max };
}
function score_to_radius(t, min_r, max_r) {
  const tt = Math.max(0, Math.min(1, Number(t) || 0));
  return Math.round(min_r + (1 - tt) * (max_r - min_r));
}
function is_vec(v) {
  return Array.isArray(v) && v.length > 0;
}
function norm(a = []) {
  let s = 0;
  for (let i = 0; i < a.length; i++) {
    const x = a[i] || 0;
    s += x * x;
  }
  return Math.sqrt(s);
}
function to_unit(a = []) {
  const n = norm(a);
  if (!Number.isFinite(n) || n === 0) return null;
  return a.map((x) => x / n);
}
function mean_unit(vectors = []) {
  if (!vectors.length) return null;
  const dim = vectors[0].length;
  const acc = new Array(dim).fill(0);
  let c = 0;
  for (const v of vectors) {
    if (!v) continue;
    for (let i = 0; i < dim; i++) acc[i] += v[i] || 0;
    c++;
  }
  if (!c) return null;
  const m = acc.map((x) => x / c);
  return to_unit(m);
}
function prng_from_seed(seed_int) {
  let a = seed_int >>> 0;
  return function rand() {
    a |= 0;
    a = a + 1831565813 | 0;
    let t = Math.imul(a ^ a >>> 15, 1 | a);
    t ^= t + Math.imul(t ^ t >>> 7, 61 | t);
    return ((t ^ t >>> 14) >>> 0) / 4294967296;
  };
}
function kmeans_cosine_unit(vecs = [], k = 4, max_iter = 100, seed = 1337) {
  const xs = vecs.map((v) => v ? to_unit(v) : null);
  const idx = xs.map((v, i) => v ? i : -1).filter((i) => i >= 0);
  const m = idx.length;
  if (m === 0) return { centers: [], assign: new Array(vecs.length).fill(-1), sims: new Array(vecs.length).fill(0) };
  const rand = prng_from_seed(seed);
  const D = new Array(m).fill(1);
  const centers = [];
  centers.push(xs[idx[Math.floor(rand() * m)]]);
  while (centers.length < Math.min(k, m)) {
    for (let ii = 0; ii < m; ii++) {
      const v = xs[idx[ii]];
      let best = -Infinity;
      for (const c of centers) {
        const s = cos_sim2(v, c);
        if (s > best) best = s;
      }
      const d = 1 - Math.max(0, Math.min(1, best));
      D[ii] = d * d;
    }
    const sumD = D.reduce((a, b) => a + b, 0) || 1;
    let r = rand() * sumD;
    let chosen = 0;
    for (let ii = 0; ii < m; ii++) {
      r -= D[ii];
      if (r <= 0) {
        chosen = ii;
        break;
      }
    }
    centers.push(xs[idx[chosen]]);
  }
  let assign = new Array(m).fill(-1);
  let sims = new Array(m).fill(0);
  for (let it = 0; it < max_iter; it++) {
    let changed = false;
    for (let ii = 0; ii < m; ii++) {
      const v = xs[idx[ii]];
      let best_c = 0, best_s = -Infinity;
      for (let c = 0; c < centers.length; c++) {
        const s = cos_sim2(v, centers[c]);
        if (s > best_s) {
          best_s = s;
          best_c = c;
        }
      }
      if (assign[ii] !== best_c) {
        assign[ii] = best_c;
        changed = true;
      }
      sims[ii] = Math.max(0, Math.min(1, best_s));
    }
    const buckets = centers.map(() => []);
    for (let ii = 0; ii < m; ii++) buckets[assign[ii]].push(xs[idx[ii]]);
    const next = centers.map((_, c) => mean_unit(buckets[c]));
    for (let c = 0; c < centers.length; c++) {
      if (!next[c]) next[c] = centers[c];
      centers[c] = next[c];
    }
    if (!changed) break;
  }
  const full_assign = new Array(vecs.length).fill(-1);
  const full_sims = new Array(vecs.length).fill(0);
  for (let j = 0; j < m; j++) {
    full_assign[idx[j]] = assign[j];
    full_sims[idx[j]] = sims[j];
  }
  return { centers, assign: full_assign, sims: full_sims };
}
function quadrant_anchors(cx, cy, radii = [100, 100, 100, 100]) {
  const angs = [Math.PI / 4, 3 * Math.PI / 4, 5 * Math.PI / 4, 7 * Math.PI / 4];
  return angs.map((ang, i) => {
    const r = radii[i] ?? radii[radii.length - 1] ?? 100;
    return { x: Math.round(cx + r * Math.cos(ang)), y: Math.round(cy + r * Math.sin(ang)) };
  });
}
function radial_strength_for(ring_r, min_r, max_r) {
  if (!Number.isFinite(ring_r) || !Number.isFinite(min_r) || !Number.isFinite(max_r) || min_r >= max_r) {
    return 0.45;
  }
  const t = 1 - (ring_r - min_r) / (max_r - min_r);
  return 0.45 + 0.55 * Math.max(0, Math.min(1, t));
}
function compute_radii(width, height, padding = 24) {
  const w = Math.max(1, Number(width) || 100);
  const h = Math.max(1, Number(height) || w);
  const half_min = Math.min(w, h) / 2;
  const min_r_base = Math.round(half_min * 0.18);
  const min_r = Math.max(12, min_r_base);
  const outer_r_exact = Math.round(w * 0.45);
  const outer_r = Math.max(min_r + 8, outer_r_exact);
  return { min_r, outer_r, half_min };
}
function truncate_middle(text = "", max = 70) {
  const str = String(text ?? "");
  const limit = Math.max(0, Number.isFinite(max) ? Math.floor(max) : 0);
  if (!limit) return "";
  if (str.length <= limit) return str;
  if (limit <= 3) return str.slice(0, limit);
  const ellipsis = "\u2026";
  const available = limit - ellipsis.length;
  const front = Math.ceil(available / 2);
  const back = available - front;
  return `${str.slice(0, front)}${ellipsis}${str.slice(str.length - back)}`;
}
function label_anchor_offset(node_x, ctx = {}) {
  const {
    center_x = 0,
    label_width = 0,
    view_width = 0,
    radius = 0,
    margin = 10
  } = ctx;
  const safe_margin = Math.max(2, margin);
  const boundary_min = safe_margin;
  const boundary_max = Math.max(boundary_min, view_width - safe_margin);
  const anchor = node_x >= center_x ? "start" : "end";
  const base = radius + safe_margin;
  if (anchor === "start") {
    let offset2 = base;
    let abs_start2 = node_x + offset2;
    let abs_end2 = abs_start2 + label_width;
    if (abs_end2 > boundary_max) {
      const delta = abs_end2 - boundary_max;
      offset2 -= delta;
      abs_start2 -= delta;
    }
    if (abs_start2 < boundary_min) {
      offset2 += boundary_min - abs_start2;
    }
    return { anchor, offset: offset2 };
  }
  let offset = -base;
  let abs_end = node_x + offset;
  let abs_start = abs_end - label_width;
  if (abs_start < boundary_min) {
    const delta = boundary_min - abs_start;
    offset += delta;
    abs_end += delta;
  }
  if (abs_end > boundary_max) {
    offset -= abs_end - boundary_max;
  }
  return { anchor, offset };
}
function is_hover_preview_eligible(node = {}) {
  if (!node || node.isCenter) return false;
  const item = node.item;
  if (!item) return false;
  return Boolean(item.collection_key && item.key);
}
function resolve_drag_item(node = {}) {
  const item = node?.item;
  if (!item) return null;
  if (!item.collection_key || !item.key) return null;
  if (!item.env?.obsidian_app) return null;
  return item;
}
function project_anchor_to_ring(cx, cy, ax, ay, r) {
  const dx = ax - cx;
  const dy = ay - cy;
  const len = Math.hypot(dx, dy) || 1;
  const ux = dx / len;
  const uy = dy / len;
  return {
    x: Math.round(cx + ux * r),
    y: Math.round(cy + uy * r)
  };
}

// src/components/connections-graph/v1.js
function build_prefixed_key_set(results = []) {
  const prefixed_keys = /* @__PURE__ */ new Set();
  for (const result of results) {
    const prefixed = prefixed_key_for_item(result?.item);
    if (prefixed) prefixed_keys.add(prefixed);
  }
  return prefixed_keys;
}
function prefixed_key_for_item(item) {
  if (!item) return void 0;
  return build_prefixed_connection_key(item.collection_key, item.key);
}
function collect_hidden_entries({
  connections_state = {},
  existing_keys = /* @__PURE__ */ new Set(),
  resolve_item
} = {}) {
  if (typeof resolve_item !== "function") return [];
  const hidden_entries = [];
  for (const [prefixed_key, state] of Object.entries(connections_state)) {
    if (!state?.hidden || state?.pinned) continue;
    if (existing_keys.has(prefixed_key)) continue;
    const parsed = parse_prefixed_key(prefixed_key);
    if (!parsed) continue;
    const item = resolve_item(parsed.collection_key, parsed.item_key);
    if (!item) continue;
    existing_keys.add(prefixed_key);
    hidden_entries.push({
      item,
      score: null,
      is_hidden: true,
      prefixed_key
    });
  }
  return hidden_entries;
}
function build_node_classname({ is_center = false, is_hidden = false, is_pinned = false } = {}) {
  const classes = ["sc-graph-node"];
  if (is_center) classes.push("sc-graph-node-center");
  if (is_pinned) classes.push("sc-result-pinned");
  if (is_hidden) classes.push("sc-result-hidden");
  return classes.join(" ").trim();
}
function parse_prefixed_key(prefixed_key) {
  if (typeof prefixed_key !== "string" || !prefixed_key.includes(":")) return null;
  const [collection_key, ...rest] = prefixed_key.split(":");
  if (!collection_key || !rest.length) return null;
  return { collection_key, item_key: rest.join(":") };
}
var D3_CDN_URL = "https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js";
var D3_EXPECTED_MAJOR = "7.";
var D3_INTEGRITY_SHA256 = typeof globalThis !== "undefined" && globalThis.SC_D3_INTEGRITY_SHA256 || "";
function validate_d3_instance(d3) {
  if (!d3) return;
  const version4 = String(d3.version || "");
  if (version4 && !version4.startsWith(D3_EXPECTED_MAJOR)) {
    console.warn(
      `[connections_graph] Loaded d3 version "${version4}" but expected v${D3_EXPECTED_MAJOR}x`
    );
  }
}
async function load_d3() {
  const g = typeof globalThis !== "undefined" ? globalThis : window;
  if (g.d3) {
    validate_d3_instance(g.d3);
    return g.d3;
  }
  const existing = typeof document !== "undefined" ? document.querySelector("script[data-sc-d3]") : null;
  if (existing && g.d3) {
    validate_d3_instance(g.d3);
    return g.d3;
  }
  const d3 = await new Promise((resolve, reject) => {
    if (typeof document === "undefined" || !document.head) {
      reject(new Error("D3 loader: document.head not available"));
      return;
    }
    const script = document.createElement("script");
    script.src = D3_CDN_URL;
    script.async = true;
    script.setAttribute("data-sc-d3", "true");
    const integrity = String(D3_INTEGRITY_SHA256 || "").trim();
    if (integrity) {
      script.integrity = integrity;
      script.crossOrigin = "anonymous";
    }
    script.onload = () => {
      if (!g.d3) {
        reject(new Error("D3 loader: script loaded but window.d3 is missing"));
        return;
      }
      resolve(g.d3);
    };
    script.onerror = () => {
      reject(new Error("D3 loader: failed to load d3 from CDN"));
    };
    document.head.appendChild(script);
  });
  validate_d3_instance(d3);
  return d3;
}
async function build_html29(connections_list, params = {}) {
  const to_item = params?.to_item || connections_list?.item;
  const width = params.width ?? 100;
  const height = params.height ?? 100;
  return `
    <div class="connections-graph sc-graph"
         data-center-key="${to_item?.key || ""}"
         data-center-collection="${to_item?.collection_key || ""}">
      <svg class="sc-graph-svg"
           width="${width}" height="${height}"
           viewBox="0 0 ${width} ${height}"
           preserveAspectRatio="xMidYMid meet">
        <g class="sc-graph-viewport">
          <g class="nodes"></g>
        </g>
      </svg>
      <div class="sc-graph-details" aria-live="polite"></div>
    </div>
  `;
}
async function render31(connections_list, params = {}) {
  this.apply_style_sheet(v1_default);
  const html = await build_html29.call(this, connections_list, params);
  const frag = this.create_doc_fragment(html);
  const container = frag.querySelector(".connections-graph");
  post_process27.call(this, connections_list, container, params);
  return container;
}
async function post_process27(connections_list, container, params = {}) {
  const {
    results = await connections_list.get_results(params)
  } = params;
  try {
    const d3 = await load_d3();
    const to_item = params.to_item || connections_list?.item;
    if (!to_item) throw new Error("connections_graph: could not resolve center item.");
    const env = to_item.env;
    const connections_settings = params.connections_settings ?? env.connections_lists.settings;
    const connection_state = to_item?.data?.connections || {};
    const event_key_domain = params.event_key_domain || "connections";
    const drag_event_key = `${event_key_domain}:drag_result`;
    const base_prefixed_keys = build_prefixed_key_set(results);
    const hidden_entries = collect_hidden_entries({
      connections_state: connection_state,
      existing_keys: base_prefixed_keys,
      resolve_item: (collection_key, item_key) => connections_list?.env?.[collection_key]?.get(item_key)
    });
    const result_entries = [...results, ...hidden_entries];
    const svg = container.querySelector("svg.sc-graph-svg");
    const viewport = svg.querySelector("g.sc-graph-viewport");
    const g_nodes = viewport.querySelector("g.nodes");
    const CENTER_R = 8;
    const NODE_R = 5;
    const PADDING = 24;
    const LABEL_MARGIN = 10;
    let sim = null;
    let node_sel = null;
    const build_label_text = (item) => {
      const display = get_item_display_name3(item, { show_full_path: false }) || item?.key || "";
      return truncate_middle(display, 70);
    };
    let view_width = +svg.getAttribute("width") || 100;
    const layout = () => {
      const width = container.clientWidth || +svg.getAttribute("width") || 100;
      const height = width;
      view_width = width;
      svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
      svg.setAttribute("width", width);
      svg.setAttribute("height", height);
      const center_x = Math.round(width * 0.5);
      const center_y = Math.round(height * 0.5);
      const { min_r, outer_r, half_min } = compute_radii(width, height, PADDING);
      const center_vec = is_vec(to_item?.vec) ? to_unit(to_item.vec) : null;
      const non_center_vecs = result_entries.map((r) => is_vec(r?.item?.vec) ? to_unit(r.item.vec) : null);
      const sims_to_center_raw = non_center_vecs.map((v) => center_vec && v ? Math.max(0, Math.min(1, cos_sim2(center_vec, v))) : null);
      const { norm: sims_center_norm } = normalize_scores(sims_to_center_raw);
      const center_node = {
        id: "__center__",
        item: to_item,
        score: 1,
        radius: CENTER_R,
        isCenter: true,
        x: center_x,
        y: center_y,
        fx: center_x,
        fy: center_y
      };
      const seed = Math.floor(hash_to_unit(result_entries.map((r) => r?.item?.key || "").join("|")) * 1e9);
      const { centers, assign } = kmeans_cosine_unit(non_center_vecs, 4, 300, seed);
      const center_to_cluster_sims = (centers.length ? centers : [null, null, null, null]).slice(0, 4).map((c) => center_vec && c ? Math.max(0, Math.min(1, cos_sim2(center_vec, c))) : 0.5);
      const cluster_anchor_radii = center_to_cluster_sims.map((s) => score_to_radius(s, min_r, outer_r));
      while (cluster_anchor_radii.length < 4) cluster_anchor_radii.push(cluster_anchor_radii[cluster_anchor_radii.length - 1] || Math.round((min_r + outer_r) / 2));
      const anchors = quadrant_anchors(center_x, center_y, cluster_anchor_radii);
      const nodes_non_center = result_entries.map((res, i) => {
        const r_item = res?.item;
        if (!r_item) return null;
        const t = sims_center_norm[i] ?? 0.5;
        const rr = score_to_radius(t, min_r, outer_r);
        const angle = seeded_angle(r_item.key || String(i), -Math.PI / 2);
        const x0 = Math.round(center_x + rr * Math.cos(angle));
        const y0 = Math.round(center_y + rr * Math.sin(angle));
        const cluster = Number.isInteger(assign[i]) ? assign[i] : Math.floor(hash_to_unit(r_item.key || String(i)) * 4);
        const v = non_center_vecs[i];
        const cluster_vec = centers[cluster] || null;
        const node_to_cluster_sim = v && cluster_vec ? Math.max(0, Math.min(1, cos_sim2(v, cluster_vec))) : 0;
        const prefixed_key = prefixed_key_for_item(r_item);
        const isPinned = prefixed_key ? is_connection_pinned(connection_state, prefixed_key) : false;
        const isHidden = Boolean(res?.is_hidden) || (prefixed_key ? is_connection_hidden(connection_state, prefixed_key) : false);
        return {
          id: r_item.key,
          item: r_item,
          score: Number.isFinite(res?.score) ? +res.score : center_vec && v ? cos_sim2(center_vec, v) : null,
          // display only
          ring_r: rr,
          angle,
          radius: NODE_R,
          isCenter: false,
          x: x0,
          y: y0,
          cluster,
          node_to_cluster_sim,
          label_text: build_label_text(r_item),
          prefixed_key,
          isPinned,
          isHidden
        };
      }).filter(Boolean);
      const nodes = [center_node, ...nodes_non_center];
      node_sel = d3.select(g_nodes).selectAll("g.sc-graph-node").data(nodes, (d) => d.id).join((enter) => {
        const g = enter.append("g").attr("class", (d) => build_node_classname({
          is_center: d.isCenter,
          is_hidden: d.isHidden,
          is_pinned: d.isPinned
        })).attr("title", (d) => (d.item.path || "").replace(/"/g, "&quot;")).attr("data-collection", (d) => d.item.collection_key).attr("data-key", (d) => d.item.key).attr("data-path", (d) => (d.item.path || "").replace(/"/g, "&quot;")).attr("data-link", (d) => (d.item.link || "").replace(/"/g, "&quot;")).attr("data-score", (d) => `${d.score ?? ""}`).attr("data-cluster", (d) => d.isCenter ? "" : String(d.cluster)).attr("data-hidden", (d) => d.isHidden ? "true" : null).attr("data-pinned", (d) => d.isPinned ? "true" : null).attr("data-prefixed-key", (d) => d.prefixed_key || "");
        g.append("circle").attr("r", (d) => d.radius).attr("class", "sc-graph-node-dot");
        g.append("text").attr("class", "sc-score-text").attr("y", -NODE_R - 6).attr("text-anchor", "middle").attr("alignment-baseline", "baseline").text((d) => typeof d.score === "number" && !d.isCenter ? d.score.toFixed(2) : "");
        g.filter((d) => !d.isCenter).append("text").attr("class", "sc-node-label").attr("y", -NODE_R - 6).attr("text-anchor", "middle").attr("alignment-baseline", "baseline").text((d) => d.label_text.replace(".md", ""));
        const drag_handle_size = (NODE_R + 6) * 2;
        const drag_handle = g.append("foreignObject").attr("class", "sc-node-drag-handle").attr("x", -(drag_handle_size / 2)).attr("y", -(drag_handle_size / 2)).attr("width", drag_handle_size).attr("height", drag_handle_size);
        drag_handle.append("xhtml:div").attr("class", "sc-node-drag-handle-inner");
        return g;
      });
      node_sel.attr("class", (d) => build_node_classname({
        is_center: d.isCenter,
        is_hidden: d.isHidden,
        is_pinned: d.isPinned
      })).attr("data-hidden", (d) => d.isHidden ? "true" : null).attr("data-pinned", (d) => d.isPinned ? "true" : null).attr("data-prefixed-key", (d) => d.prefixed_key || "").attr("transform", (d) => `translate(${d.x},${d.y})`);
      const clear_hover = () => {
        node_sel.classed("sc-graph-node-hover", false);
      };
      const set_hover = (node) => {
        if (!node) return;
        clear_hover();
        node.classList.add("sc-graph-node-hover");
      };
      node_sel.on("mouseenter", function() {
        clear_hover();
        d3.select(this).classed("sc-graph-node-hover", true);
      }).on("mouseleave", function() {
        d3.select(this).classed("sc-graph-node-hover", false);
      }).on("click", function(event, datum) {
        handle_node_click({
          node: datum,
          container,
          env,
          center_item: to_item
        });
      });
      node_sel.select("foreignObject.sc-node-drag-handle").select("div.sc-node-drag-handle-inner").each(function(datum) {
        if (this.dataset.scDragBound === "true") return;
        this.dataset.scDragBound = "true";
        const drag_item = resolve_drag_item(datum);
        if (drag_item) register_item_drag(this, drag_item, { drag_event_key });
        if (this.getAttribute("data-sc-hover-preview-bound") !== "true" && is_hover_preview_eligible(datum)) {
          this.setAttribute("data-sc-hover-preview-bound", "true");
          register_item_hover_popover(this, datum.item, { event_key_domain });
        }
        const node = this.closest("g.sc-graph-node");
        this.addEventListener("mouseenter", () => set_hover(node));
        this.addEventListener("mouseleave", () => {
          if (node) node.classList.remove("sc-graph-node-hover");
        });
        this.addEventListener("click", (event) => {
          event.preventDefault();
          event.stopPropagation();
          handle_node_click({
            node: datum,
            container,
            env,
            center_item: to_item
          });
        });
      });
      const radial_force = d3.forceRadial(
        (d) => d.isCenter ? 0 : d.ring_r,
        center_x,
        center_y
      ).strength((d) => d.isCenter ? 1 : radial_strength_for(d.ring_r, min_r, outer_r));
      const collide_force = d3.forceCollide((d) => d.radius + 16).strength(0.9).iterations(2);
      const charge_force = d3.forceManyBody().strength(-80).distanceMax(half_min);
      const cluster_strength_fn = (d) => {
        if (d.isCenter) return 0;
        const base = 0.04;
        const node_c = Math.max(0, Math.min(1, d.node_to_cluster_sim || 0));
        const center_c = center_to_cluster_sims[(d.cluster ?? 0) % 4] || 0.5;
        return base + 0.28 * node_c * (0.5 + 0.5 * center_c);
      };
      function force_cluster(anchors_in = [], strength_fn = () => 0.2, cx = 0, cy = 0) {
        let nodes_f = [];
        function force(alpha) {
          for (let i = 0; i < nodes_f.length; i++) {
            const d = nodes_f[i];
            if (d.isCenter) continue;
            const idx = Number.isInteger(d.cluster) ? d.cluster % anchors_in.length : 0;
            const a = anchors_in[idx];
            const s = Math.max(0, Math.min(1, strength_fn(d)));
            if (!a || s <= 0) continue;
            const pr = project_anchor_to_ring(cx, cy, a.x, a.y, d.ring_r);
            d.vx += (pr.x - d.x) * s * alpha;
            d.vy += (pr.y - d.y) * s * alpha;
          }
        }
        force.initialize = function(_nodes) {
          nodes_f = _nodes;
        };
        return force;
      }
      const cluster_force = force_cluster(anchors, cluster_strength_fn, center_x, center_y);
      if (!sim) {
        sim = d3.forceSimulation(nodes).alpha(0.95).alphaDecay(0.08).force("radial", radial_force).force("cluster", cluster_force).force("collide", collide_force).force("charge", charge_force).on("tick", () => {
          nodes[0].fx = center_x;
          nodes[0].fy = center_y;
          node_sel.attr("transform", (d) => `translate(${d.x},${d.y})`);
          node_sel.select("text.sc-node-label").each(function(d) {
            if (d.isCenter) return;
            const node = this;
            const label_width = typeof node.getComputedTextLength === "function" ? node.getComputedTextLength() : 0;
            const { anchor, offset } = label_anchor_offset(d.x, {
              center_x,
              label_width,
              view_width,
              radius: d.radius,
              margin: LABEL_MARGIN
            });
            d3.select(node).attr("text-anchor", anchor).attr("x", offset);
          });
        });
      } else {
        sim.nodes(nodes);
        sim.force("radial", radial_force);
        sim.force("cluster", cluster_force);
        sim.force("collide", collide_force);
        sim.force("charge", charge_force);
        nodes[0].fx = center_x;
        nodes[0].fy = center_y;
        sim.alpha(0.8).restart();
      }
    };
    layout();
    const ro = new ResizeObserver(() => layout());
    ro.observe(container);
  } catch (err) {
    console.error("[connections_graph] post_process error:", err);
    const fallback = document.createElement("p");
    fallback.className = "sc-no-results";
    fallback.textContent = "Unable to render graph. See console for details.";
    container.appendChild(fallback);
  }
  return container;
}
function handle_node_click({ node, container, env, center_item }) {
  if (!node?.item) return;
  const detail = build_result_detail(node, center_item);
  if (!detail) return;
  if (typeof CustomEvent === "function" && container?.dispatchEvent) {
    container.dispatchEvent(new CustomEvent("connections:result", {
      detail,
      bubbles: true
    }));
  }
  env?.events?.emit?.("connections:result", detail);
  node.item.emit_event?.("connections:result", detail);
  node.item.emit_event?.("connections:open_result", detail);
}
function build_result_detail(node, center_item) {
  const collection_key = node?.item?.collection_key;
  const item_key = node?.item?.key;
  if (!collection_key || !item_key) return null;
  return {
    collection_key,
    item_key,
    prefixed_key: node.prefixed_key || prefixed_key_for_item(node.item) || build_prefixed_connection_key(collection_key, item_key),
    score: typeof node.score === "number" ? node.score : null,
    is_hidden: Boolean(node.isHidden),
    is_pinned: Boolean(node.isPinned),
    event_source: "connections-graph-v1",
    center_key: center_item?.key
  };
}

// src/components/connections-list-item/v3.js
var import_obsidian55 = require("obsidian");

// src/components/connections-list-item/v3.css
var v3_default = "a.sc-result-file-title {\n  text-decoration: none !important;\n}\n\na.sc-result-file-title > .sc-score {\n  display: inline-block;\n  width: auto;\n  height: 1.7em;\n  padding: 0 0.3em;\n  line-height: 1.7em;\n  text-align: center;\n  font-weight: 600 !important;\n  font-size: 0.8em !important;\n  color: var(--nav-item-color) !important;\n  background: var(--background-modifier-hover);\n  border-radius: 6px;\n}\n\na.sc-result-file-title > .sc-path {\n  margin-right: -3px;\n}\n\na.sc-result-file-title > .sc-path,\na.sc-result-file-title > .sc-title {\n  font-weight: bold !important;\n  text-decoration: underline;\n}\n\na.sc-result-file-title > .sc-breadcrumb:not(.sc-path, .sc-title, .sc-score) {\n  font-style: italic;\n}\n\na.sc-result-file-title > .sc-breadcrumb-separator {\n  color: color-mix(in srgb, var(--text-normal) 50%, transparent) !important;\n}\n\n.sc-result.sc-result-graph-focus {\n  outline: 2px solid color-mix(in srgb, var(--interactive-accent) 70%, transparent);\n  border-radius: var(--radius-s);\n  box-shadow:\n    0 0 0 1px color-mix(in srgb, var(--background-primary) 80%, transparent),\n    0 0 0.5rem color-mix(in srgb, var(--interactive-accent) 45%, transparent);\n  transition: outline 120ms ease, box-shadow 120ms ease;\n}\n";

// src/components/connections-list-item/v3.js
async function build_html30(result, params = {}) {
  const item = result.item;
  const env = item.env;
  const score = result.score;
  const connections_settings = params.connections_settings ?? env.connections_lists.settings;
  const component_settings = connections_settings.components?.connections_list_item_v3 || {};
  const header_html = get_result_header_html(score, item, component_settings);
  const all_expanded = connections_settings.expanded_view;
  return `<div class="temp-container">
    <div
      class="sc-result ${all_expanded ? "" : "sc-collapsed"}"
      data-path="${item.path.replace(/"/g, "&quot;")}"
      data-link="${item.link?.replace(/"/g, "&quot;") || ""}"
      data-collection="${item.collection_key}"
      data-score="${score}"
      data-key="${item.key}"
      draggable="true"
    >
      <span class="header">
        ${this.get_icon_html("right-triangle")}
        <a class="sc-result-file-title" href="#" title="${item.path.replace(/"/g, "&quot;")}" draggable="true">
          ${header_html}
        </a>
      </span>
      <ul draggable="true">
        <li class="sc-result-file-title" title="${item.path.replace(/"/g, "&quot;")}" data-collection="${item.collection_key}" data-key="${item.key}"></li>
      </ul>
    </div>
  </div>`;
}
async function render32(result_scope, params = {}) {
  this.apply_style_sheet(v3_default);
  let html = await build_html30.call(this, result_scope, params);
  const frag = this.create_doc_fragment(html);
  const container = frag.querySelector(".sc-result");
  post_process28.call(this, result_scope, container, params);
  return container;
}
async function post_process28(result_scope, container, params = {}) {
  const { item } = result_scope;
  const env = item.env;
  const plugin = env.smart_connections_plugin;
  const app2 = plugin.app;
  const connections_settings = params.connections_settings ?? env.connections_lists.settings;
  const component_settings = connections_settings.components?.connections_list_item_v3 || {};
  const should_render_markdown = component_settings?.render_markdown ?? true;
  if (!should_render_markdown) container.classList.add("sc-result-plaintext");
  const source_item = result_scope.connections_list?.item;
  const prefixed_key = build_prefixed_connection_key(item.collection_key, item.key);
  container.dataset.prefixedKey = prefixed_key;
  const connection_state = source_item?.data?.connections;
  if (is_connection_hidden(connection_state, prefixed_key)) {
    container.style.display = "none";
    container.dataset.hidden = "true";
  }
  if (is_connection_pinned(connection_state, prefixed_key)) {
    container.classList.add("sc-result-pinned");
    container.dataset.pinned = "true";
  }
  const render_result = async (_result_elm) => {
    if (!_result_elm.querySelector("li").innerHTML) {
      const collection_key = _result_elm.dataset.collection;
      const entity = env[collection_key].get(_result_elm.dataset.path);
      let markdown;
      if (should_render_embed(entity)) markdown = `${entity.embed_link}

${await entity.read()}`;
      else markdown = process_for_rendering(await entity.read());
      let entity_frag;
      if (should_render_markdown) entity_frag = await this.render_markdown(markdown, entity);
      else entity_frag = this.create_doc_fragment(markdown);
      container.querySelector("li").appendChild(entity_frag);
    }
  };
  const toggle_fold_elm = container.querySelector(".header .svg-icon.right-triangle");
  toggle_fold_elm.addEventListener("click", toggle_result);
  const event_key_domain = params.event_key_domain || "connections";
  const drag_event_key = `${event_key_domain}:drag_result`;
  register_item_drag(container, item, { drag_event_key });
  register_item_hover_popover(container, item, { event_key_domain });
  container.addEventListener("click", (event) => {
    open_source(item, event);
    item.emit_event(`${event_key_domain}:open_result`, { event_source: "connections-list-item-v3" });
  });
  const observer = new MutationObserver((mutations) => {
    const has_expansion_change = mutations.some((mutation) => {
      const target = mutation.target;
      return mutation.attributeName === "class" && mutation.oldValue?.includes("sc-collapsed") !== target.classList.contains("sc-collapsed");
    });
    if (has_expansion_change && !mutations[0].target.classList.contains("sc-collapsed")) {
      render_result(mutations[0].target);
    }
  });
  observer.observe(container, {
    attributes: true,
    attributeOldValue: true,
    attributeFilter: ["class"]
  });
  plugin.registerDomEvent(container, "contextmenu", (event) => {
    event.preventDefault();
    event.stopPropagation();
    if (!source_item) return;
    source_item.data.connections ||= {};
    const prefixed_key2 = build_prefixed_connection_key(
      item.collection_key,
      item.key
    );
    const pinned = is_connection_pinned(source_item.data.connections, prefixed_key2);
    const hidden_count = count_hidden_connections(source_item.data.connections);
    const pinned_count = count_pinned_connections(source_item.data.connections);
    const results = result_scope.connections_list?.results || [];
    const target_name = get_item_display_name3(item, component_settings) || item.key;
    console.log({ target_name, item });
    const menu = new import_obsidian55.Menu(app2);
    menu.addItem((menu_item) => {
      menu_item.setTitle(`Hide ${target_name}`).setIcon("eye-off").onClick(() => {
        try {
          apply_hidden_state(source_item.data.connections, prefixed_key2, Date.now());
          if (source_item.data.hidden_connections) {
            delete source_item.data.hidden_connections[item.key];
            if (!Object.keys(source_item.data.hidden_connections).length) delete source_item.data.hidden_connections;
          }
          source_item.queue_save();
          container.style.display = "none";
          container.dataset.hidden = "true";
          source_item.collection.save();
          source_item.emit_event("connections:hidden_item");
        } catch (err) {
          env?.events?.emit?.("connections:hide_failed", {
            level: "error",
            message: "Hide failed \u2013 check console",
            details: err?.message || "",
            event_source: "connections_list_item.contextmenu"
          });
          console.error(err);
        }
      });
    });
    menu.addItem((menu_item) => {
      const title_prefix = pinned ? "Unpin" : "Pin";
      menu_item.setTitle(`${title_prefix} ${target_name}`).setIcon(pinned ? "pin-off" : "pin").onClick(() => {
        try {
          if (pinned) {
            remove_pinned_state(source_item.data.connections, prefixed_key2);
            container.classList.remove("sc-result-pinned");
            container.removeAttribute("data-pinned");
          } else {
            apply_pinned_state(source_item.data.connections, prefixed_key2, Date.now());
            container.classList.add("sc-result-pinned");
            container.dataset.pinned = "true";
            source_item.emit_event("connections:pinned_item");
          }
          source_item.queue_save();
          source_item.collection.save();
        } catch (err) {
          env?.events?.emit?.("connections:pin_toggle_failed", {
            level: "error",
            message: `${title_prefix} failed \u2013 check console`,
            details: err?.message || "",
            event_source: "connections_list_item.contextmenu"
          });
          console.error(err);
        }
      });
    });
    menu.addSeparator();
    const links_payload = format_connections_as_links(results);
    if (links_payload) {
      menu.addItem((menu_item) => {
        menu_item.setTitle("Copy as list of links").setIcon("copy").onClick(async () => {
          await copy_to_clipboard(links_payload, {
            env,
            event_source: "connections_list_item.copy_as_links",
            success_event_key: "connections:list_copied",
            error_event_key: "connections:list_copy_failed",
            unavailable_event_key: "connections:list_copy_unavailable"
          });
          result_scope.connections_list.emit_event("connections:copied_list");
        });
      });
    }
    menu.addSeparator();
    menu.addItem((menu_item) => {
      menu_item.setTitle(`Unhide All (${hidden_count})`).setIcon("eye").setDisabled(!hidden_count).onClick(() => {
        try {
          if (!source_item.data.connections) return;
          const changed = remove_all_hidden_states(source_item.data.connections);
          if (!changed) return;
          if (source_item.data.hidden_connections) delete source_item.data.hidden_connections;
          source_item.queue_save();
          container.closest(".sc-connections-view")?.querySelector('[title="Refresh"]')?.click();
          source_item.collection.save();
        } catch (err) {
          env?.events?.emit?.("connections:unhide_failed", {
            level: "error",
            message: "Unhide failed \u2013 check console",
            details: err?.message || "",
            event_source: "connections_list_item.contextmenu"
          });
          console.error(err);
        }
      });
    });
    menu.addItem((menu_item) => {
      menu_item.setTitle(`Unpin All (${pinned_count})`).setIcon("pin-off").setDisabled(!pinned_count).onClick(() => {
        try {
          if (!source_item.data.connections) return;
          const changed = remove_all_pinned_states(source_item.data.connections);
          if (!changed) return;
          const list_root = container.closest(".connections-list");
          list_root?.querySelectorAll(".sc-result[data-pinned]").forEach((result_el) => {
            result_el.classList.remove("sc-result-pinned");
            result_el.removeAttribute("data-pinned");
          });
          source_item.queue_save();
          source_item.collection.save();
        } catch (err) {
          env?.events?.emit?.("connections:unpin_failed", {
            level: "error",
            message: "Unpin failed \u2013 check console",
            details: err?.message || "",
            event_source: "connections_list_item.contextmenu"
          });
          console.error(err);
        }
      });
    });
    menu.showAtMouseEvent(event);
  });
  if (!container.classList.contains("sc-collapsed")) {
    render_result(container);
  }
  return container;
}
function get_result_header_html(score, item, component_settings = {}) {
  const raw_parts = get_item_display_name3(item, component_settings).split(DISPLAY_SEPARATOR).filter(Boolean);
  const parts = format_item_parts(raw_parts, item?.lines);
  const name = parts.pop();
  const formatted_score = typeof score === "number" ? score.toFixed(2) : score;
  const separator = '<small class="sc-breadcrumb-separator"> &gt; </small>';
  const parts_html = parts.map((part) => `<small class="sc-breadcrumb">${part}</small>`).join(separator);
  return [
    `<small class="sc-breadcrumb sc-score">${formatted_score}</small>`,
    `${parts_html}${separator}`,
    `<small class="sc-breadcrumb sc-title">${name.endsWith(".md") ? name.replace(/\.md$/, "") : name}</small>`
  ].join("");
}
function format_item_parts(parts, lines = []) {
  if (!Array.isArray(parts) || !parts.length) return [];
  const has_line_marker = Array.isArray(lines) && lines.length;
  return parts.map((part) => {
    if (has_line_marker && part.startsWith("{")) {
      return `Lines: ${lines.join("-")}`;
    }
    return part;
  });
}
function should_render_embed(entity) {
  if (!entity) return false;
  if (entity.is_media) return true;
  return false;
}
function process_for_rendering(content) {
  if (content.includes("```dataview")) content = content.replace(/```dataview/g, "```\\dataview");
  if (content.includes("```smart-context")) content = content.replace(/```smart-context/g, "```\\smart-context");
  if (content.includes("```smart-chatgpt")) content = content.replace(/```smart-chatgpt/g, "```\\smart-chatgpt");
  if (content.includes("![[")) content = content.replace(/\!\[\[/g, "! [[");
  return content;
}
function toggle_result(event) {
  event.preventDefault();
  event.stopPropagation();
  const _result_elm = event.target.closest(".sc-result");
  _result_elm.classList.toggle("sc-collapsed");
}
var settings_config11 = {
  "show_full_path": {
    name: "Show full path",
    type: "toggle",
    description: "Turning on will include the folder path in the connections results.",
    // default: true,
    group: "Connections list item"
  },
  "render_markdown": {
    name: "Render markdown",
    type: "toggle",
    description: "Turn off to prevent rendering markdown and display connection results as plain text.",
    // default: true,
    group: "Connections list item"
  }
};

// src/components/connections-list/v3.js
async function build_html31(connections_list, opts = {}) {
  return `<div><div class="connections-list sc-list" data-key="${connections_list.item.key}"></div></div>`;
}
async function render33(connections_list, opts = {}) {
  const html = await build_html31.call(this, connections_list, opts);
  const frag = this.create_doc_fragment(html);
  const container = frag.querySelector(".connections-list");
  post_process29.call(this, connections_list, container, opts);
  return container;
}
async function post_process29(connections_list, container, opts = {}) {
  container.dataset.key = connections_list.item.key;
  const results = await connections_list.get_results(opts);
  if (!results || !Array.isArray(results) || results.length === 0) {
    const no_results = this.create_doc_fragment(`<p class="sc-no-results">No results found.<br><em>Try using the refresh button. If that doesn't work, try running "Clear sources data" and then "Reload sources" in the Smart Environment settings.</em></p>`);
    container.appendChild(no_results);
    return container;
  }
  const smart_components = connections_list.env.smart_components;
  const result_frags = await Promise.all(results.map((result) => {
    return smart_components.render_component("connections_list_item_v3", result, { ...opts });
  }));
  result_frags.forEach((result_frag) => container.appendChild(result_frag));
  return container;
}
var display_name5 = "List only";

// src/components/connections-list/v4.js
async function build_html32(connections_list, opts = {}) {
  return `<div>
      <div class="connections-graph-container"></div>
      <div class="connections-list sc-list" data-key="${connections_list.item.key}"></div>
  </div>`;
}
async function render34(connections_list, opts = {}) {
  const html = await build_html32.call(this, connections_list, opts);
  const frag = this.create_doc_fragment(html);
  const container = frag.firstElementChild;
  post_process30.call(this, connections_list, container, opts);
  return container;
}
async function post_process30(connections_list, container, opts = {}) {
  const env = connections_list.env;
  const graph_container = container.querySelector(".connections-graph-container");
  const list_container = container.querySelector(".connections-list.sc-list");
  container.dataset.key = connections_list.item.key;
  const results = await connections_list.get_results(opts);
  const connections_settings = opts.connections_settings ?? env.connections_lists.settings;
  const component_settings = connections_settings.components?.connections_list_v4 || {};
  const show_graph = opts.show_graph ?? component_settings.show_graph;
  if (show_graph) {
    try {
      const graph = await env.smart_components.render_component("connections_graph_v1", connections_list, { ...opts, results });
      this.empty(graph_container);
      graph_container.appendChild(graph);
      register_graph_events(graph, list_container);
    } catch (_err) {
      this.empty(graph_container);
    }
  }
  if (!results || !Array.isArray(results) || results.length === 0) {
    const no_results = this.create_doc_fragment(`<p class="sc-no-results">No results found.<br><em>Try using the refresh button. If that doesn't work, try running "Clear sources data" and then "Reload sources" in the Smart Environment settings.</em></p>`);
    list_container.appendChild(no_results);
    return container;
  }
  const smart_components = connections_list.env.smart_components;
  const result_frags = await Promise.all(results.map((result) => {
    return smart_components.render_component("connections_list_item_v3", result, { ...opts });
  }));
  result_frags.forEach((result_frag) => list_container.appendChild(result_frag));
  return container;
}
var GRAPH_FOCUS_CLASS = "sc-result-graph-focus";
var GRAPH_FOCUS_TIMEOUT_MS = 2400;
function register_graph_events(graph, list_container) {
  if (!graph || !list_container) return;
  graph.addEventListener("connections:result", (event) => {
    focus_result_from_graph(list_container, event?.detail || {});
  });
}
function focus_result_from_graph(list_container, detail = {}) {
  const target = find_result_element(list_container, detail);
  if (!target) return;
  if (target.classList.contains("sc-collapsed")) target.classList.remove("sc-collapsed");
  target.scrollIntoView?.({ block: "center", behavior: "smooth" });
  target.classList.add(GRAPH_FOCUS_CLASS);
  const schedule = typeof window !== "undefined" ? window.setTimeout : setTimeout;
  schedule?.(() => target.classList.remove(GRAPH_FOCUS_CLASS), GRAPH_FOCUS_TIMEOUT_MS);
}
function find_result_element(list_container, detail = {}) {
  if (!list_container) return null;
  const { collection_key, item_key } = detail;
  if (!collection_key || !item_key) return null;
  return Array.from(list_container.querySelectorAll(".sc-result")).find((node) => {
    return node.dataset.collection === collection_key && node.dataset.key === item_key;
  }) || null;
}
var display_name6 = "Version 4.0 (Graph + List)";
var settings_config12 = {
  "show_graph": {
    name: "Show graph",
    type: "toggle",
    description: "Show a graph visualization of the connections above the list.",
    // default: true,
    group: "Connections lists"
  }
};

// src/components/connections-settings/header.js
var import_obsidian56 = require("obsidian");
async function build_html33(scope_plugin) {
  return `
    <div>
      <div data-user-agreement></div>
      <div class="actions-container">
        <button class="sc-getting-started-button">Getting started guide</button>
        <button class="sc-report-bug-button">Report a bug</button>
        <button class="sc-request-feature-button">Request a feature</button>
        <button class="sc-share-workflow-button">Share workflow \u2B50</button>
      </div>
      <div id="smart-lookup-header-callout"></div>
    </div>
  `;
}
async function render35(scope_plugin) {
  const html = await build_html33.call(this, scope_plugin);
  const frag = this.create_doc_fragment(html);
  const container = frag.firstElementChild;
  post_process31.call(this, scope_plugin, container);
  return container;
}
async function post_process31(scope_plugin, frag) {
  const user_agreement_container = frag.querySelector("[data-user-agreement]");
  if (user_agreement_container) {
    const user_agreement = await scope_plugin.env.smart_components.render_component(
      "user_agreement_callout",
      scope_plugin
    );
    user_agreement_container.appendChild(user_agreement);
  }
  const header_link = frag.querySelector("#header-callout a");
  if (header_link) {
    header_link.addEventListener("click", (e) => {
      e.preventDefault();
      window.open(header_link.href, "_external");
    });
  }
  frag.querySelector(".sc-getting-started-button")?.addEventListener("click", () => {
    StoryModal.open(scope_plugin, {
      title: "Getting Started With Smart Connections",
      url: "https://smartconnections.app/story/smart-connections-getting-started/?utm_source=sc-op-settings"
    });
  });
  frag.querySelector(".sc-report-bug-button")?.addEventListener("click", () => {
    if (scope_plugin.env?.is_pro) {
      new ScProSupportModal(scope_plugin.app).open();
      return;
    }
    window.open(
      "https://github.com/brianpetro/obsidian-smart-connections/issues/new?template=bug_report.yml",
      "_external"
    );
  });
  frag.querySelector(".sc-request-feature-button")?.addEventListener("click", () => {
    window.open(
      "https://github.com/brianpetro/obsidian-smart-connections/issues/new?template=feature_request.yml",
      "_external"
    );
  });
  frag.querySelector(".sc-share-workflow-button")?.addEventListener("click", () => {
    window.open(
      "https://github.com/brianpetro/obsidian-smart-connections/discussions/new?category=showcase",
      "_external"
    );
  });
  const smart_lookup_header_callout = frag.querySelector("#smart-lookup-header-callout");
  if (smart_lookup_header_callout) {
    const smart_lookup_callout = await scope_plugin.env.smart_components.render_component(
      "connections_settings_lookup_callout",
      scope_plugin
    );
    smart_lookup_header_callout.appendChild(smart_lookup_callout);
  }
  return frag;
}
var ScProSupportModal = class extends import_obsidian56.Modal {
  open() {
    super.open();
    this.titleEl.setText("Need help and support?");
    const content = this.contentEl.createDiv({ cls: "sc-pro-support-modal" });
    content.createEl("p", {
      text: "Reply to your Smart Environment Pro welcome email for priority support."
    });
    const reportBugButton = content.createEl("button", { text: "Report a bug", cls: "mod-warning" });
    reportBugButton.addEventListener("click", () => {
      window.open(
        "https://github.com/brianpetro/obsidian-smart-connections/issues/new?template=bug_report.yml",
        "_external"
      );
    });
    const closeButton = content.createEl("button", { text: "Close" });
    closeButton.addEventListener("click", () => {
      this.close();
    });
  }
};

// src/components/connections-settings/lookup_callout.js
var import_obsidian57 = require("obsidian");
function build_html34(plugin, opts = {}) {
  return `<div class="wrapper">
    <div id="footer-callout" data-callout-metadata="" data-callout-fold="" data-callout="info" class="callout" style="mix-blend-mode: unset;">
      <div class="callout-title" style="align-items: center;">
        <div class="callout-icon">
        </div>
        <div class="callout-title-inner"><strong>Smart Lookup</strong></div>
      </div>
      <div class="callout-content">
        <p></p>
        <button></button>
      </div>
    </div>
  </div>`;
}
function render36(plugin, params = {}) {
  const html = build_html34.call(this, plugin, params);
  const frag = this.create_doc_fragment(html);
  const container = frag.firstElementChild;
  post_process32.call(this, plugin, container, params);
  return container;
}
function post_process32(plugin, container) {
  const icon_container = container.querySelector(".callout-icon");
  (0, import_obsidian57.setIcon)(icon_container, "smart-lookup");
  const has_lookup = plugin.app.plugins.enabledPlugins.has("smart-lookup");
  const content_container = container.querySelector(".callout-content");
  const callout_text = content_container.querySelector("p");
  const callout_btn = content_container.querySelector("button");
  if (has_lookup) {
    callout_text.textContent = "Smart Lookup now has its own settings tab.";
    callout_btn.textContent = "Open lookup settings";
    callout_btn.addEventListener("click", () => {
      plugin.app.setting.openTabById("smart-lookup");
    });
  } else {
    callout_text.textContent = "Smart Lookup is moving to a dedicated plugin. Please install Smart Lookup to continue using lookup features and access Lookup settings.";
    callout_btn.textContent = "Install Smart Lookup";
    callout_btn.addEventListener("click", () => install_smart_lookup(plugin));
  }
}
async function install_smart_lookup(plugin) {
  const app2 = plugin.app;
  const env = plugin.env;
  const adapter = app2.vault.adapter;
  async function download_and_write(url, _path) {
    try {
      const resp = await (0, import_obsidian57.requestUrl)({
        url,
        method: "GET"
      });
      await adapter.write(_path, resp.text);
      return true;
    } catch (error) {
      console.error(`Failed to download or write file from ${url} to ${_path}:`, error);
      env.events.emit("plugin:install_failed", {
        level: "error",
        message: `Failed to download or write Smart Lookup file "${_path.split("/").slice(-1)[0]}" from "${url}"`,
        event_source: "install_smart_lookup"
      });
      return false;
    }
  }
  const { json: response } = await (0, import_obsidian57.requestUrl)({
    url: "https://api.github.com/repos/brianpetro/smart-lookup-obsidian/releases/latest",
    method: "GET",
    headers: {
      "Content-Type": "application/json"
    },
    contentType: "application/json"
  });
  const assets = response.assets;
  const main_asset = assets.find((asset) => asset.name === "main.js");
  const manifest_asset = assets.find((asset) => asset.name === "manifest.json");
  const styles_asset = assets.find((asset) => asset.name === "styles.css");
  if (!main_asset || !manifest_asset || !styles_asset) {
    env.events.emit("plugin:install_failed", {
      level: "error",
      message: "Failed to find necessary assets for Smart Lookup. Installation failed.",
      event_source: "install_smart_lookup"
    });
    return;
  }
  const main_url = main_asset.browser_download_url;
  const manifest_url = manifest_asset.browser_download_url;
  const styles_url = styles_asset.browser_download_url;
  const plugin_folder = `${app2.vault.configDir}/plugins/smart-lookup`;
  if (!await adapter.exists(plugin_folder)) {
    await adapter.mkdir(plugin_folder);
  }
  const results = await Promise.all([
    download_and_write(main_url, `${plugin_folder}/main.js`),
    download_and_write(manifest_url, `${plugin_folder}/manifest.json`),
    download_and_write(styles_url, `${plugin_folder}/styles.css`)
  ]);
  if (results.some((result) => result === false)) {
    return;
  }
  await app2.plugins.loadManifests();
  if (!app2.plugins.enabledPlugins.has("smart-lookup")) {
    enable_plugin(app2, "smart-lookup");
  }
  env.events.emit("plugin:install_completed", {
    level: "info",
    message: "Smart Lookup installed.",
    event_source: "install_smart_lookup"
  });
}

// src/components/connections-view/v3.css
var v3_default2 = ".connections-view-early {\n  .connections-top-bar {\n    display: flex;\n    gap: 0.5rem 1rem;\n\n    .connections-actions {\n      display: flex;\n      flex-direction: row;\n      flex-wrap: wrap;\n      flex: 0 0 auto;\n    }\n\n    .sc-context {\n      display: flex;\n      flex-direction: column;\n      margin: 0;\n      gap: 0.1rem;\n      width: auto;\n      flex: 1 1 auto;\n\n      .sc-context-line {\n        line-height: 1.1;\n      }\n\n      .sc-context-line--parent {\n        color: var(--text-muted);\n        font-size: 0.85em;\n      }\n\n      .sc-context-line--focus {\n        color: var(--text-normal);\n        font-size: 1.05em;\n        font-weight: 600;\n      }\n    }\n  }\n  .connections-list {\n    padding-top: 0.85rem;\n\n    > .sc-collapsed ul {\n      display: none;\n    }\n    > .sc-collapsed span svg {\n      transform: rotate(-90deg);\n    }\n    > .sc-result-pinned {\n      box-shadow: 0 0 0 1px var(--interactive-accent);\n      border-radius: var(--radius-s);\n      background-color: var(--background-modifier-hover);\n    }\n  }\n}";

// src/components/connections-view/v3.js
var import_obsidian58 = require("obsidian");

// src/utils/context_lines.js
var BLOCK_SEPARATOR = "#";
var DISPLAY_SEPARATOR2 = " \u203A ";
var PATH_SEPARATOR = "/";
function get_context_lines(item) {
  const key = item.key;
  let top_line = "";
  let bottom_line = "";
  let parts = [];
  if (key.includes(BLOCK_SEPARATOR)) {
    parts = key.split(BLOCK_SEPARATOR);
    bottom_line = parts.pop().trim();
    if (bottom_line[0] === "{") {
      const lines = item.lines.join("-");
      bottom_line = parts.pop().trim() + ` Lines: ${lines}`;
    }
    top_line = parts.filter(Boolean).join(BLOCK_SEPARATOR);
  } else if (key.includes(PATH_SEPARATOR)) {
    parts = key.split(PATH_SEPARATOR);
    bottom_line = parts.pop().trim();
  }
  top_line = parts.filter(Boolean).join(DISPLAY_SEPARATOR2);
  return [top_line, bottom_line];
}

// src/utils/connections_view_refresh_handler.js
async function connections_view_refresh_handler(event) {
  const view_container = event.target.closest(".connections-view");
  const list_el = view_container?.querySelector(".connections-list");
  const entity_key = list_el?.dataset?.key;
  console.log(`Refreshing smart connections view entity ${entity_key}`);
  const refresh_entity = this.env.smart_sources.get(entity_key);
  if (refresh_entity) {
    await refresh_entity.read();
    refresh_entity.queue_import();
    await refresh_entity.collection.process_source_import_queue?.();
    this.render_view(refresh_entity);
  } else {
    console.warn("No entity found for refresh");
  }
}

// src/components/connections-view/v3.js
async function build_html35(view, opts = {}) {
  const is_paused = Boolean(view.paused);
  const pause_title = is_paused ? "Resume auto-refresh" : "Pause auto-refresh";
  const top_bar_buttons = [
    {
      title: pause_title,
      icon: is_paused ? "play-circle" : "pause-circle",
      attrs: `data-action="toggle-pause" aria-pressed="${is_paused}"`
    },
    {
      title: "More actions",
      icon: "menu",
      attrs: 'data-action="open-menu"'
    }
  ].map((btn) => `
    <button
      aria-label="${btn.title}"
      ${btn.attrs ?? ""}
    >
      ${this.get_icon_html(btn.icon)}
    </button>
  `).join("");
  const html = `<div><div class="connections-view connections-item-view sc-connections-view connections-view-early">
    <div class="sc-top-bar connections-top-bar">
      <div class="connections-actions">
        ${top_bar_buttons}
      </div>
      <p class="sc-context" data-key="">
        Loading...
      </p>
    </div>
    <div class="connections-list-container"></div>
    <div class="connections-bottom-bar"></div>
  </div></div>`;
  return html;
}
async function render37(view, opts = {}) {
  const html = await build_html35.call(this, view, opts);
  const frag = this.create_doc_fragment(html);
  this.apply_style_sheet(v3_default2);
  const container = frag.querySelector(".sc-connections-view");
  post_process33.call(this, view, container, opts);
  return frag;
}
async function post_process33(view, container, opts = {}) {
  const list_container = container.querySelector(".connections-list-container");
  const sc_top_bar_context = container.querySelector(".sc-top-bar .sc-context");
  const env = view.env;
  let connections_item = opts.connections_item;
  if (!connections_item) {
    list_container.textContent = "No source item detected for current active view.";
    return container;
  }
  let connections_list = connections_item.connections || env.connections_lists.new_item(connections_item);
  if (!container._has_listeners) {
    container._has_listeners = true;
    const pause_button = container.querySelector('[data-action="toggle-pause"]');
    pause_button?.addEventListener("click", () => {
      view.toggle_connections_paused();
    });
    const open_help = () => {
      StoryModal.open(view.plugin, {
        title: "Getting Started With Smart Connections",
        url: "https://smartconnections.app/story/smart-connections-getting-started/?utm_source=connections-view-help#page=understanding-connections-1"
      });
    };
    const menu_button = container.querySelector('[data-action="open-menu"]');
    menu_button?.addEventListener("click", (event) => {
      const menu = new import_obsidian58.Menu(view.plugin.app);
      const raw_results = Array.isArray(connections_list?.results) ? connections_list.results : [];
      const connections_state = connections_list?.item?.data?.connections || {};
      const visible_results = filter_hidden_results(raw_results, connections_state);
      menu.addItem((menu_item) => {
        menu_item.setTitle("Refresh connections").setIcon("refresh-cw").onClick(() => {
          connections_view_refresh_handler.call(view, { target: container });
        });
      });
      menu.addItem((menu_item) => {
        const context_items = build_connections_context_items({
          source_item: connections_item,
          results: visible_results
        });
        menu_item.setTitle("Send results to Smart Context").setIcon("briefcase").setDisabled(!context_items.length).onClick(async () => {
          if (!context_items.length) {
            env.events.emit("connections:send_to_context_empty", {
              level: "warning",
              message: "No connection results to send to Smart Context.",
              event_source: "connections_view_menu"
            });
            return;
          }
          const smart_context = env.smart_contexts.new_context();
          smart_context.add_items(context_items);
          smart_context.emit_event("context_selector:open");
          connections_list.emit_event("connections:sent_to_context");
        });
      });
      menu.addItem((menu_item) => {
        const links_payload = format_connections_as_links(visible_results);
        menu_item.setTitle("Copy as list of links").setIcon("copy").setDisabled(!links_payload).onClick(async () => {
          if (!links_payload) {
            env.events.emit("connections:copy_list_empty", {
              level: "warning",
              message: "No connection results to copy.",
              event_source: "connections_view_menu"
            });
            return;
          }
          await copy_to_clipboard(links_payload);
          connections_list.emit_event("connections:copied_list");
        });
      });
      menu.addItem((menu_item) => {
        const connections_settings = opts.connections_settings ?? connections_list?.settings;
        const expanded = connections_settings?.expanded_view;
        const title = expanded ? "Collapse all results" : "Expand all results";
        const icon_name = expanded ? "fold-vertical" : "unfold-vertical";
        menu_item.setTitle(title).setIcon(icon_name).onClick(() => {
          const curr_settings = opts.connections_settings ?? connections_list?.settings;
          const curr_expanded = curr_settings?.expanded_view;
          if (curr_settings) curr_settings.expanded_view = !curr_expanded;
          container.querySelectorAll(".sc-result").forEach((element) => {
            curr_expanded ? element.classList.add("sc-collapsed") : element.classList.remove("sc-collapsed");
          });
        });
      });
      env.build_menu?.("connections_list", menu, connections_list);
      menu.addSeparator();
      menu.addItem((menu_item) => {
        menu_item.setTitle("Connections settings").setIcon("settings").onClick(() => {
          view.open_settings();
        });
      });
      menu.addItem((menu_item) => {
        menu_item.setTitle("Help & getting started").setIcon("help-circle").onClick(open_help);
      });
      menu.showAtMouseEvent(event);
    });
  }
  const connections_list_component_key = opts.connections_list_component_key || connections_list.connections_list_component_key || "connections_list_v4";
  const list = await env.smart_components.render_component(connections_list_component_key, connections_list, opts);
  this.empty(list_container);
  list_container.appendChild(list);
  const entity = connections_list?.item || connections_item;
  const [top_line, bottom_line] = get_context_lines(entity);
  this.empty(sc_top_bar_context);
  const doc = sc_top_bar_context.ownerDocument;
  const context_spans = [
    { text: top_line, class_name: "sc-context-line sc-context-line--parent" },
    { text: bottom_line, class_name: "sc-context-line sc-context-line--focus" }
  ];
  context_spans.forEach((line) => {
    const span = doc.createElement("span");
    span.className = line.class_name;
    span.textContent = line.text || "\xA0";
    sc_top_bar_context.appendChild(span);
  });
  sc_top_bar_context.dataset.key = connections_item.key;
  const pause_btn = container.querySelector('[data-action="toggle-pause"]');
  if (pause_btn) {
    const update_pause_button = (paused) => {
      const next_title = paused ? "Resume auto-refresh" : "Pause auto-refresh";
      pause_btn.title = next_title;
      pause_btn.setAttribute("aria-label", `${next_title}`);
      pause_btn.setAttribute("aria-pressed", String(paused));
      this.safe_inner_html(
        pause_btn,
        this.get_icon_html(paused ? "play-circle" : "pause-circle")
      );
    };
    update_pause_button(Boolean(view.paused));
    view.register_pause_controls({ update: update_pause_button });
  }
  return container;
}

// node_modules/smart-lookup-obsidian/src/components/lookup/item_view.css
var item_view_default = ".lookup-item-view {\n  display: flex;\n  flex-direction: column;\n  gap: 1rem;\n}\n\n.lookup-item-view .lookup-query-form {\n  display: flex;\n  flex-direction: column;\n  gap: 0.5rem;\n}\n\n.lookup-item-view .lookup-query-label {\n  font-size: 0.75rem;\n  font-weight: 600;\n  letter-spacing: 0.03em;\n  text-transform: uppercase;\n  color: var(--text-muted);\n}\n\n.lookup-item-view .lookup-query-input {\n  resize: vertical;\n  min-height: 5rem;\n  padding: 0.75rem;\n  border-radius: var(--radius-m);\n  border: 1px solid var(--background-modifier-border);\n  background-color: var(--background-secondary);\n  color: var(--text-normal);\n  font-size: 0.95rem;\n  line-height: 1.5;\n}\n\n.lookup-item-view .lookup-query-input:focus {\n  outline: none;\n  border-color: var(--interactive-accent);\n  box-shadow: 0 0 0 2px color-mix(in srgb, var(--interactive-accent) 30%, transparent);\n}\n\n.lookup-item-view .lookup-query-controls {\n  display: flex;\n  align-items: center;\n  gap: 0.75rem;\n  flex-wrap: wrap;\n}\n\n.lookup-item-view .lookup-query-toggle {\n  display: inline-flex;\n  align-items: center;\n  gap: 0.5rem;\n  color: var(--text-muted);\n  font-size: 0.85rem;\n}\n\n.lookup-item-view .lookup-query-toggle input {\n  margin: 0;\n}\n\n.lookup-item-view .lookup-query-submit {\n  margin-left: auto;\n}\n\n.lookup-item-view .smart-lookup-list-container {\n  display: flex;\n  flex-direction: column;\n  gap: 0.5rem;\n}\n";

// node_modules/smart-lookup-obsidian/src/utils/lookup_query_utils.js
var DEFAULT_DEBOUNCE_MS = 300;
function create_debounced_submit(handler, delay = DEFAULT_DEBOUNCE_MS) {
  let timeout_id;
  const schedule = (value) => {
    if (timeout_id) clearTimeout(timeout_id);
    timeout_id = setTimeout(() => {
      timeout_id = void 0;
      handler(value);
    }, delay);
  };
  schedule.cancel = () => {
    if (timeout_id) {
      clearTimeout(timeout_id);
      timeout_id = void 0;
    }
  };
  return schedule;
}
function sanitize_query(value) {
  if (typeof value !== "string") return "";
  return value.trim();
}

// node_modules/smart-lookup-obsidian/src/components/lookup/item_view.js
var REQUIRED_MESSAGE = "Enter a lookup query to continue.";
var PLACEHOLDER = "Describe the idea, topic, or question you want to explore\u2026";
var INFO = "Use semantic (embeddings) search to surface relevant notes. Results are sorted by similarity to your query. Note: returns different results than lexical (keyword) search.";
var AUTO_SUBMIT_LABEL = "Auto-submit";
var AUTO_SUBMIT_INFO = "Automatically run lookup after you pause typing. Turn off to submit manually.";
var SUBMIT_LABEL = "Lookup";
async function build_html36(view, params = {}) {
  const auto_submit_checked = params.auto_submit === false ? "" : "checked";
  return `<div><div class="lookup-item-view">
    <form class="lookup-query-form" novalidate>
      <label class="lookup-query-label" for="lookup-query-input" title="${INFO}">Smart Lookup</label>
      <textarea
        class="lookup-query-input"
        id="lookup-query-input"
        name="lookup-query"
        rows="4"
        placeholder="${PLACEHOLDER}"
        required
      ></textarea>
      <div class="lookup-query-controls">
        <label class="lookup-query-toggle" title="${AUTO_SUBMIT_INFO}">
          <input
            class="lookup-query-auto-submit"
            type="checkbox"
            name="lookup-auto-submit"
            ${auto_submit_checked}
          />
          <span>${AUTO_SUBMIT_LABEL}</span>
        </label>
        <button class="mod-cta lookup-query-submit" type="submit">${SUBMIT_LABEL}</button>
      </div>
    </form>
    <div class="smart-lookup-list-container">
      <p>${INFO}</p>
    </div>
  </div></div>`;
}
async function render38(view, params = {}) {
  this.apply_style_sheet(item_view_default);
  const html = await build_html36.call(this, view, params);
  const frag = this.create_doc_fragment(html);
  const container = frag.querySelector(".lookup-item-view");
  post_process34.call(this, view, container, params);
  return container;
}
async function post_process34(view, container, params = {}) {
  const query_input = container.querySelector(".lookup-query-input");
  const query_form = container.querySelector(".lookup-query-form");
  const auto_submit_input = container.querySelector(".lookup-query-auto-submit");
  const submit_btn = container.querySelector(".lookup-query-submit");
  const list_container = container.querySelector(".smart-lookup-list-container");
  const state = { last_query: null, active_request_id: 0 };
  const render_info_state = () => {
    this.empty(list_container);
    list_container.innerHTML = `<p>${INFO}</p>`;
  };
  const sync_form_state = () => {
    const query = sanitize_query(query_input.value);
    update_query_validity({ input_el: query_input, query });
    update_submit_state({ submit_btn, query });
    return query;
  };
  const submit_query = async (raw_query) => {
    const query = sanitize_query(raw_query);
    const request_id = ++state.active_request_id;
    update_query_validity({ input_el: query_input, query });
    update_submit_state({ submit_btn, query });
    if (!query) {
      state.last_query = null;
      render_info_state();
      return;
    }
    if (query === state.last_query) return;
    state.last_query = query;
    const next_params = { ...params, query, auto_submit: auto_submit_input.checked };
    const lookup_list = view.env.lookup_lists.new_item(next_params);
    const rendered_list = await view.env.smart_components.render_component("lookup_v3_list", lookup_list, next_params);
    if (request_id !== state.active_request_id) return;
    if (sanitize_query(query_input.value) !== query) return;
    this.empty(list_container);
    list_container.appendChild(rendered_list);
  };
  const debounced_submit = create_debounced_submit(submit_query);
  query_input.addEventListener("input", () => {
    const query = sync_form_state();
    if (!query) {
      debounced_submit.cancel?.();
      submit_query(query);
      return;
    }
    if (!auto_submit_input.checked) {
      debounced_submit.cancel?.();
      return;
    }
    debounced_submit(query);
  });
  auto_submit_input.addEventListener("change", () => {
    const query = sync_form_state();
    if (!auto_submit_input.checked) {
      debounced_submit.cancel?.();
      return;
    }
    if (query) debounced_submit(query);
  });
  query_form.addEventListener("submit", (event) => {
    event.preventDefault();
    const query = sync_form_state();
    debounced_submit.cancel?.();
    submit_query(query);
  });
  sync_form_state();
  return container;
}
function update_query_validity({ input_el, query }) {
  if (!input_el?.setCustomValidity) return;
  if (!query) input_el.setCustomValidity(REQUIRED_MESSAGE);
  else input_el.setCustomValidity("");
}
function update_submit_state({ submit_btn, query }) {
  if (!submit_btn) return;
  submit_btn.disabled = !query;
}

// src/components/lookup/item_view.js
var version = "0.0.1";

// node_modules/smart-lookup-obsidian/src/components/lookup/v3/styles.css
var styles_default2 = ".lookup-item-view .smart-lookup-list {\n  a.lookup-result-file-title {\n    text-decoration: none !important;\n  }\n\n  a.lookup-result-file-title > .sc-score {\n    display: inline-block;\n    width: auto;\n    height: 1.7em;\n    padding: 0 0.3em;\n    line-height: 1.7em;\n    text-align: center;\n    font-weight: 600 !important;\n    font-size: 0.8em !important;\n    color: var(--nav-item-color) !important;\n    background: var(--background-modifier-hover);\n    border-radius: 6px;\n  }\n\n  a.lookup-result-file-title > .sc-path {\n    margin-right: -3px;\n  }\n\n  a.lookup-result-file-title > .sc-path,\n  a.lookup-result-file-title > .sc-title {\n    font-weight: bold !important;\n    text-decoration: underline;\n  }\n\n  a.lookup-result-file-title > .sc-breadcrumb:not(.sc-path, .sc-title, .sc-score) {\n    font-style: italic;\n  }\n\n  a.lookup-result-file-title > .sc-breadcrumb-separator {\n    color: color-mix(in srgb, var(--text-normal) 50%, transparent) !important;\n  }\n}\n\n\n.smart-lookup-list {\n  padding-bottom: 20px;\n\n  .tree-item-self {\n    cursor: pointer;\n\n    small {\n      color: var(--color-gray-40);\n    }\n  }\n\n  > .sc-collapsed ul {\n    display: none;\n  }\n\n  > .sc-collapsed span svg {\n    transform: rotate(-90deg);\n  }\n\n  > :not(.sc-collapsed) span svg {\n    transform: rotate(0deg);\n  }\n\n  > div {\n    span svg {\n      height: auto;\n      margin: auto 0.5em auto 0;\n      flex: none;\n    }\n\n    > span {\n      display: inline-flex;\n      width: 100%;\n      padding-left: 0;\n    }\n\n    ul {\n      margin: 0;\n      padding-left: 1.3rem;\n    }\n\n    > a {\n      display: block;\n    }\n\n    > ul > li {\n      display: block;\n    }\n  }\n\n  .lookup-result {\n    > ul {\n      list-style: none;\n      padding-left: 0;\n    }\n  }\n\n  .lookup-result.lookup-result-plaintext {\n    font-size: var(--font-ui-smaller);\n    line-height: var(--line-height-tight);\n    background-color: var(--search-result-background);\n    border-radius: var(--radius-s);\n    overflow: hidden;\n    margin: var(--size-4-1) 0 var(--size-4-2);\n    color: var(--text-muted);\n    box-shadow: 0 0 0 1px var(--background-modifier-border);\n\n    & > * li {\n      cursor: var(--cursor);\n      position: relative;\n      padding: var(--size-4-2) var(--size-4-5) var(--size-4-2) var(--size-4-3);\n      white-space: pre-wrap;\n      width: 100%;\n      border-bottom: 1px solid var(--background-modifier-border);\n    }\n  }\n\n  .lookup-result:not(.lookup-result-plaintext) {\n    cursor: pointer;\n    padding: var(--nav-item-padding);\n    padding-left: 0;\n    margin-bottom: 1px;\n    align-items: baseline;\n    border-radius: var(--radius-s);\n    font-weight: var(--nav-item-weight);\n\n    &:hover {\n      color: var(--nav-item-color-active);\n      background-color: var(--nav-item-background-active);\n      font-weight: var(--nav-item-weight-active);\n    }\n\n    span {\n      color: var(--h5-color);\n    }\n\n    small {\n      color: var(--h5-color);\n      font-size: 0.8rem;\n      font-weight: 500;\n    }\n\n    p {\n      margin-top: 0.3rem;\n      margin-bottom: 0.3rem;\n    }\n\n    ul > li {\n      h1 {\n        font-size: 1.3rem;\n      }\n\n      h2 {\n        font-size: 1.25rem;\n      }\n\n      h3 {\n        font-size: 1.2rem;\n      }\n\n      h4 {\n        font-size: 1.15rem;\n      }\n\n      h5 {\n        font-size: 1.1rem;\n      }\n\n      h6 {\n        font-size: 1.05rem;\n      }\n\n      h1,\n      h2,\n      h3,\n      h4,\n      h5,\n      h6 {\n        margin-block-start: calc(var(--p-spacing) / 2);\n        margin-block-end: calc(var(--p-spacing) / 2);\n      }\n    }\n  }\n}\n\n.mod-right-split .smart-lookup-list .lookup-result {\n  font-size: 0.88rem;\n}\n";

// node_modules/smart-lookup-obsidian/src/components/lookup/v3/list.js
async function build_html37(lookup_list, opts = {}) {
  return `<div class="smart-lookup-list" data-key="${lookup_list.item.key}"></div>`;
}
async function render39(lookup_list, opts = {}) {
  this.apply_style_sheet(styles_default2);
  const html = await build_html37.call(this, lookup_list, opts);
  const frag = this.create_doc_fragment(html);
  const container = frag.firstElementChild;
  post_process35.call(this, lookup_list, container, opts);
  return container;
}
async function post_process35(lookup_list, container, opts = {}) {
  container.dataset.key = lookup_list.key;
  const results = await lookup_list.get_results(opts);
  if (!results || !Array.isArray(results) || results.length === 0) {
    const no_results = this.create_doc_fragment('<p class="sc-no-results">No results found</p>');
    container.appendChild(no_results);
    return container;
  }
  const smart_components = lookup_list.env.smart_components;
  const result_frags = await Promise.all(results.map((result) => {
    return smart_components.render_component("lookup_v3_list_item", result, {
      event_key_domain: "lookup",
      ...opts
    });
  }));
  result_frags.forEach((result_frag) => container.appendChild(result_frag));
  return container;
}

// src/components/lookup/v3/list.js
var version2 = "0.0.1";

// node_modules/smart-lookup-obsidian/node_modules/obsidian-smart-env/src/utils/get_item_display_name.js
var DISPLAY_SEPARATOR3 = " \u203A ";
function get_item_display_name4(item, settings = {}) {
  if (!item?.key) return "";
  const show_full_path = settings.show_full_path ?? true;
  if (show_full_path) {
    return item.key.replace(/#/g, DISPLAY_SEPARATOR3).replace(/\//g, DISPLAY_SEPARATOR3);
  }
  const pcs = [];
  const [source_key, ...block_parts] = item.key.split("#");
  const filename = source_key.split("/").pop();
  pcs.push(filename);
  if (block_parts.length) {
    pcs.push(block_parts.pop());
  }
  return pcs.join(DISPLAY_SEPARATOR3);
}

// node_modules/smart-lookup-obsidian/node_modules/obsidian-smart-env/src/utils/register_block_hover_popover.js
var import_obsidian59 = require("obsidian");
function register_block_hover_popover2(parent, target, env, block_key, params = {}) {
  const app2 = env?.plugin?.app || window.app;
  target.addEventListener("mouseover", async (ev) => {
    if (import_obsidian59.Keymap.isModEvent(ev)) {
      const block = env.smart_blocks.get(block_key);
      const markdown = await block?.read();
      if (markdown) {
        const popover = new import_obsidian59.HoverPopover(parent, target);
        const frag = env.smart_view.create_doc_fragment(`<div class="markdown-embed is-loaded">
                <div class="markdown-embed-content node-insert-event">
                  <div class="markdown-preview-view markdown-rendered node-insert-event show-indentation-guide allow-fold-headings allow-fold-lists">
                    <div class="markdown-preview-sizer markdown-preview-section">
                    </div>
                  </div>
                </div>
              </div>`);
        popover.hoverEl.classList.add("smart-block-popover");
        popover.hoverEl.appendChild(frag);
        const sizer = popover.hoverEl.querySelector(".markdown-preview-sizer");
        import_obsidian59.MarkdownRenderer.render(app2, markdown, sizer, "/", popover);
        const event_domain = params.event_key_domain || "block";
        block.emit_event(`${event_domain}:hover_preview`);
      }
    }
  });
}

// node_modules/smart-lookup-obsidian/node_modules/obsidian-smart-env/src/utils/register_item_hover_popover.js
var import_obsidian60 = require("obsidian");
function register_item_hover_popover2(container, item, params = {}) {
  const app2 = item.env?.plugin?.app || window.app;
  if (item.key.indexOf("{") === -1) {
    container.addEventListener("mouseover", (event) => {
      const linktext_path = item.key.replace(/#$/, "");
      app2.workspace.trigger("hover-link", {
        event,
        source: "smart-connections-view",
        hoverParent: container.parentElement,
        targetEl: container,
        linktext: linktext_path
      });
      if (import_obsidian60.Keymap.isModEvent(event)) {
        const event_domain = params.event_key_domain || item.collection_key || "item";
        item.emit_event(`${event_domain}:hover_preview`);
      }
    });
  } else {
    register_block_hover_popover2(container.parentElement, container, item.env, item.key);
  }
}

// node_modules/smart-lookup-obsidian/node_modules/obsidian-smart-env/utils/parse_item_key_to_wikilink.js
function parse_item_key_to_wikilink2(key) {
  if (!key) return "";
  const [file_path, ...parts] = key.split("#");
  const file_name = file_path.split("/").pop().replace(/\.md$/, "");
  if (!parts.length) return `[[${file_name}]]`;
  const heading = parts.filter((part) => !part.startsWith("{")).pop();
  if (!heading) return `[[${file_name}]]`;
  return `[[${file_name}#${heading}]]`;
}

// node_modules/smart-lookup-obsidian/node_modules/obsidian-smart-env/src/utils/register_item_drag.js
function handle_connection_drag2(obsidian_app, item, params, event) {
  const drag_manager = obsidian_app.dragManager;
  const link_text = parse_item_key_to_wikilink2(item.key);
  const drag_data = drag_manager.dragLink(event, link_text);
  drag_manager.onDragStart(event, drag_data);
  if (params.drag_event_key) {
    item.emit_event(params.drag_event_key);
  } else {
    item.emit_event("connections:drag_result");
  }
}
function register_item_drag2(container, item, params = {}) {
  const env = item.env;
  const app2 = env.obsidian_app;
  container.setAttribute("draggable", "true");
  container.addEventListener("dragstart", handle_connection_drag2.bind(null, app2, item, params));
}

// node_modules/smart-lookup-obsidian/node_modules/obsidian-smart-env/src/utils/open_source.js
var import_obsidian61 = require("obsidian");
async function open_source2(item, event = null) {
  try {
    const env = item.env;
    const obsidian_app = env.obsidian_app;
    let target_path = item.key;
    if (target_path.endsWith("#")) target_path = target_path.slice(0, -1);
    let target_file;
    if (target_path.includes("#")) {
      const [file_path] = target_path.split("#");
      target_file = obsidian_app.metadataCache.getFirstLinkpathDest(file_path, "");
    } else {
      target_file = obsidian_app.metadataCache.getFirstLinkpathDest(target_path, "");
    }
    if (!target_file) {
      const message = `Unable to resolve file for ${target_path}`;
      console.warn(`[open_source] ${message}`);
      item.emit_event("sources:open_failed", {
        level: "warning",
        message,
        event_source: "open_source"
      });
      return;
    }
    let leaf;
    if (event) {
      const is_mod = import_obsidian61.Keymap.isModEvent(event);
      const is_alt = import_obsidian61.Keymap.isModifier(event, "Alt");
      if (is_mod && is_alt) {
        leaf = obsidian_app.workspace.splitActiveLeaf("vertical");
      } else if (is_mod) {
        leaf = obsidian_app.workspace.getLeaf(true);
      } else {
        leaf = obsidian_app.workspace.getMostRecentLeaf();
      }
    } else {
      leaf = obsidian_app.workspace.getMostRecentLeaf();
    }
    await leaf.openFile(target_file);
    if (typeof item?.line_start === "number") {
      const { editor } = leaf.view;
      const pos = { line: item.line_start, ch: 0 };
      editor.setCursor(pos);
      editor.scrollIntoView({ to: pos, from: pos }, true);
    }
    item.emit_event("sources:opened", { event_source: "open_source" });
  } catch (error) {
    console.error("Error in open_source:", error);
    item.emit_event("sources:open_failed", {
      level: "error",
      message: error?.message || "Failed to open source.",
      details: error?.stack || "",
      event_source: "open_source"
    });
  }
}

// node_modules/smart-lookup-obsidian/src/components/lookup/v3/list_item.js
async function build_html38(result, params = {}) {
  const item = result.item;
  const env = item.env;
  const score = result.score;
  const lookup_settings = params.lookup_settings ?? env.lookup_lists.settings;
  const component_settings = lookup_settings.components?.lookup_v3_list_item || {};
  const header_html = get_result_header_html2(score, item, component_settings);
  const all_expanded = lookup_settings.expanded_view;
  return `<div class="temp-container">
    <div
      class="lookup-result ${all_expanded ? "" : "sc-collapsed"}"
      data-path="${item.path.replace(/"/g, "&quot;")}"
      data-link="${item.link?.replace(/"/g, "&quot;") || ""}"
      data-collection="${item.collection_key}"
      data-score="${score}"
      data-key="${item.key}"
      draggable="true"
    >
      <span class="header">
        ${this.get_icon_html("right-triangle")}
        <a class="lookup-result-file-title" href="#" title="${item.path.replace(/"/g, "&quot;")}" draggable="true">
          ${header_html}
        </a>
      </span>
      <ul draggable="true">
        <li class="lookup-result-file-title" title="${item.path.replace(/"/g, "&quot;")}" data-collection="${item.collection_key}" data-key="${item.key}"></li>
      </ul>
    </div>
  </div>`;
}
async function render40(result_scope, params = {}) {
  let html = await build_html38.call(this, result_scope, params);
  const frag = this.create_doc_fragment(html);
  const container = frag.querySelector(".lookup-result");
  post_process36.call(this, result_scope, container, params);
  return container;
}
async function post_process36(result_scope, container, params = {}) {
  const { item } = result_scope;
  const env = item.env;
  const app2 = env.obsidian_app;
  const lookup_settings = params.lookup_settings ?? env.lookup_lists.settings;
  const component_settings = lookup_settings.components?.lookup_v3_list_item || {};
  const should_render_markdown = component_settings?.render_markdown ?? true;
  if (!should_render_markdown) container.classList.add("lookup-result-plaintext");
  const render_result = async (_result_elm) => {
    if (!_result_elm.querySelector("li").innerHTML) {
      const collection_key = _result_elm.dataset.collection;
      const entity = env[collection_key].get(_result_elm.dataset.path);
      let markdown;
      if (should_render_embed2(entity)) markdown = `${entity.embed_link}

${await entity.read()}`;
      else markdown = process_for_rendering2(await entity.read());
      let entity_frag;
      if (should_render_markdown) entity_frag = await this.render_markdown(markdown, entity);
      else entity_frag = this.create_doc_fragment(markdown);
      container.querySelector("li").appendChild(entity_frag);
    }
  };
  const toggle_fold_elm = container.querySelector(".header .svg-icon.right-triangle");
  toggle_fold_elm.addEventListener("click", toggle_result2);
  const event_key_domain = params.event_key_domain || "lookup";
  const drag_event_key = `${event_key_domain}:drag_result`;
  register_item_drag2(container, item, { drag_event_key });
  register_item_hover_popover2(container, item, { event_key_domain });
  container.addEventListener("click", (event) => {
    open_source2(item, event);
    item.emit_event(`${event_key_domain}:open_result`, { event_source: "lookup-v3-list-item" });
  });
  const observer = new MutationObserver((mutations) => {
    const has_expansion_change = mutations.some((mutation) => {
      const target = mutation.target;
      return mutation.attributeName === "class" && mutation.oldValue?.includes("sc-collapsed") !== target.classList.contains("sc-collapsed");
    });
    if (has_expansion_change && !mutations[0].target.classList.contains("sc-collapsed")) {
      render_result(mutations[0].target);
    }
  });
  observer.observe(container, {
    attributes: true,
    attributeOldValue: true,
    attributeFilter: ["class"]
  });
  if (!container.classList.contains("sc-collapsed")) {
    render_result(container);
  }
  return container;
}
function get_result_header_html2(score, item, component_settings = {}) {
  const raw_parts = get_item_display_name4(item, component_settings).split(DISPLAY_SEPARATOR3).filter(Boolean);
  const parts = format_item_parts2(raw_parts, item?.lines);
  const name = parts.pop();
  const formatted_score = typeof score === "number" ? score.toFixed(2) : score;
  const separator = '<small class="sc-breadcrumb-separator"> &gt; </small>';
  const parts_html = parts.map((part) => `<small class="sc-breadcrumb">${part}</small>`).join(separator);
  return [
    `<small class="sc-breadcrumb sc-score">${formatted_score}</small>`,
    `${parts_html}${separator}`,
    `<small class="sc-breadcrumb sc-title">${name.endsWith(".md") ? name.replace(/\.md$/, "") : name}</small>`
  ].join("");
}
function format_item_parts2(parts, lines = []) {
  if (!Array.isArray(parts) || !parts.length) return [];
  const has_line_marker = Array.isArray(lines) && lines.length;
  return parts.map((part) => {
    if (has_line_marker && part.startsWith("{")) {
      return `Lines: ${lines.join("-")}`;
    }
    return part;
  });
}
function should_render_embed2(entity) {
  if (!entity) return false;
  if (entity.is_media) return true;
  return false;
}
function process_for_rendering2(content) {
  if (content.includes("```dataview")) content = content.replace(/```dataview/g, "```\\dataview");
  if (content.includes("```smart-context")) content = content.replace(/```smart-context/g, "```\\smart-context");
  if (content.includes("```smart-chatgpt")) content = content.replace(/```smart-chatgpt/g, "```\\smart-chatgpt");
  if (content.includes("![[")) content = content.replace(/\!\[\[/g, "! [[");
  return content;
}
function toggle_result2(event) {
  event.preventDefault();
  event.stopPropagation();
  const _result_elm = event.target.closest(".lookup-result");
  _result_elm.classList.toggle("sc-collapsed");
}

// src/components/lookup/v3/list_item.js
var version3 = "0.0.1";

// src/actions/connections-list/pre_process.js
function pre_process2(params) {
  if (!params.limit) params.limit = this.settings?.results_limit ?? 20;
  if (!params.results_collection_key) {
    params.results_collection_key = this.collection.results_collection_key;
  }
  if (!params.filter) params.filter = {};
  if (!params.score_algo_key) params.score_algo_key = this.collection.score_algo_key;
  params.to_item = this.item;
  if (!params.filter.exclude_keys) params.filter.exclude_keys = [];
  if (!params.filter.exclude_key_starts_with_any) {
    params.filter.exclude_key_starts_with_any = [];
  }
  if (!params.filter.frontmatter) params.filter.frontmatter = {};
  if (!params.filter.frontmatter.include) {
    params.filter.frontmatter.include = this.collection.frontmatter_inclusions;
  }
  if (!params.filter.frontmatter.exclude) {
    params.filter.frontmatter.exclude = this.collection.frontmatter_exclusions;
  }
  get_connections_feedback_items(this, params);
  const exclude_keys_set = new Set(params.filter.exclude_keys);
  exclude_keys_set.add(this.item.key);
  params.hidden_keys.forEach((key) => exclude_keys_set.add(key));
  params.pinned_keys.forEach((key) => exclude_keys_set.add(key));
  params.filter.exclude_keys = Array.from(exclude_keys_set);
  if (params.results_collection_key === "smart_blocks") {
    const exclude_starts_set = new Set(params.filter.exclude_key_starts_with_any);
    exclude_starts_set.add(this.item.key);
    params.hidden_keys.forEach((key) => exclude_starts_set.add(key));
    params.pinned_keys.forEach((key) => exclude_starts_set.add(key));
    params.filter.exclude_key_starts_with_any = Array.from(exclude_starts_set);
    if (this.collection.settings.exclude_frontmatter_blocks) {
      if (!params.filter.exclude_key_ends_with_any || !Array.isArray(params.filter.exclude_key_ends_with_any)) {
        params.filter.exclude_key_ends_with_any = [];
      }
      params.filter.exclude_key_ends_with_any.push("---frontmatter---");
    }
  }
}
function get_connections_feedback_items(connections_list, params) {
  params.hidden = [];
  params.hidden_keys = [];
  params.pinned = [];
  params.pinned_keys = [];
  const connections_state = connections_list.item.data?.connections || {};
  Object.entries(connections_state).forEach(([key, state]) => {
    if (!state || state.hidden == null && state.pinned == null) return;
    const [collection_key, ...item_key_parts] = key.split(":");
    if (!collection_key || !item_key_parts.length) return;
    const item_key = item_key_parts.join(":");
    const collection = connections_list.env[collection_key];
    if (!collection) return;
    const item = collection.get(item_key);
    if (!item) return;
    if (state.hidden && !state.pinned) {
      params.hidden.push(item);
      params.hidden_keys.push(item_key);
    }
    if (state.pinned) {
      params.pinned.push(item);
      params.pinned_keys.push(item_key);
    }
  });
}

// smart_env.config.js
var smart_env_config3 = {
  collections: {
    connections_lists: connections_lists_default
  },
  items: {
    connections_list: { class: ConnectionsList, version: "2.4.4" }
  },
  modules: {},
  components: {
    connections_codeblock: { render: render29, version: "2.4.4" },
    connections_footer_view: { render: render30, version: "2.4.4" },
    connections_graph_v1: { render: render31, version: "2.4.4" },
    connections_list_item_v3: { render: render32, settings_config: settings_config11, version: "2.4.4" },
    connections_list_v3: { render: render33, display_name: display_name5, version: "2.4.4" },
    connections_list_v4: { render: render34, settings_config: settings_config12, display_name: display_name6, version: "2.4.4" },
    connections_settings_header: { render: render35, version: "2.4.4" },
    connections_settings_lookup_callout: { render: render36, version: "2.4.4" },
    connections_view_v3: { render: render37, version: "2.4.4" },
    lookup_item_view: { render: render38, version },
    lookup_v3_list: { render: render39, version: version2 },
    lookup_v3_list_item: { render: render40, version: version3 }
  },
  actions: {
    connections_list_pre_process: { action: pre_process2, pre_process: pre_process2, version: "2.4.4" }
  }
};

// node_modules/obsidian-smart-env/utils/open_note.js
var import_obsidian62 = require("obsidian");
async function open_note(plugin, target_path, event = null, opts = {}) {
  const { new_tab = false } = opts;
  const env = plugin.env;
  if (target_path.endsWith("#")) target_path = target_path.slice(0, -1);
  let target_file;
  let block = null;
  if (target_path.includes("#")) {
    const [file_path] = target_path.split("#");
    target_file = plugin.app.metadataCache.getFirstLinkpathDest(file_path, "");
    block = env.smart_blocks.get(target_path);
  } else {
    target_file = plugin.app.metadataCache.getFirstLinkpathDest(target_path, "");
  }
  if (!target_file) {
    console.warn(`[open_note] Unable to resolve file for ${target_path}`);
    return;
  }
  let leaf;
  if (event) {
    const is_mod = import_obsidian62.Keymap.isModEvent(event);
    const is_alt = import_obsidian62.Keymap.isModifier(event, "Alt");
    if (is_mod && is_alt) {
      leaf = plugin.app.workspace.splitActiveLeaf("vertical");
    } else if (is_mod || new_tab) {
      leaf = plugin.app.workspace.getLeaf(true);
    } else {
      leaf = plugin.app.workspace.getMostRecentLeaf();
    }
  } else {
    leaf = plugin.app.workspace.getMostRecentLeaf();
  }
  await leaf.openFile(target_file);
  if (typeof block?.line_start === "number") {
    const { editor } = leaf.view;
    const pos = { line: block.line_start, ch: 0 };
    editor.setCursor(pos);
    editor.scrollIntoView({ to: pos, from: pos }, true);
  }
}

// src/views/settings_tab.js
var ScEarlySettingsTab = class extends SmartPluginSettingsTab {
  constructor(app2, plugin) {
    super(app2, plugin);
    this.plugin = plugin;
  }
  hide() {
    super.hide?.();
    this.plugin_container?.empty?.();
    this.turn_off_listener?.();
  }
  async render_header(container) {
    const header = await this.env.smart_components.render_component("connections_settings_header", this.plugin);
    container.appendChild(header);
  }
  async render_plugin_settings(container) {
    if (!container) return;
    container.empty?.();
    container.innerHTML = '<div class="sc-loading">Loading main settings...</div>';
    container.empty?.();
    const cl_container = container.createDiv({
      cls: "sc-settings-tab__section",
      attr: { "data-section-key": "connections_lists" }
    });
    cl_container.createEl("h1", { text: "Connections" });
    const connections_lists_settings_config = this.env.config.collections.connections_lists.settings_config;
    render_settings_config(
      connections_lists_settings_config,
      this.env.connections_lists,
      cl_container,
      {
        default_group_name: "Connections lists",
        group_params: {
          "Connections lists": {
            heading_btn: [
              {
                label: "Learn about Connections Lists",
                btn_text: "Learn more",
                callback: () => window.open("https://smartconnections.app/smart-connections/list-feature/?utm_source=connections-settings-tab", "_external")
              },
              {
                label: "Settings documentation for Connections Lists",
                btn_icon: "help-circle",
                callback: () => window.open("https://smartconnections.app/smart-connections/settings/?utm_source=connections-settings-tab#connections-lists", "_external")
              }
            ],
            order: 1
          },
          "Display": {
            heading_btn: {
              label: "Settings documentation for Display",
              btn_icon: "help-circle",
              callback: () => window.open("https://smartconnections.app/smart-connections/settings/?utm_source=connections-settings-tab#display", "_external")
            },
            order: 2
          },
          "Connections list item": {
            heading_btn: {
              label: "Settings documentation for Connections List Items",
              btn_icon: "help-circle",
              callback: () => window.open("https://smartconnections.app/smart-connections/settings/?utm_source=connections-settings-tab#connections-list-item", "_external")
            },
            order: 3
          },
          "Score algorithm": {
            heading_btn: {
              label: "Settings documentation for Score Algorithms",
              btn_icon: "help-circle",
              callback: () => window.open("https://smartconnections.app/smart-connections/settings/?utm_source=connections-settings-tab#score-algorithm", "_external")
            },
            order: 4
          },
          "Ranking algorithm": {
            heading_btn: {
              label: "Settings documentation for Ranking Algorithms",
              btn_icon: "help-circle",
              callback: () => window.open("https://smartconnections.app/smart-connections/settings/?utm_source=connections-settings-tab#ranking-algorithm", "_external")
            },
            order: 5
          },
          "Connections filters": {
            heading_btn: {
              label: "Settings documentation for Filters",
              btn_icon: "help-circle",
              callback: () => window.open("https://smartconnections.app/smart-connections/settings/?utm_source=connections-settings-tab#filters", "_external")
            },
            order: 6
          },
          "Inline connections": {
            heading_btn: [
              {
                label: "Learn about the inline connections feature",
                btn_text: "Learn more",
                callback: () => window.open("https://smartconnections.app/smart-connections/inline/?utm_source=connections-settings-tab", "_external")
              },
              {
                label: "Settings documentation for inline connections",
                btn_icon: "help-circle",
                callback: () => window.open("https://smartconnections.app/smart-connections/settings/?utm_source=connections-settings-tab#inline-connections", "_external")
              }
            ],
            order: 7
          },
          "Footer connections": {
            heading_btn: {
              label: "Settings documentation for Footer Connections",
              btn_icon: "help-circle",
              callback: () => window.open("https://smartconnections.app/smart-connections/settings/?utm_source=connections-settings-tab#footer-connections", "_external")
            },
            order: 8
          }
        }
      }
    );
    this.register_env_events();
  }
  register_env_events() {
    if (this.turn_off_listener || !this.env?.events) return;
    this.turn_off_listener = this.env.events.on("settings:changed", (event) => {
      if (event.path?.includes("connections_post_process") || event.path?.includes("score_algo_key") || event.path?.includes("connections_list_item")) {
        this.render_plugin_settings(this.plugin_container);
      }
    });
  }
};

// node_modules/obsidian-smart-env/views/release_notes_view.js
var import_obsidian63 = require("obsidian");
var ReleaseNotesView = class extends SmartItemView {
  static view_type = "smart-release-notes-view";
  static display_text = "Release Notes";
  static icon_name = "file-text";
  static plugin_id = "";
  static release_notes_md = "";
  static open(workspace, version4, active = true) {
    const leaf = workspace.getLeaf("tab");
    leaf.setViewState({ type: this.view_type, active, state: { version: version4 } });
  }
  onOpen() {
    this.titleEl.setText(`What's new in v${this.version}? (${this.constructor.plugin_id})`);
    this.render();
  }
  get container() {
    const content = this.containerEl?.querySelector(".view-content");
    let preview = content?.querySelector(".markdown-preview-view");
    if (!preview) {
      const main = content?.createDiv("cm-scroller is-readable-line-width");
      preview = main?.createDiv("markdown-preview-view markdown-rendered");
    }
    return preview;
  }
  async render() {
    while (!this.container) {
      await new Promise((resolve) => setTimeout(resolve, 100));
      console.warn("Waiting for containerEl to be ready...", this.container);
    }
    await import_obsidian63.MarkdownRenderer.render(
      this.app,
      this.constructor.release_notes_md,
      this.container,
      "",
      this
    );
    this.container.querySelectorAll("a").forEach((a) => {
      a.setAttribute("target", "_external");
    });
    requestAnimationFrame(() => this.scroll_to_version());
  }
  get version() {
    const view_version = this.leaf.viewState?.state?.version;
    if (view_version) return view_version;
    const plugin_id = this.constructor.plugin_id;
    return this.app.plugins.getPlugin(plugin_id)?.manifest.version ?? "";
  }
  scroll_to_version() {
    const matcher = build_version_matcher(this.version);
    if (!matcher) return;
    this.container.querySelectorAll("h1,h2,h3,h4,h5,h6").forEach((heading) => {
      if (!heading_matches_version({ matcher, heading_text: heading.textContent })) return;
      heading.scrollIntoView({ behavior: "smooth", block: "start" });
    });
  }
};
function escape_regex(value) {
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function build_version_matcher(version4) {
  if (!version4) return null;
  const safe_version = escape_regex(version4);
  return new RegExp(`\\bv?${safe_version}\\b`, "i");
}
function heading_matches_version({ matcher, heading_text }) {
  if (!matcher) return false;
  return matcher.test(heading_text ?? "");
}

// releases/latest_release.md
var latest_release_default = "# Smart Connections `v4.5`\n## Connections Footer is now a Core feature!\nFooter connections are now included in Smart Connections Core, bringing the most mobile-friendly and no-panel writing surface to every install. Connections Pro continues to add inline discovery, Bases workflows, and advanced ranking control.\n- Place your connections list in the footer of every note.\n- Toggle footer connections from the command palette (hotkey), ribbon icon, or settings.\n\n## Recent highlights\n- Connections lists can now open with a graph view.\n- [Substrate Update.](https://smartconnections.app/smart-plugins/substrate-update/)\n\n## Additional notes\n\n- Fixed: transformers embedding model should fallback to non-GPU v4 usage and subsequently v3 if that still fails\n- Fixed: should only calculate connections results once (improves performance)\n\n[More details about the latest releases](https://smartconnections.app/smart-connections/releases/4-5/)\n";

// src/views/release_notes_view.js
var ReleaseNotesView2 = class extends ReleaseNotesView {
  static view_type = "smart-release-notes-view";
  static plugin_id = "smart-connections";
  static release_notes_md = latest_release_default;
};

// src/utils/get_random_connection.js
var DEFAULT_RESULTS_LIMIT = 20;
async function get_random_connection(env, file_path, { rng = Math.random } = {}) {
  if (!env?.smart_sources || !file_path) return null;
  const source = env.smart_sources.get(file_path);
  if (!source?.should_embed) return null;
  const connections = await source.connections.get_results({ limit: DEFAULT_RESULTS_LIMIT });
  if (!Array.isArray(connections) || connections.length === 0) return null;
  return pick_weighted_connection(connections, { rng });
}
function pick_weighted_connection(connections, { rng }) {
  const scored_connections = connections.map((connection) => ({
    connection,
    score: Math.max(0, typeof connection?.score === "number" ? connection.score : 0)
  }));
  const total_score = scored_connections.reduce((sum, { score }) => sum + score, 0);
  if (total_score === 0) return connections[0];
  const threshold = rng() * total_score;
  let cumulative = 0;
  for (const { connection, score } of scored_connections) {
    cumulative += score;
    if (threshold < cumulative) return connection;
  }
  return connections.at(-1);
}

// src/utils/add_icons.js
var import_obsidian64 = require("obsidian");
function add_smart_dice_icon() {
  (0, import_obsidian64.addIcon)("smart-dice", `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <rect x="1" y="1" width="22" height="22" rx="2" fill="none"/>

  <g transform="translate(12 10) scale(0.18) translate(-50 -50)">
    <path d="M50 20 L80 40 L80 60 L50 100" fill="none" stroke="currentColor" stroke-width="4"/>
    <path d="M30 50 L55 70" fill="none" stroke="currentColor" stroke-width="5"/>
    <circle cx="50" cy="20" r="9" fill="currentColor"/>
    <circle cx="75" cy="40" r="9" fill="currentColor"/>
    <circle cx="75" cy="70" r="9" fill="currentColor"/>
    <circle cx="50" cy="100" r="9" fill="currentColor"/>
    <circle cx="25" cy="50" r="9" fill="currentColor"/>
  </g>
</svg>
`);
}

// src/utils/view_leaf_location.js
var is_descendant_of = (node, ancestor) => {
  let current = node;
  while (current) {
    if (current === ancestor) {
      return true;
    }
    current = current.parent;
  }
  return false;
};
var get_leaf_location = ({ workspace, leaf }) => {
  if (!workspace || !leaf) {
    return "root";
  }
  const { leftSplit, rightSplit } = workspace;
  if (is_descendant_of(leaf, leftSplit)) {
    return "left";
  }
  if (is_descendant_of(leaf, rightSplit)) {
    return "right";
  }
  return "root";
};
var should_relocate_leaf = ({ workspace, leaf, desired_location }) => {
  if (!leaf || desired_location !== "left" && desired_location !== "right") {
    return false;
  }
  const leaf_location = get_leaf_location({ workspace, leaf });
  return leaf_location !== desired_location;
};

// src/utils/pause_controls.js
function apply_pause_state(target, paused) {
  const value = Boolean(paused);
  target.paused = value;
  target.pause_controls?.update?.(value);
  return value;
}
function toggle_pause_state(target) {
  const next = !target.paused;
  target.paused = next;
  target.pause_controls?.update?.(next);
  return next;
}

// src/views/connections_item_view.js
var ConnectionsItemView = class extends SmartItemView {
  static get view_type() {
    return "smart-connections-view";
  }
  static get display_text() {
    return "Connections";
  }
  static get icon_name() {
    return "smart-connections";
  }
  constructor(leaf, plugin) {
    super(leaf, plugin);
    this.paused = false;
    this.pause_controls = null;
    this.current = null;
  }
  async render_view(params = {}, container = this.container) {
    if (!params.connections_item) {
      const active_path = this.plugin.app.workspace.getActiveFile()?.path;
      params.connections_item = this.env.smart_sources.get(active_path);
    }
    this.current = params.connections_item;
    this.pause_controls = null;
    const frag = await this.env.smart_components.render_component("connections_view_v3", this, {
      connections_item: params.connections_item
    });
    container.empty();
    container.appendChild(frag);
    this.register_env_listeners();
    this.env.events.emit("connections:opened");
  }
  async open_settings() {
    await this.app.setting.open();
    await this.app.setting.openTabById(this.plugin.manifest.id);
  }
  register_env_listeners() {
    let handle_current_source_debounce;
    register_env_event_listener(this, "sources:opened", (event = {}) => {
      if (this.paused) return;
      if (!is_visible(this.container)) return;
      const connections_item = this.env[event.collection_key || "smart_sources"]?.get(event.item_key || event.key);
      if (connections_item.key === this.current?.key) return;
      if (handle_current_source_debounce) clearTimeout(handle_current_source_debounce);
      handle_current_source_debounce = setTimeout(() => {
        this.render_view({ connections_item });
      }, 250);
    });
    register_env_event_listener(this, "settings:changed", (event) => {
      if (event.path?.includes("expanded_view")) return;
      if (event.path?.includes("connections_lists") && is_visible(this.container)) {
        this.render_view({ connections_item: this.current });
      }
    });
    register_env_event_listener(this, "connections:show", (event) => {
      console.log("connections:show event received", { event });
      if (event.collection_key && event.item_key) {
        const collection = this.env[event.collection_key];
        const item = collection.get(event.item_key);
        console.log({ collection, item });
        if (item) {
          this.set_connections_paused(true);
          this.render_view({ connections_item: item });
        }
      }
    });
    register_env_event_listener(this, "items:embedded", (event = {}) => {
      if (event.collection_key === this.current?.collection_key && event.keys?.includes(this.current?.key) && is_visible(this.container)) {
        this.render_view({ connections_item: this.current });
      }
    });
  }
  /**
   * Register UI controls for reflecting pause state.
   * @param {{ update: (paused: boolean) => void }} controls
   */
  register_pause_controls(controls) {
    this.pause_controls = controls;
    this.pause_controls?.update(this.paused);
  }
  /**
   * Set the paused state and sync UI controls.
   * @param {boolean} paused
   * @returns {boolean}
   */
  set_connections_paused(paused) {
    return apply_pause_state(this, paused);
  }
  /**
   * Toggle the paused state and sync UI controls.
   * @returns {boolean}
   */
  toggle_connections_paused() {
    return toggle_pause_state(this);
  }
};
function is_visible(container) {
  if (!container) {
    return false;
  }
  if (!container.isConnected) {
    console.warn("Connections container is not connected to DOM");
    return false;
  }
  if (typeof container.checkVisibility === "function" && container.checkVisibility() === false) {
    console.log("Connections container is not visible");
    return false;
  }
  return true;
}
var view_event_registry = /* @__PURE__ */ new WeakMap();
var get_registry = (view) => {
  if (!view_event_registry.has(view)) {
    view_event_registry.set(view, /* @__PURE__ */ new Map());
  }
  return view_event_registry.get(view);
};
var register_env_event_listener = (view, event_key, callback) => {
  if (!view || typeof view.env?.events?.on !== "function") {
    console.warn("View or event system not available for registering event listener");
    return () => {
    };
  }
  const registry = get_registry(view);
  const previous_dispose = registry.get(event_key);
  if (typeof previous_dispose === "function") {
    previous_dispose();
  }
  const off = view.env.events.on(event_key, (event) => {
    callback(event);
  });
  let active = true;
  const dispose = () => {
    if (!active) return;
    active = false;
    off?.();
    if (registry.get(event_key) === dispose) {
      registry.delete(event_key);
    }
  };
  registry.set(event_key, dispose);
  if (typeof view.register === "function") {
    view.register(() => dispose());
  }
  return dispose;
};

// node_modules/smart-lookup-obsidian/node_modules/obsidian-smart-env/views/smart_item_view.js
var import_obsidian70 = require("obsidian");

// node_modules/smart-lookup-obsidian/node_modules/obsidian-smart-env/utils/wait_for_env_to_load.js
var import_obsidian65 = require("obsidian");
async function wait_for_env_to_load2(scope, opts = {}) {
  const { wait_for_states = ["loaded"] } = opts;
  const container = scope.container || scope.containerEl;
  if (!wait_for_states.includes(scope.env?.state)) {
    let clicked_load_env = false;
    while (scope.env.state === "init" && import_obsidian65.Platform.isMobile && !clicked_load_env) {
      if (container) {
        container.empty();
        scope.env.smart_view.safe_inner_html(container, "<button>Load Smart Environment</button>");
        container.querySelector("button").addEventListener("click", () => {
          clicked_load_env = true;
          if (typeof scope.env.start_mobile_env_load === "function") {
            scope.env.start_mobile_env_load({ source: "wait_for_env_to_load" });
            return;
          }
          scope.env.load(true);
        });
      } else {
        console.log("Waiting for env to load (mobile)...");
      }
      await new Promise((r) => setTimeout(r, 2e3));
    }
    while (!wait_for_states.includes(scope.env.state)) {
      if (container) {
        const loading_msg = scope.env?.obsidian_is_syncing ? "Waiting for Obsidian Sync to finish..." : "Loading Obsidian Smart Environment...";
        container.empty();
        scope.env.smart_view.safe_inner_html(container, loading_msg);
      } else {
        console.log("Waiting for env to load...");
      }
      await new Promise((r) => setTimeout(r, 2e3));
    }
  }
}

// node_modules/smart-lookup-obsidian/node_modules/obsidian-smart-env/src/components/status_bar.js
var import_obsidian69 = require("obsidian");

// node_modules/smart-lookup-obsidian/node_modules/obsidian-smart-env/src/utils/register_status_bar_context_menu.js
var import_obsidian68 = require("obsidian");

// node_modules/smart-lookup-obsidian/node_modules/obsidian-smart-env/views/source_inspector.js
var import_obsidian66 = require("obsidian");
var SmartNoteInspectModal2 = class extends import_obsidian66.Modal {
  constructor(smart_connections_plugin, entity) {
    super(smart_connections_plugin.app);
    this.smart_connections_plugin = smart_connections_plugin;
    this.entity = entity;
  }
  get env() {
    return this.smart_connections_plugin.env;
  }
  onOpen() {
    this.titleEl.innerText = this.entity.key;
    this.render();
  }
  async render() {
    this.contentEl.empty();
    const frag = await this.env.smart_components.render_component("source_inspector", this.entity);
    this.contentEl.appendChild(frag);
  }
};

// node_modules/smart-lookup-obsidian/node_modules/obsidian-smart-env/src/modals/env_stats.js
var import_obsidian67 = require("obsidian");
var EnvStatsModal2 = class extends import_obsidian67.Modal {
  constructor(app2, env) {
    super(app2);
    this.env = env;
  }
  onOpen() {
    this.titleEl.setText("Smart Environment");
    this.contentEl.empty();
    this.contentEl.createEl("p", { text: "Loading stats..." });
    setTimeout(this.render.bind(this), 100);
  }
  async render() {
    const frag = await this.env.smart_components.render_component("env_stats", this.env);
    this.contentEl.empty();
    if (frag) {
      this.contentEl.appendChild(frag);
    } else {
      this.contentEl.createEl("p", { text: "Failed to load stats." });
    }
  }
};

// node_modules/smart-lookup-obsidian/node_modules/obsidian-smart-env/src/utils/register_status_bar_context_menu.js
function register_status_bar_context_menu2(env, status_container, deps = {}) {
  const { Menu: MenuClass = import_obsidian68.Menu } = deps;
  const plugin = env.main;
  const on_context_menu = (ev) => {
    ev.preventDefault();
    ev.stopPropagation();
    const menu = new MenuClass(plugin.app);
    menu.addItem(
      (item) => item.setTitle("Inspect active note").setIcon("search").onClick(async () => {
        const active_file = plugin.app.workspace.getActiveFile();
        if (!active_file) {
          env?.events?.emit?.("status_bar:inspect_active_note_missing", {
            level: "warning",
            message: "No active note found",
            event_source: "register_status_bar_context_menu.inspect"
          });
          return;
        }
        const src = env.smart_sources?.get(active_file.path);
        if (!src) {
          env?.events?.emit?.("status_bar:inspect_source_missing", {
            level: "warning",
            message: "Active note is not indexed by Smart Environment",
            event_source: "register_status_bar_context_menu.inspect"
          });
          return;
        }
        new SmartNoteInspectModal2(plugin, src).open();
      })
    );
    menu.addItem(
      (item) => item.setTitle("Show stats").setIcon("chart-pie").onClick(() => {
        const modal = new EnvStatsModal2(plugin.app, env);
        modal.open();
      })
    );
    menu.addItem(
      (item) => item.setTitle("Export data").setIcon("download").onClick(() => {
        env.export_json();
        env?.events?.emit?.("smart_env:exported", {
          level: "attention",
          message: "Smart Env exported",
          event_source: "register_status_bar_context_menu.export"
        });
      })
    );
    menu.addItem(
      (item) => item.setTitle("Milestones").setIcon("flag").onClick(() => {
        env.open_milestones_modal();
      })
    );
    menu.addItem(
      (item) => item.setTitle("Notifications").setIcon("bell").onClick(() => {
        env.open_notifications_feed_modal();
      })
    );
    menu.addSeparator();
    menu.addItem(
      (item) => item.setTitle("Browse Smart Plugins").setIcon("package").onClick(() => {
        env.events?.emit?.("smart_plugins:browse", {
          event_source: "status_bar"
        });
      })
    );
    if (env.is_pro) {
      menu.addItem(
        (item) => item.setTitle("Refer a friend (Give 30, Get 30)").setIcon("hand-heart").onClick(() => {
          const url = "https://smartconnections.app/my-referrals/?utm_source=status-bar";
          window.open(url, "_external");
        })
      );
    } else {
      menu.addItem(
        (item) => item.setTitle("Start 14-day Pro trial").setIcon("hand-heart").onClick(() => {
          const url = "https://smartconnections.app/pro-plugins/?utm_source=status-bar";
          window.open(url, "_external");
        })
      );
    }
    menu.showAtPosition({ x: ev.pageX, y: ev.pageY });
  };
  plugin.registerDomEvent(status_container, "contextmenu", on_context_menu);
  return on_context_menu;
}

// node_modules/smart-lookup-obsidian/node_modules/obsidian-smart-env/node_modules/smart-events/event_level_utils.js
var notification_levels2 = Object.freeze([
  "milestone",
  "attention",
  "error",
  "warning",
  "info"
]);
var notification_level_set2 = new Set(notification_levels2);
function normalize_event_level2(level) {
  if (typeof level !== "string") return null;
  const normalized_level = level.trim().toLowerCase();
  if (!notification_level_set2.has(normalized_level)) return null;
  return normalized_level;
}
function get_legacy_notification_level2(event_key = "") {
  if (typeof event_key !== "string") return null;
  const trimmed_event_key = event_key.trim().toLowerCase();
  if (!trimmed_event_key.startsWith("notification:")) return null;
  const [, legacy_level = ""] = trimmed_event_key.split(":");
  return normalize_event_level2(legacy_level);
}
function get_display_fallback_level2(event_key = "") {
  if (typeof event_key !== "string") return null;
  const trimmed_event_key = event_key.trim().toLowerCase();
  const event_key_parts = trimmed_event_key.split(":").filter(Boolean);
  const last_part = event_key_parts[event_key_parts.length - 1];
  if (last_part === "error") return "error";
  return null;
}
function get_event_level2(event_key = "", event = {}, params = {}) {
  const { allow_display_fallback = false } = params;
  const payload_level = normalize_event_level2(event?.level);
  if (payload_level) return payload_level;
  const legacy_level = get_legacy_notification_level2(event_key);
  if (legacy_level) return legacy_level;
  if (allow_display_fallback) {
    return get_display_fallback_level2(event_key);
  }
  return null;
}

// node_modules/smart-lookup-obsidian/node_modules/obsidian-smart-env/src/utils/event_logs_utils.js
var notice_timeout_ms2 = 7e3;
var milestone_notice_timeout_ms2 = 12e3;
function get_timeout_override2(event = {}) {
  const timeout_value = event.timeout_ms ?? event.timeout;
  if (typeof timeout_value !== "number" || !Number.isFinite(timeout_value)) return null;
  if (timeout_value < 0) return null;
  return timeout_value;
}
function get_native_notice_timeout2(event_key, event = {}) {
  const timeout_override = get_timeout_override2(event);
  if (timeout_override !== null) return timeout_override;
  const level = get_event_level2(event_key, event);
  if (level === "milestone") return milestone_notice_timeout_ms2;
  return notice_timeout_ms2;
}
function get_native_notice_message2(event_key, event = {}) {
  if (typeof event?.message === "string" && event.message.trim()) return event.message.trim();
  if (typeof event?.details === "string" && event.details.trim()) return event.details.trim();
  if (typeof event?.milestone === "string" && event.milestone.trim()) return event.milestone.trim();
  return event_key || "notification";
}

// node_modules/smart-lookup-obsidian/node_modules/obsidian-smart-env/src/utils/status_bar_state.js
var indicator_level_rank2 = {
  info: 1,
  milestone: 2,
  attention: 3,
  warning: 4,
  error: 5
};
function get_unseen_notification_entries2(event_logs) {
  const session_events = event_logs?.session_events;
  if (!Array.isArray(session_events)) return [];
  return session_events.filter((entry) => entry?.unseen === true);
}
function get_notification_event_count2(event_logs) {
  return get_unseen_notification_entries2(event_logs).length;
}
function get_next_indicator_level2(current_level, event_key = "", event = {}) {
  const next_level = get_event_level2(event_key, event);
  if (!next_level) return current_level ?? null;
  const current_rank = indicator_level_rank2[current_level] || 0;
  const next_rank = indicator_level_rank2[next_level] || 0;
  if (next_rank > current_rank) return next_level;
  return current_level ?? next_level;
}
function get_notification_indicator_level2(event_logs) {
  return get_unseen_notification_entries2(event_logs).reduce((current_level, entry) => {
    return get_next_indicator_level2(current_level, entry?.event_key, entry?.event);
  }, null);
}
function get_status_bar_notice_line2(event_key = "", event = {}) {
  const message = get_native_notice_message2(event_key, event);
  return String(message || "").split(/\r?\n/u)[0].trim();
}
function format_status_bar_notice_message2(value = "") {
  const text = typeof value === "string" ? value.trim() : "";
  if (!text) return "";
  if (text.length <= 20) return text;
  return `${text.slice(0, 20)}\u2026`;
}
function get_status_bar_notice_preview2(event_logs) {
  const session_events = event_logs?.session_events;
  if (!Array.isArray(session_events) || session_events.length === 0) return null;
  for (let i = session_events.length - 1; i >= 0; i -= 1) {
    const entry = session_events[i];
    const event_key = typeof entry?.event_key === "string" ? entry.event_key : "";
    const event = entry?.event && typeof entry.event === "object" ? entry.event : {};
    if (!get_event_level2(event_key, event)) continue;
    if (entry?.native_notice_shown === true) return null;
    const timeout_ms = get_native_notice_timeout2(event_key, event);
    const expires_at = get_entry_timestamp3(entry) + timeout_ms;
    if (Date.now() > expires_at) return null;
    const title = get_status_bar_notice_line2(event_key, event);
    const message = format_status_bar_notice_message2(title);
    if (!message) return null;
    return { message, title };
  }
  return null;
}
function get_import_progress_state2(env) {
  return env?.smart_sources?.get_import_progress_state?.() || null;
}
function get_embed_progress_state2(env) {
  return env?.smart_sources?.entities_vector_adapter?.get_progress_state?.() || null;
}
function get_reimport_queue_count2(env) {
  return Object.keys(env?.smart_sources?.sources_re_import_queue || {}).length;
}
function get_progress_pct2(progress, total) {
  if (typeof progress !== "number" || typeof total !== "number") return null;
  if (total <= 0) return null;
  return Math.round(progress / total * 1e3) / 10;
}
function normalize_number2(value) {
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
}
function to_non_empty_string2(value) {
  return typeof value === "string" ? value.trim() : "";
}
function format_collection_label2(collection_key = "") {
  return collection_key.split("_").filter(Boolean).map((part) => part.slice(0, 1).toUpperCase() + part.slice(1)).join(" ");
}
function get_collection_loading_state2(env) {
  const collection_entries = Object.entries(env?.collections || {}).filter(([collection_key]) => Boolean(collection_key));
  const total = collection_entries.length;
  const loaded = collection_entries.filter(([, state]) => state === "loaded").length;
  const pending_entries = collection_entries.filter(([, state]) => state !== "loaded");
  const current_key = pending_entries[0]?.[0] || "";
  return {
    total,
    loaded,
    pending: Math.max(0, total - loaded),
    current_key,
    current_label: current_key ? format_collection_label2(current_key) : ""
  };
}
function get_env_activity_state2(env) {
  const import_progress = get_import_progress_state2(env);
  const embed_progress = get_embed_progress_state2(env);
  const reimport_queue_count = get_reimport_queue_count2(env);
  const version4 = `v${env?.constructor?.version || ""}`;
  const default_message = `${env?.is_pro ? "Pro" : "Smart"} ${version4}`;
  if (import_progress?.active) {
    const progress = normalize_number2(import_progress.progress);
    const total = normalize_number2(import_progress.total);
    const stage = to_non_empty_string2(import_progress.stage) || "importing";
    const is_reimporting = stage === "reimporting";
    return {
      kind: is_reimporting ? "reimporting" : "importing",
      message: `${is_reimporting ? "Re-importing" : "Importing"} ${progress}/${total}`,
      title: is_reimporting ? "Smart Environment is re-importing queued sources." : "Smart Environment is importing sources.",
      click_action: "noop",
      indicator_level: null,
      progress_value: progress,
      progress_total: total,
      progress_pct: get_progress_pct2(progress, total),
      view_title: is_reimporting ? "Re-importing sources" : "Importing sources",
      view_status: `${progress}/${total}`,
      view_summary: is_reimporting ? "Refreshing queued source changes before embeddings continue." : "Discovering and importing sources into Smart Environment.",
      view_details: [
        reimport_queue_count > 0 ? `${reimport_queue_count} additional source${reimport_queue_count === 1 ? "" : "s"} queued.` : "",
        env?.state === "loading" ? "Smart Environment is still loading in the background." : ""
      ].filter(Boolean),
      view_actions: []
    };
  }
  if (embed_progress?.active) {
    const progress = normalize_number2(embed_progress.progress);
    const total = normalize_number2(embed_progress.total);
    const paused = Boolean(embed_progress.paused);
    const tokens_per_second = normalize_number2(embed_progress.tokens_per_second);
    const model_name = to_non_empty_string2(embed_progress.model_name);
    const reason = to_non_empty_string2(embed_progress.reason);
    return {
      kind: paused ? "embed_paused" : "embed_active",
      message: `${paused ? "Embedding paused" : "Embedding"} ${progress}/${total}`,
      title: paused ? "Click to resume embedding." : "Click to pause embedding.",
      click_action: paused ? "resume_embed" : "pause_embed",
      indicator_level: paused ? "attention" : "milestone",
      progress_value: progress,
      progress_total: total,
      progress_pct: get_progress_pct2(progress, total),
      view_title: paused ? "Embedding paused" : "Embedding in progress",
      view_status: `${progress}/${total}`,
      view_summary: model_name ? `Using ${model_name} for embeddings.` : "Generating embeddings for imported content.",
      view_details: [
        tokens_per_second > 0 ? `${tokens_per_second} tokens/sec` : "",
        reason,
        reimport_queue_count > 0 ? `${reimport_queue_count} source${reimport_queue_count === 1 ? "" : "s"} still queued for re-import.` : ""
      ].filter(Boolean),
      view_actions: [paused ? "resume_embed" : "pause_embed"]
    };
  }
  if (env?.state === "loading") {
    const collection_loading = get_collection_loading_state2(env);
    const collection_progress_value = collection_loading.total > 0 ? collection_loading.loaded : null;
    const collection_progress_total = collection_loading.total > 0 ? collection_loading.total : null;
    const current_collection_label = collection_loading.current_label;
    const collection_status = collection_loading.total > 0 ? `${collection_loading.loaded}/${collection_loading.total}` : "In progress";
    return {
      kind: "loading",
      message: current_collection_label ? `Loading ${current_collection_label}\u2026` : "Loading Smart Env\u2026",
      title: current_collection_label ? `Smart Environment is loading ${current_collection_label}.` : "Smart Environment is loading.",
      click_action: "noop",
      indicator_level: null,
      progress_value: collection_progress_value,
      progress_total: collection_progress_total,
      progress_pct: get_progress_pct2(collection_progress_value, collection_progress_total),
      view_title: "Loading Smart Environment",
      view_status: current_collection_label ? `${current_collection_label} \u2022 ${collection_status}` : collection_status,
      view_summary: current_collection_label ? `Loading collection: ${current_collection_label}.` : "Preparing collections, sources, and shared plugin state.",
      view_details: [
        collection_loading.total > 0 ? `${collection_loading.loaded} of ${collection_loading.total} collection${collection_loading.total === 1 ? "" : "s"} loaded.` : "",
        collection_loading.pending > 0 ? `${collection_loading.pending} collection${collection_loading.pending === 1 ? "" : "s"} remaining.` : "",
        reimport_queue_count > 0 ? `${reimport_queue_count} source${reimport_queue_count === 1 ? "" : "s"} queued for re-import after load.` : ""
      ].filter(Boolean),
      view_actions: []
    };
  }
  if (reimport_queue_count > 0) {
    return {
      kind: "reimport_queued",
      message: `Re-import (${reimport_queue_count})`,
      title: "Click to re-import queued sources.",
      click_action: "run_reimport",
      indicator_level: "attention",
      progress_value: null,
      progress_total: null,
      progress_pct: null,
      view_title: "Queued re-import work",
      view_status: `${reimport_queue_count} queued`,
      view_summary: "Run re-import to refresh changed sources and resume downstream embedding work.",
      view_details: [],
      view_actions: ["run_reimport"]
    };
  }
  if (env?.state === "loaded") {
    return {
      kind: "ready",
      message: default_message,
      title: "Smart Environment status",
      click_action: "context_menu",
      indicator_level: null,
      progress_value: null,
      progress_total: null,
      progress_pct: null,
      view_title: "Smart Environment ready",
      view_status: "Ready",
      view_summary: "No active import or embedding work is running right now.",
      view_details: [],
      view_actions: ["open_notifications"]
    };
  }
  return {
    kind: "not_loaded",
    message: default_message,
    title: "Smart Environment status",
    click_action: "context_menu",
    indicator_level: null,
    progress_value: null,
    progress_total: null,
    progress_pct: null,
    view_title: "Smart Environment not loaded",
    view_status: "Idle",
    view_summary: "Load Smart Environment to enable Smart Plugins.",
    view_details: [],
    view_actions: ["load_env"]
  };
}
function should_poll_env_activity2(env) {
  const activity_state = get_env_activity_state2(env);
  if (get_status_bar_notice_preview2(env?.event_logs)) return true;
  return [
    "embed_active",
    "embed_paused",
    "importing",
    "reimporting",
    "loading",
    "not_loaded"
  ].includes(activity_state.kind);
}
function get_status_bar_state2(env) {
  const notification_count = get_notification_event_count2(env?.event_logs);
  const notification_indicator_level = get_notification_indicator_level2(env?.event_logs);
  const activity_state = get_env_activity_state2(env);
  const embed_queue_count = get_reimport_queue_count2(env);
  const status_bar_notice_preview = get_status_bar_notice_preview2(env?.event_logs);
  const can_show_notice_preview = Boolean(status_bar_notice_preview) && !["embed_active", "embed_paused", "importing", "reimporting", "loading"].includes(activity_state.kind);
  let title = activity_state.title;
  let indicator_level = activity_state.indicator_level;
  if (!indicator_level && notification_count > 0) {
    indicator_level = notification_indicator_level;
  }
  if (activity_state.kind === "ready" && notification_count > 0) {
    title = `${notification_count} unseen notification${notification_count === 1 ? "" : "s"}`;
  }
  if (can_show_notice_preview && status_bar_notice_preview?.title) {
    title = status_bar_notice_preview.title;
  }
  return {
    message: can_show_notice_preview ? status_bar_notice_preview.message : activity_state.message,
    title,
    indicator_count: notification_count,
    indicator_level,
    embed_queue_count,
    click_action: activity_state.click_action
  };
}
function get_entry_timestamp3(entry) {
  if (typeof entry?.event?.at === "number") return entry.event.at;
  if (typeof entry?.at === "number") return entry.at;
  return Date.now();
}

// node_modules/smart-lookup-obsidian/node_modules/obsidian-smart-env/src/components/status_bar.css
var status_bar_default2 = ".status-bar-item:has(.smart-env-status-container) {\n  padding: 0 0.5em;\n\n  &:hover {\n    background-color: var(--background-modifier-hover);\n  }\n  & > .smart-env-status-container {\n    display: flex;\n    align-items: center;\n    gap: 0.5em;\n    text-decoration: none;\n    color: var(--status-bar-text-color);\n  }\n}\n\n.smart-env-status-indicator {\n  --smart-env-status-indicator-color: var(--status-bar-text-color);\n  --smart-env-status-indicator-glow-size: 0.28em;\n  --smart-env-status-indicator-glow-opacity: 0.38;\n\n  position: relative;\n  isolation: isolate;\n\n  width: 0.62em;\n  height: 0.62em;\n  min-width: 0.62em;\n  min-height: 0.62em;\n  border-radius: 999px;\n\n  color: var(--smart-env-status-indicator-color);\n  background-color: currentColor;\n  opacity: 1;\n  transform: scale(1);\n  box-shadow:\n    0 0 0 1px color-mix(in srgb, currentColor 24%, transparent),\n    inset 0 0 0 1px color-mix(in srgb, var(--background-primary) 78%, transparent);\n  transition:\n    background-color 150ms ease,\n    box-shadow 150ms ease,\n    transform 150ms ease,\n    color 150ms ease;\n  cursor: pointer;\n}\n\n.smart-env-status-indicator::before {\n  content: '';\n  position: absolute;\n  inset: calc(var(--smart-env-status-indicator-glow-size) * -1);\n  border-radius: inherit;\n  pointer-events: none;\n  z-index: -1;\n  opacity: var(--smart-env-status-indicator-glow-opacity);\n  transform: scale(1);\n  filter: blur(7px);\n  background:\n    radial-gradient(\n      circle at center,\n      color-mix(in srgb, currentColor 74%, transparent) 0%,\n      color-mix(in srgb, currentColor 46%, transparent) 34%,\n      color-mix(in srgb, currentColor 18%, transparent) 58%,\n      transparent 80%\n    );\n  transition:\n    opacity 150ms ease,\n    transform 150ms ease,\n    filter 150ms ease;\n}\n\n.smart-env-status-indicator::after {\n  content: '';\n  position: absolute;\n  inset: -1px;\n  border-radius: inherit;\n  pointer-events: none;\n  box-shadow: 0 0 0 1px color-mix(in srgb, currentColor 28%, transparent);\n  opacity: 0.82;\n}\n\n.smart-env-status-indicator:hover::before,\n.smart-env-status-indicator:focus-visible::before {\n  opacity: max(var(--smart-env-status-indicator-glow-opacity), 0.56);\n  transform: scale(1.08);\n}\n\n.smart-env-status-indicator:focus-visible {\n  outline: 2px solid var(--color-accent);\n  outline-offset: 2px;\n}\n\n.smart-env-status-indicator[data-level='default'] {\n  --smart-env-status-indicator-color: var(--status-bar-text-color);\n  --smart-env-status-indicator-glow-size: 0.24em;\n  --smart-env-status-indicator-glow-opacity: 0.32;\n}\n\n.smart-env-status-indicator[data-level='info'] {\n  --smart-env-status-indicator-color: var(--status-bar-text-color);\n  --smart-env-status-indicator-glow-size: 0.28em;\n  --smart-env-status-indicator-glow-opacity: 0.4;\n}\n\n.smart-env-status-indicator[data-level='milestone'] {\n  --smart-env-status-indicator-color: var(--color-accent);\n  --smart-env-status-indicator-glow-size: 0.52em;\n  --smart-env-status-indicator-glow-opacity: 0.86;\n}\n\n.smart-env-status-indicator[data-level='attention'] {\n  --smart-env-status-indicator-color: var(--color-yellow);\n  --smart-env-status-indicator-glow-size: 0.5em;\n  --smart-env-status-indicator-glow-opacity: 0.84;\n}\n\n.smart-env-status-indicator[data-level='warning'] {\n  --smart-env-status-indicator-color: var(--color-orange);\n  --smart-env-status-indicator-glow-size: 0.54em;\n  --smart-env-status-indicator-glow-opacity: 0.9;\n}\n\n.smart-env-status-indicator[data-level='error'] {\n  --smart-env-status-indicator-color: var(--color-red);\n  --smart-env-status-indicator-glow-size: 0.58em;\n  --smart-env-status-indicator-glow-opacity: 0.96;\n}\n\n.smart-env-status-indicator[data-count] {\n  transform: scale(1.08);\n  box-shadow:\n    0 0 0 1px color-mix(in srgb, currentColor 30%, transparent),\n    inset 0 0 0 1px color-mix(in srgb, var(--background-primary) 84%, transparent);\n}\n\n.smart-env-status-indicator[data-count]::before {\n  opacity: 1;\n  transform: scale(1.12);\n  filter: blur(7px);\n  animation: smart-env-status-indicator-glow 2200ms ease-in-out infinite;\n}\n\n.status-bar-mobile {\n  position: var(--status-bar-position);\n  bottom: 0;\n  border-radius: 0 8px 0 0;\n  border-style: solid;\n  border-width: 1px;\n  border-color: var(--status-bar-border-color);\n  background-color: var(--status-bar-background);\n  color: var(--status-bar-text-color);\n  font-size: var(--status-bar-font-size);\n  min-height: 18px;\n  padding: var(--size-4-1);\n  user-select: none;\n  z-index: var(--layer-status-bar);\n  font-variant-numeric: tabular-nums;\n  & > .smart-env-status-container {\n    padding: 5px 5px 5px 0;\n  }\n}\n\n/* footer view on mobile */\n.embedded-backlinks > .status-bar-mobile {\n  position: relative;\n  border-style: none;\n}\n\n@keyframes smart-env-status-indicator-glow {\n  0%,\n  100% {\n    transform: scale(0.88);\n    opacity: 0.92;\n  }\n\n  50% {\n    transform: scale(1.23);\n    opacity: 1;\n  }\n}\n";

// node_modules/smart-lookup-obsidian/node_modules/obsidian-smart-env/src/components/status_bar.js
function build_html39() {
  return `
    <a
      class="smart-env-status-container"
      role="button"
      title="Smart Environment status"
      tabindex="0"
    >
      <span class="smart-env-status-icon" aria-hidden="true"></span>
      <span class="smart-env-status-msg" aria-live="polite"></span>
      <span
        class="smart-env-status-indicator"
        title="Open events feed"
        role="button"
        tabindex="0"
      ></span>
    </a>
  `;
}
async function render41(env, opts = {}) {
  this.apply_style_sheet(status_bar_default2);
  const frag = this.create_doc_fragment(build_html39());
  const anchor = frag.firstElementChild;
  post_process37.call(this, env, anchor, opts);
  return anchor;
}
function post_process37(env, container, opts = {}) {
  const icon_slot = container?.querySelector?.(".smart-env-status-icon");
  const status_indicator = container?.querySelector?.(".smart-env-status-indicator");
  const status_msg = container?.querySelector?.(".smart-env-status-msg");
  const pause_embedding = () => {
    return env?.smart_sources?.entities_vector_adapter?.halt_embed_queue_processing?.();
  };
  const resume_embedding = () => {
    return env?.smart_sources?.entities_vector_adapter?.resume_embed_queue_processing?.();
  };
  const set_status_message = (message) => {
    if (!status_msg) return;
    if (typeof status_msg.setText === "function") {
      status_msg.setText(message);
      return;
    }
    status_msg.textContent = message;
  };
  const open_notifications_feed3 = (event) => {
    event.preventDefault?.();
    event.stopPropagation?.();
    env.open_notifications_feed_modal?.();
  };
  const update_indicator = (status_state) => {
    if (!status_indicator) return;
    const {
      indicator_count,
      indicator_level
    } = status_state;
    status_indicator.dataset.level = indicator_level || "default";
    if (indicator_count > 0) {
      status_indicator.dataset.count = String(indicator_count);
    } else {
      status_indicator.removeAttribute("data-count");
    }
    const indicator_title = indicator_count > 0 ? `${indicator_count} unseen notification${indicator_count === 1 ? "" : "s"}` : "Open notifications feed";
    status_indicator.setAttribute("title", indicator_title);
  };
  const action_handlers = {
    pause_embed(event) {
      event.preventDefault?.();
      event.stopPropagation?.();
      pause_embedding();
    },
    resume_embed(event) {
      event.preventDefault?.();
      event.stopPropagation?.();
      resume_embedding();
    },
    run_reimport(event) {
      event.preventDefault?.();
      event.stopPropagation?.();
      set_status_message("Re-importing\u2026");
      env.run_re_import?.();
    },
    noop() {
    },
    context_menu(event) {
      const context_event = new MouseEvent("contextmenu", {
        bubbles: true,
        cancelable: true,
        clientX: event?.clientX || 0,
        clientY: event?.clientY || 0
      });
      container.dispatchEvent?.(context_event);
    }
  };
  const render_status_elm = () => {
    const status_state = get_status_bar_state2(env);
    const {
      message,
      title
    } = status_state;
    if (icon_slot) {
      (0, import_obsidian69.setIcon)(icon_slot, "smart-connections");
    }
    update_indicator(status_state);
    set_status_message(message);
    container.setAttribute?.("title", title);
    container.removeAttribute?.("href");
    container.removeAttribute?.("target");
  };
  const run_container_action = (event) => {
    const status_state = get_status_bar_state2(env);
    const action_key = Object.prototype.hasOwnProperty.call(action_handlers, status_state.click_action) ? status_state.click_action : "context_menu";
    action_handlers[action_key](event);
  };
  register_status_bar_context_menu2(env, container);
  let progress_poll_interval = null;
  const set_polling = (active) => {
    if (active) {
      if (progress_poll_interval) return;
      progress_poll_interval = setInterval(() => {
        refresh_status_bar();
      }, 1e3);
      return;
    }
    if (!progress_poll_interval) return;
    clearInterval(progress_poll_interval);
    progress_poll_interval = null;
  };
  const refresh_status_bar = () => {
    render_status_elm();
    set_polling(should_poll_env_activity2(env));
  };
  refresh_status_bar();
  bind_once2(status_indicator, "_click_handler", "click", open_notifications_feed3);
  bind_once2(status_indicator, "_keydown_handler", "keydown", (event) => {
    if (event.key !== "Enter" && event.key !== " ") return;
    open_notifications_feed3(event);
  });
  bind_once2(container, "_click_handler", "click", (event) => {
    run_container_action(event);
  });
  bind_once2(container, "_keydown_handler", "keydown", (event) => {
    if (event.key !== "Enter" && event.key !== " ") return;
    event.preventDefault();
    run_container_action(event);
  });
  let debounce_timeout = null;
  const debounce_refresh_status_bar = () => {
    if (debounce_timeout) clearTimeout(debounce_timeout);
    debounce_timeout = setTimeout(() => {
      refresh_status_bar();
      debounce_timeout = null;
    }, 100);
  };
  const disposers = [];
  disposers.push(env.events.on("*", debounce_refresh_status_bar));
  disposers.push(() => {
    set_polling(false);
    if (debounce_timeout) clearTimeout(debounce_timeout);
  });
  this.attach_disposer(container, disposers);
}
function bind_once2(element, handler_key, event_name, handler) {
  if (!element || typeof handler !== "function") return;
  if (element[handler_key]) return;
  element[handler_key] = handler;
  element.addEventListener(event_name, handler);
}

// node_modules/smart-lookup-obsidian/node_modules/obsidian-smart-env/views/smart_item_view.js
var SmartItemView2 = class extends import_obsidian70.ItemView {
  /**
   * Creates an instance of SmartItemView.
   * @param {any} leaf
   * @param {any} plugin
   */
  constructor(leaf, plugin) {
    super(leaf);
    this.app = plugin.app;
    this.plugin = plugin;
  }
  /**
   * The unique view type. Must be implemented in subclasses.
   * @returns {string}
   */
  static get view_type() {
    throw new Error("view_type must be implemented in subclass");
  }
  /**
   * The display text for this view. Must be implemented in subclasses.
   * @returns {string}
   */
  static get display_text() {
    throw new Error("display_text must be implemented in subclass");
  }
  /**
   * The icon name for this view.
   * @returns {string}
   */
  static get icon_name() {
    return "smart-connections";
  }
  /**
   * Whether the view should wait for the environment to be loaded before rendering.
   * Override in subclasses that must stay visible during environment load.
   * @returns {boolean}
   */
  static get wait_for_env() {
    return true;
  }
  /**
   * Registers this ItemView subclass against a plugin instance and
   * installs ergonomic accessors, an open helper, and an `${view_type}:open` listener.
   *
   * Usage from a plugin class:
   *   SubClass.register_item_view(this);
   *
   * This will:
   * - call plugin.registerView(view_type, ...)
   * - add a command "Open: <display_text> view"
   * - define a getter on plugin: plugin[method_name] -> the view instance
   * - define a method on plugin: plugin["open_" + method_name]() -> opens the view
   * - listen for env events named `${view_type}:open` and open the view when emitted
   *
   * @param {import('obsidian').Plugin} plugin
   * @returns {{method_name:string, open_method_name:string, event_name:string}}
   */
  static register_item_view(plugin) {
    const View = (
      /** @type {typeof SmartItemView} */
      this
    );
    if (plugin.app.viewRegistry?.viewByType?.[View.view_type]) {
      plugin.app.viewRegistry.unregisterView(View.view_type);
      console.warn(`View type "${View.view_type}" was already registered. Overwriting with new registration.`);
    }
    plugin.registerView(View.view_type, (leaf) => new View(leaf, plugin));
    plugin.addCommand({
      id: View.view_type,
      name: "Open: " + View.display_text + " view",
      callback: () => {
        View.open(plugin.app.workspace);
      }
    });
    const method_name = View.view_type.replace(/^smart-/, "").replace(/-/g, "_");
    const open_method_name = "open_" + method_name;
    if (!Object.getOwnPropertyDescriptor(plugin, method_name)) {
      Object.defineProperty(plugin, method_name, {
        configurable: true,
        enumerable: false,
        get: () => View.get_view(plugin.app.workspace)
      });
    }
    plugin[open_method_name] = (params = {}) => {
      if (!plugin.app.viewRegistry?.viewByType?.[View.view_type]) {
        console.warn(`View type "${View.view_type}" not registered when calling ${open_method_name}. Registering now. This should NOT happen frequently.`);
        plugin.registerView(View.view_type, (leaf) => new View(leaf, plugin));
      }
      View.open(plugin.app.workspace, params);
    };
    const event_name = `${method_name}:open`;
    const handler = (payload = {}) => {
      const active = typeof payload?.active === "boolean" ? payload.active : true;
      View.open(plugin.app.workspace, { ...payload, active });
    };
    const unsubscribe = plugin?.env?.events.on(event_name, handler);
    if (typeof plugin.register === "function" && typeof unsubscribe === "function") {
      plugin.register(() => unsubscribe());
    }
    return { method_name, open_method_name, event_name };
  }
  /**
   * Retrieves the Leaf instance for this view type if it exists.
   * @param {import("obsidian").Workspace} workspace
   * @returns {import("obsidian").WorkspaceLeaf | undefined}
   */
  static get_leaf(workspace) {
    return workspace.getLeavesOfType(this.view_type)[0];
  }
  /**
   * Retrieves the view instance if it exists.
   * @param {import("obsidian").Workspace} workspace
   * @returns {SmartItemView | undefined}
   */
  static get_view(workspace) {
    const leaf = this.get_leaf(workspace);
    return leaf ? leaf.view : void 0;
  }
  /**
   * Opens the view. If `this.default_open_location` is "root",
   * it opens (or reveals) in a root leaf; otherwise in a sidebar leaf.
   *
   * @param {import("obsidian").Workspace} workspace
   * @param {boolean|object} [params={}]
   */
  static open(workspace, params = {}) {
    if (typeof params === "boolean") {
      params = { active: params };
    }
    const {
      active = true,
      state = null
    } = params;
    const existing_leaf = this.get_leaf(workspace);
    const open_location = this.default_open_location;
    let leaf;
    if (open_location === "root") {
      leaf = existing_leaf || workspace.getLeaf(false);
    } else if (open_location === "left") {
      leaf = existing_leaf || workspace.getLeftLeaf(false);
      if (workspace.leftSplit?.collapsed) {
        workspace.leftSplit.toggle();
      }
    } else {
      leaf = existing_leaf || workspace.getRightLeaf(false);
      if (workspace.rightSplit?.collapsed) {
        workspace.rightSplit.toggle();
      }
    }
    const view_state = { type: this.view_type, active };
    if (state && typeof state === "object") {
      view_state.state = state;
    }
    Promise.resolve(leaf?.setViewState?.(view_state)).then(() => {
      if (active) {
        try {
          workspace.revealLeaf?.(leaf);
        } catch (error) {
          console.warn(`Failed to reveal item view "${this.view_type}"`, error);
        }
      }
      setTimeout(() => {
        this.get_view(workspace)?.render_view(params);
      }, 100);
    }).catch((error) => {
      console.error(`Failed to open item view "${this.view_type}"`, error);
    });
  }
  static is_open(workspace) {
    return this.get_leaf(workspace)?.view instanceof this;
  }
  // instance
  getViewType() {
    return this.constructor.view_type;
  }
  getDisplayText() {
    return this.constructor.display_text;
  }
  getIcon() {
    return this.constructor.icon_name;
  }
  async onOpen() {
    this.app.workspace.onLayoutReady(this.initialize.bind(this));
  }
  async initialize() {
    await this.render_mobile_status_bar();
    const should_wait_for_env = this.constructor.wait_for_env !== false;
    const wait_for_states = ["loaded"];
    if (should_wait_for_env) {
      await wait_for_env_to_load2(this, { wait_for_states });
    }
    this.container?.empty?.();
    this.register_plugin_events();
    this.app.workspace.registerHoverLinkSource(this.constructor.view_type, { display: this.getDisplayText(), defaultMod: true });
    this.render_view();
  }
  async render_mobile_status_bar() {
    if (!import_obsidian70.Platform.isMobile) return;
    if (!this.env?.smart_view) return;
    const status_bar_container = this.containerEl.querySelector(".status-bar-mobile") ?? this.containerEl.createDiv({ cls: "status-bar-mobile" });
    status_bar_container.empty?.();
    const status_bar_item = status_bar_container.createDiv({ cls: "status-bar-item" });
    try {
      const status_bar = await render41.call(this.env.smart_view, this.env);
      if (status_bar) status_bar_item.appendChild(status_bar);
    } catch (error) {
      console.error("Failed to render mobile Smart Env status bar", error);
    }
  }
  register_plugin_events() {
  }
  render_view(params = {}) {
    throw new Error("render_view must be implemented in subclass");
  }
  get container() {
    return this.containerEl.children[1];
  }
  get env() {
    return this.plugin.env;
  }
  async open_settings() {
    await this.app.setting.open();
    await this.app.setting.openTabById(this.plugin.manifest.id);
  }
};

// node_modules/smart-lookup-obsidian/src/views/lookup_item_view.js
var LookupItemView = class extends SmartItemView2 {
  static get view_type() {
    return "smart-lookup-view";
  }
  static get display_text() {
    return "Lookup";
  }
  static get icon_name() {
    return "smart-lookup";
  }
  async render_view(lookup_params, container = this.container) {
    const frag = await this.env.smart_components.render_component("lookup_item_view", this, lookup_params);
    container.empty();
    container.appendChild(frag);
  }
};

// src/views/lookup_item_view.js
var ConnectionsLookupItemView = class extends LookupItemView {
  static get view_type() {
    return "smart-lookup-view-connections";
  }
};

// src/views/connections_footer_deco.js
var import_view = require("@codemirror/view");
var import_state = require("@codemirror/state");
var set_connections_footer_dom_effect = import_state.StateEffect.define();
var connections_footer_plugin = import_view.ViewPlugin.fromClass(
  class {
    /* ------------------------------------------------------ lifecycle ---- */
    constructor(view) {
      this.view = view;
      this.connections_footer_frag = null;
      this.container_el = null;
    }
    destroy() {
      if (this.container_el?.isConnected) {
        this.container_el.remove();
      }
    }
    /* ----------------------------------------------------- view updates -- */
    update(update) {
      for (const tr of update.transactions) {
        for (const ef of tr.effects) {
          if (ef.is(set_connections_footer_dom_effect)) {
            if (ef.value === null) {
              if (this.container_el) this.container_el.style.display = "none";
            } else {
              this.render_footer(ef.value);
            }
          }
        }
      }
    }
    render_footer(container = null) {
      if (container) {
        if (this.container_el && this.container_el !== container && this.container_el.isConnected) {
          this.container_el.remove();
        }
        this.connections_footer_frag = container;
        this.container_el = container;
        this.#ensure_dom_inserted();
      }
      if (!this.container_el) return;
      this.container_el.style.display = this.connections_footer_frag ? "block" : "none";
    }
    #ensure_dom_inserted() {
      if (!this.container_el || this.container_el.isConnected) return;
      const cm_container = this.view.dom.querySelector(".cm-contentContainer");
      cm_container?.parentNode?.insertBefore(
        this.container_el,
        cm_container.nextSibling
      );
    }
  }
);

// src/views/connections_footer_view.js
var import_obsidian71 = require("obsidian");
var get_active_entity = (env, workspace) => {
  const active_file = workspace.getActiveFile();
  if (!active_file) return null;
  return env.smart_sources?.get(active_file.path) || null;
};
var is_last_line_visible = (editor_view) => {
  try {
    if (!editor_view?.state?.doc) return false;
    const doc = editor_view.state.doc;
    const last_line = doc.line(doc.lines);
    const start = last_line.from;
    const end = last_line.to;
    return (Array.isArray(editor_view.visibleRanges) ? editor_view.visibleRanges : []).some((range) => range.from <= end && range.to >= start);
  } catch (err) {
    console.warn("[Smart Connections] last-line visibility check failed", err);
    return false;
  }
};
var schedule_next_frame = (callback) => {
  if (typeof requestAnimationFrame === "function") {
    requestAnimationFrame(callback);
    return;
  }
  setTimeout(callback, 0);
};
var ConnectionsFooterView = class {
  constructor(plugin) {
    this.plugin = plugin;
    this.app = plugin.app;
    this.register_env_listeners();
    this.container_map = {};
    this._detach_visibility_guard = null;
  }
  get env() {
    return this.plugin.env;
  }
  /**
   * Attach a lightweight scroll/resize guard that waits until the last line becomes visible.
   * When the condition is met, it detaches itself and triggers render_view() again.
   * @param {import('@codemirror/view').EditorView} editor_view
   */
  attach_visibility_guard(editor_view) {
    this.detach_visibility_guard();
    if (!editor_view) return;
    const scroll_target = editor_view.scrollDOM || editor_view.dom || null;
    if (!scroll_target) return;
    let ticking = false;
    const check_and_render = () => {
      if (ticking) return;
      ticking = true;
      schedule_next_frame(() => {
        ticking = false;
        if (is_last_line_visible(editor_view)) {
          this.detach_visibility_guard();
          this.render_view();
        }
      });
    };
    const on_scroll = () => check_and_render();
    const on_resize = () => check_and_render();
    scroll_target.addEventListener("scroll", on_scroll, { passive: true });
    window.addEventListener("resize", on_resize);
    this._detach_visibility_guard = () => {
      try {
        scroll_target.removeEventListener("scroll", on_scroll);
      } catch {
      }
      try {
        window.removeEventListener("resize", on_resize);
      } catch {
      }
      this._detach_visibility_guard = null;
    };
  }
  detach_visibility_guard() {
    if (typeof this._detach_visibility_guard === "function") {
      this._detach_visibility_guard();
    }
  }
  async render_view() {
    if (!this.env.connections_lists?.settings?.footer_connections) return this.remove();
    const editor_view = this.plugin.get_editor_view();
    if (!editor_view) return;
    if (!is_last_line_visible(editor_view)) {
      try {
        editor_view.dispatch({ effects: [set_connections_footer_dom_effect.of(null)] });
      } catch (err) {
        console.warn("[Smart Connections] footer hide dispatch failed", err);
      }
      this.attach_visibility_guard(editor_view);
      return;
    }
    this.detach_visibility_guard();
    const entity = get_active_entity(this.env, this.app.workspace);
    if (!entity) {
      editor_view.dispatch({ effects: [set_connections_footer_dom_effect.of(null)] });
      return;
    }
    if (this.container_map[entity.key]?.isConnected) {
      editor_view.dispatch({ effects: [set_connections_footer_dom_effect.of(this.container_map[entity.key])] });
      return;
    }
    const footer_container = await render30.call(
      this.env.smart_view,
      this,
      {
        connections_item: entity
      }
    );
    if (this.container_map[entity.key] && this.container_map[entity.key] instanceof HTMLElement) {
      this.container_map[entity.key].remove();
    }
    this.container_map[entity.key] = footer_container;
    editor_view.dispatch({ effects: [set_connections_footer_dom_effect.of(footer_container)] });
    if (import_obsidian71.Platform.isMobile) {
      const container = this.container_map[entity.key];
      const status_bar_container = container.querySelector(".status-bar-mobile") ?? container.createDiv({ cls: "status-bar-mobile" });
      status_bar_container.empty();
      const status_bar_item = status_bar_container.createDiv({ cls: "status-bar-item" });
      this.env.smart_components.render_component("status_bar", this.env).then((el) => status_bar_item.appendChild(el));
    }
  }
  remove() {
    const editor_view = this.plugin.get_editor_view();
    this.detach_visibility_guard();
    if (editor_view) {
      try {
        editor_view.dispatch({ effects: [set_connections_footer_dom_effect.of(null)] });
      } catch (err) {
        console.warn("[Smart Connections] footer remove dispatch failed", err);
      }
    }
  }
  register_env_listeners() {
    let handle_current_source_debounce;
    this.register_env_listener("sources:opened", () => {
      if (handle_current_source_debounce) clearTimeout(handle_current_source_debounce);
      handle_current_source_debounce = setTimeout(() => {
        this.render_view();
      }, 250);
    });
    this.register_env_listener("settings:changed", (event) => {
      if (event.path?.includes("connections_lists")) {
        this.render_view();
      }
    });
  }
  register_env_listener(event_key, callback) {
    if (!this.env_listeners) this.env_listeners = [];
    this.env_listeners.push(this.env.events.on(event_key, callback));
  }
  async open_settings() {
    await this.app.setting.open();
    await this.app.setting.openTabById(this.plugin.manifest.id);
  }
  unload() {
    this.detach_visibility_guard();
    if (this.env_listeners) {
      this.env_listeners.forEach((off) => off());
      this.env_listeners = [];
    }
  }
};

// src/views/connections_codeblock.js
async function register_smart_connections_codeblock(plugin) {
  plugin.registerMarkdownCodeBlockProcessor(
    "smart-connections",
    async (cb_content, container, mpp_ctx) => {
      container.empty();
      container.createEl("span", { text: "Loading\u2026" });
      const cb_config = JSON.parse(cb_content.trim() || "{}");
      const env = plugin.env;
      const entity = env.smart_sources.get(mpp_ctx.sourcePath) ?? env.smart_sources.init_file_path(mpp_ctx.sourcePath);
      const smart_view = env.smart_view;
      if (!entity) {
        container.empty();
        container.createEl("p", { text: "Entity not found: " + mpp_ctx.sourcePath });
        return;
      }
      const render_codeblock = async () => {
        const connections_list = entity.connections;
        if (!connections_list?.env) {
          container.empty();
          container.createEl("p", { text: "Smart Environment / Connections loading\u2026" });
          const retry_button = container.createEl("button", { text: "Retry" });
          retry_button.addEventListener("click", () => {
            render_codeblock();
          });
          return;
        }
        const connections_container = await plugin.env.smart_components.render_component(
          "connections_codeblock",
          connections_list,
          {
            ...cb_config
            // FUTURE: handling codeblock config options
          }
        );
        container.empty();
        container.appendChild(connections_container);
      };
      if (!container._has_listeners) {
        container._has_listeners = true;
        const disposers = [];
        disposers.push(env.events.on("settings:changed", (event) => {
          console.log("connections codeblock view detected settings change", event);
          if (event.path?.includes("connections_lists")) {
            render_codeblock();
          }
        }));
        smart_view.attach_disposer(container, disposers);
      }
      render_codeblock();
    }
  );
}

// src/utils/build_connections_codeblock.js
function build_connections_codeblock(settings = null) {
  const json = settings ? JSON.stringify(settings, null, 2) : "";
  return `\`\`\`smart-connections
${json}
\`\`\`
`;
}

// src/main.js
var {
  Plugin: Plugin2,
  requestUrl: requestUrl7,
  Platform: Platform16
} = import_obsidian72.default;
var SmartConnectionsPlugin = class extends SmartPlugin {
  SmartEnv = SmartEnv2;
  ReleaseNotesView = ReleaseNotesView2;
  get smart_env_config() {
    if (!this._smart_env_config) {
      this._smart_env_config = { ...smart_env_config3 };
    }
    return this._smart_env_config;
  }
  ConnectionsSettingsTab = ScEarlySettingsTab;
  get item_views() {
    return {
      ConnectionsItemView,
      ReleaseNotesView: this.ReleaseNotesView,
      // TEMP during transition to Lookup as standalone plugin (conditionally include ConnectionsLookupItemView if Smart Lookup is not enabled to avoid conflicts)
      ...!this.app.plugins.enabledPlugins.has("smart-lookup") ? { ConnectionsLookupItemView } : {}
    };
  }
  get obsidian() {
    return import_obsidian72.default;
  }
  get api() {
    return this._api;
  }
  onload() {
    this.app.workspace.onLayoutReady(this.initialize.bind(this));
    this.SmartEnv.create(this, this.smart_env_config);
    this.addSettingTab(new this.ConnectionsSettingsTab(this.app, this));
    add_smart_dice_icon();
    this.register_commands();
    this.register_item_views();
    this.register_ribbon_icons();
  }
  onunload() {
    console.log("Unloading Smart Connections plugin");
    this.connections_footer_view?.unload();
    this.notices?.unload();
    this.env?.unload_main?.(this);
  }
  async initialize() {
    this.smart_connections_view = null;
    this.is_new_user().then(async (is_new) => {
      if (!is_new) return;
      setTimeout(() => {
        StoryModal.open(this, {
          title: "Getting Started With Smart Connections",
          url: "https://smartconnections.app/story/smart-connections-getting-started/?utm_source=sc-op-new-user"
        });
      }, 1e3);
      await this.SmartEnv.wait_for({ loaded: true });
      setTimeout(() => {
        this.apply_connections_view_location();
        this.open_connections_view();
      }, 1e3);
      this.add_to_gitignore("\n\n# Ignore Smart Environment folder\n.smart-env");
    });
    await this.SmartEnv.wait_for({ loaded: true });
    this.wrap_connections_view_open();
    this.apply_connections_view_location();
    this.register_connections_view_location_listener();
    register_smart_connections_codeblock(this);
    if (!this.connections_footer_view) {
      this.registerEditorExtension(connections_footer_plugin);
      this.connections_footer_view = new ConnectionsFooterView(this);
    }
    this.toggled_footer_connections();
    await this.check_for_updates();
  }
  get ribbon_icons() {
    return {
      connections: {
        icon_name: "smart-connections",
        description: "Smart Connections: Open connections view",
        callback: () => {
          this.open_connections_view();
        }
      },
      footer_connections: {
        description: "Toggle Footer Connections",
        icon_name: "smart-footer-connections",
        callback: () => {
          const settings = this.env.connections_lists.settings;
          settings.footer_connections = !settings.footer_connections;
        }
      },
      random_note: {
        icon_name: "smart-dice",
        description: "Smart Connections: Open random connection",
        callback: () => {
          this.open_random_connection();
        }
      },
      // TEMP during transition to Lookup as standalone plugin (conditionally include ribbon icon if Smart Lookup is not enabled to avoid conflicts)
      ...app.plugins.enabledPlugins.has("smart-lookup") ? {} : {
        lookup: {
          icon_name: "smart-lookup",
          description: "Smart Lookup: Open lookup view",
          callback: () => {
            this.open_lookup_view_connections();
          }
        }
      }
    };
  }
  get settings() {
    return this.env?.settings || {};
  }
  /**
   * Sync connections view location with settings.
   * @returns {void}
   */
  apply_connections_view_location() {
    const connections_view_location = this.env?.connections_lists?.settings?.connections_view_location ?? "right";
    ConnectionsItemView.default_open_location = connections_view_location === "left" ? "left" : "right";
    this.ensure_connections_view_leaf_location();
  }
  wrap_connections_view_open() {
    if (this._open_connections_view_base || typeof this.open_connections_view !== "function") {
      return;
    }
    this._open_connections_view_base = this.open_connections_view.bind(this);
    this.open_connections_view = (...args) => {
      this.ensure_connections_view_leaf_location();
      return this._open_connections_view_base(...args);
    };
  }
  ensure_connections_view_leaf_location() {
    const workspace = this.app?.workspace;
    if (!workspace) {
      return;
    }
    const desired_location = ConnectionsItemView.default_open_location;
    const connections_leaf = ConnectionsItemView.get_leaf(workspace);
    if (!should_relocate_leaf({ workspace, leaf: connections_leaf, desired_location })) {
      return;
    }
    connections_leaf.detach();
  }
  register_connections_view_location_listener() {
    if (this.connections_view_location_listener || !this.env?.events) return;
    this.connections_view_location_listener = this.env.events.on("settings:changed", (event) => {
      if (!event?.path?.includes?.("connections_view_location")) return;
      this.apply_connections_view_location();
    });
  }
  async check_for_updates() {
    if (await this.is_new_plugin_version(this.manifest.version)) {
      console.log("opening release notes modal");
      try {
        this.ReleaseNotesView.open(this.app.workspace, this.manifest.version);
      } catch (error) {
        console.error("Failed to open ReleaseNotesView", error);
      }
      await this.set_last_known_version(this.manifest.version);
    }
    setTimeout(this.check_for_update.bind(this), 3e3);
    setInterval(this.check_for_update.bind(this), 108e5);
  }
  async check_for_update() {
    try {
      const { json: response } = await requestUrl7({
        url: "https://api.github.com/repos/brianpetro/obsidian-smart-connections/releases/latest",
        method: "GET",
        headers: {
          "Content-Type": "application/json"
        },
        contentType: "application/json"
      });
      const latest_release = response.tag_name;
      if (latest_release !== this.manifest.version) {
        if (!this.update_available || this.latest_release_version !== latest_release) {
          this.env?.events?.emit("plugin:new_version_available", {
            level: "attention",
            message: `Smart Connections ${latest_release} is available.`,
            version: latest_release,
            event_source: "check_for_update"
          });
        }
        this.latest_release_version = latest_release;
        this.update_available = true;
      }
    } catch (error) {
      console.error(error);
    }
  }
  async restart_plugin() {
    this.env?.unload_main?.(this);
    await new Promise((resolve) => setTimeout(resolve, 3e3));
    window.restart_plugin = async (id) => {
      await window.app.plugins.disablePlugin(id);
      await window.app.plugins.enablePlugin(id);
    };
    await window.restart_plugin(this.manifest.id);
  }
  get commands() {
    return {
      ...super.commands,
      random_connection: {
        id: "smart-connections-random",
        name: "Open: Random note from connections",
        callback: async () => {
          await this.open_random_connection();
        }
      },
      getting_started: {
        id: "smart-connections-getting-started",
        name: "Show: Getting started slideshow",
        callback: () => {
          StoryModal.open(this, {
            title: "Getting Started With Smart Connections",
            url: "https://smartconnections.app/story/smart-connections-getting-started/?utm_source=sc-op-command"
          });
        }
      },
      insert_connections_codeblock: {
        id: "insert-connections-codeblock",
        name: "Insert: Connections codeblock",
        editorCallback: (editor) => {
          editor.replaceSelection(build_connections_codeblock());
        }
      },
      toggle_footer_connections: {
        id: "toggle-footer-connections",
        name: "Toggle: Footer connections",
        callback: () => {
          const settings = this.env.connections_lists.settings;
          settings.footer_connections = !settings.footer_connections;
        }
      }
    };
  }
  async open_random_connection() {
    const curr_file = this.app.workspace.getActiveFile();
    if (!curr_file) {
      this.env?.events?.emit("connections:open_random_unavailable", {
        level: "warning",
        message: "No active file to find connections for.",
        event_source: "open_random_connection"
      });
      return;
    }
    const rand_entity = await get_random_connection(this.env, curr_file.path);
    if (!rand_entity) {
      this.env?.events?.emit("connections:open_random_unavailable", {
        level: "warning",
        message: `Cannot open random connection for non-embedded source: ${curr_file.path}`,
        event_source: "open_random_connection"
      });
      return;
    }
    this.open_note(rand_entity.item.path);
    this.env?.events?.emit?.("connections:open_random");
  }
  /**
   * Attempts to retrieve the CodeMirror 6 EditorView for the active markdown file.
   * @returns {EditorView|null}
   */
  get_editor_view() {
    const file = this.app.workspace.getActiveFile();
    if (!file) {
      console.log("Smart Connections: No active file found");
      return null;
    }
    const markdown_view = this.app.workspace.getActiveFileView();
    if (!markdown_view) {
      console.log("Smart Connections: No active file view found");
      return null;
    }
    return markdown_view.editor?.cm || null;
  }
  toggled_footer_connections() {
    const view = this.get_editor_view();
    if (view && this.env.connections_lists.settings.footer_connections) {
      this.connections_footer_view?.render_view();
    } else {
      this.connections_footer_view?.remove();
    }
  }
  async open_note(target_path, event = null) {
    await open_note(this, target_path, event);
  }
  /**
   * @deprecated extract into utility
   */
  async add_to_gitignore(ignore, message = null) {
    if (!await this.app.vault.adapter.exists(".gitignore")) return;
    let gitignore_file = await this.app.vault.adapter.read(".gitignore");
    if (gitignore_file.indexOf(ignore) < 0) {
      await this.app.vault.adapter.append(".gitignore", `

${message ? "# " + message + "\n" : ""}${ignore}`);
      console.log("Added to .gitignore: " + ignore);
    }
  }
};

/* nosourcemap */