/* THIS IS A GENERATED/BUNDLED FILE BY ESBUILD if you want to view the source, please visit the github repository of this plugin */ 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 __typeError = (msg) => { throw TypeError(msg); }; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; 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); var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg); var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method); var __privateWrapper = (obj, member, setter, getter) => ({ set _(value) { __privateSet(obj, member, value, setter); }, get _() { return __privateGet(obj, member, getter); } }); // node_modules/eventemitter3/index.js var require_eventemitter3 = __commonJS({ "node_modules/eventemitter3/index.js"(exports, module2) { "use strict"; var has = Object.prototype.hasOwnProperty; var prefix = "~"; function Events() { } if (Object.create) { Events.prototype = /* @__PURE__ */ Object.create(null); if (!new Events().__proto__) prefix = false; } function EE(fn, context, once) { this.fn = fn; this.context = context; this.once = once || false; } function addListener(emitter, event, fn, context, once) { if (typeof fn !== "function") { throw new TypeError("The listener must be a function"); } var listener = new EE(fn, context || emitter, once), evt = prefix ? prefix + event : event; if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++; else if (!emitter._events[evt].fn) emitter._events[evt].push(listener); else emitter._events[evt] = [emitter._events[evt], listener]; return emitter; } function clearEvent(emitter, evt) { if (--emitter._eventsCount === 0) emitter._events = new Events(); else delete emitter._events[evt]; } function EventEmitter2() { this._events = new Events(); this._eventsCount = 0; } EventEmitter2.prototype.eventNames = function eventNames() { var names = [], events, name; if (this._eventsCount === 0) return names; for (name in events = this._events) { if (has.call(events, name)) names.push(prefix ? name.slice(1) : name); } if (Object.getOwnPropertySymbols) { return names.concat(Object.getOwnPropertySymbols(events)); } return names; }; EventEmitter2.prototype.listeners = function listeners(event) { var evt = prefix ? prefix + event : event, handlers = this._events[evt]; if (!handlers) return []; if (handlers.fn) return [handlers.fn]; for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) { ee[i] = handlers[i].fn; } return ee; }; EventEmitter2.prototype.listenerCount = function listenerCount(event) { var evt = prefix ? prefix + event : event, listeners = this._events[evt]; if (!listeners) return 0; if (listeners.fn) return 1; return listeners.length; }; EventEmitter2.prototype.emit = function emit(event, a1, a2, a3, a4, a5) { var evt = prefix ? prefix + event : event; if (!this._events[evt]) return false; var listeners = this._events[evt], len = arguments.length, args, i; if (listeners.fn) { if (listeners.once) this.removeListener(event, listeners.fn, void 0, true); switch (len) { case 1: return listeners.fn.call(listeners.context), true; case 2: return listeners.fn.call(listeners.context, a1), true; case 3: return listeners.fn.call(listeners.context, a1, a2), true; case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true; case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true; case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true; } for (i = 1, args = new Array(len - 1); i < len; i++) { args[i - 1] = arguments[i]; } listeners.fn.apply(listeners.context, args); } else { var length = listeners.length, j; for (i = 0; i < length; i++) { if (listeners[i].once) this.removeListener(event, listeners[i].fn, void 0, true); switch (len) { case 1: listeners[i].fn.call(listeners[i].context); break; case 2: listeners[i].fn.call(listeners[i].context, a1); break; case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break; case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break; default: if (!args) for (j = 1, args = new Array(len - 1); j < len; j++) { args[j - 1] = arguments[j]; } listeners[i].fn.apply(listeners[i].context, args); } } } return true; }; EventEmitter2.prototype.on = function on(event, fn, context) { return addListener(this, event, fn, context, false); }; EventEmitter2.prototype.once = function once(event, fn, context) { return addListener(this, event, fn, context, true); }; EventEmitter2.prototype.removeListener = function removeListener(event, fn, context, once) { var evt = prefix ? prefix + event : event; if (!this._events[evt]) return this; if (!fn) { clearEvent(this, evt); return this; } var listeners = this._events[evt]; if (listeners.fn) { if (listeners.fn === fn && (!once || listeners.once) && (!context || listeners.context === context)) { clearEvent(this, evt); } } else { for (var i = 0, events = [], length = listeners.length; i < length; i++) { if (listeners[i].fn !== fn || once && !listeners[i].once || context && listeners[i].context !== context) { events.push(listeners[i]); } } if (events.length) this._events[evt] = events.length === 1 ? events[0] : events; else clearEvent(this, evt); } return this; }; EventEmitter2.prototype.removeAllListeners = function removeAllListeners(event) { var evt; if (event) { evt = prefix ? prefix + event : event; if (this._events[evt]) clearEvent(this, evt); } else { this._events = new Events(); this._eventsCount = 0; } return this; }; EventEmitter2.prototype.off = EventEmitter2.prototype.removeListener; EventEmitter2.prototype.addListener = EventEmitter2.prototype.on; EventEmitter2.prefixed = prefix; EventEmitter2.EventEmitter = EventEmitter2; if ("undefined" !== typeof module2) { module2.exports = EventEmitter2; } } }); // node_modules/canvas/lib/parse-font.js var require_parse_font = __commonJS({ "node_modules/canvas/lib/parse-font.js"(exports, module2) { "use strict"; var weights = "bold|bolder|lighter|[1-9]00"; var styles = "italic|oblique"; var variants = "small-caps"; var stretches = "ultra-condensed|extra-condensed|condensed|semi-condensed|semi-expanded|expanded|extra-expanded|ultra-expanded"; var units = "px|pt|pc|in|cm|mm|%|em|ex|ch|rem|q"; var string = /'((\\'|[^'])+)'|"((\\"|[^"])+)"|[\w\s-]+/.source; var weightRe = new RegExp(`(${weights}) +`, "i"); var styleRe = new RegExp(`(${styles}) +`, "i"); var variantRe = new RegExp(`(${variants}) +`, "i"); var stretchRe = new RegExp(`(${stretches}) +`, "i"); var familyRe = new RegExp(string, "g"); var unquoteRe = /^['"](.*)['"]$/; var unescapeRe = /\\(['"])/g; var sizeFamilyRe = new RegExp( `([\\d\\.]+)(${units}) *((?:${string})( *, *(?:${string}))*)` ); var cache = {}; var defaultHeight = 16; module2.exports = (str) => { if (cache[str]) return cache[str]; const sizeFamily = sizeFamilyRe.exec(str); if (!sizeFamily) return; const names = sizeFamily[3].match(familyRe).map((s) => s.trim().replace(unquoteRe, "$1").replace(unescapeRe, "$1")).filter((s) => !!s); const font = { weight: "normal", style: "normal", stretch: "normal", variant: "normal", size: parseFloat(sizeFamily[1]), unit: sizeFamily[2], family: names.join(",") }; let weight, style, variant, stretch; const substr = str.substring(0, sizeFamily.index); if (weight = weightRe.exec(substr)) font.weight = weight[1]; if (style = styleRe.exec(substr)) font.style = style[1]; if (variant = variantRe.exec(substr)) font.variant = variant[1]; if (stretch = stretchRe.exec(substr)) font.stretch = stretch[1]; switch (font.unit) { case "pt": font.size /= 0.75; break; case "pc": font.size *= 16; break; case "in": font.size *= 96; break; case "cm": font.size *= 96 / 2.54; break; case "mm": font.size *= 96 / 25.4; break; case "%": break; case "em": case "rem": font.size *= defaultHeight / 0.75; break; case "q": font.size *= 96 / 25.4 / 4; break; } return cache[str] = font; }; } }); // node_modules/canvas/browser.js var require_browser = __commonJS({ "node_modules/canvas/browser.js"(exports) { var parseFont = require_parse_font(); exports.parseFont = parseFont; exports.createCanvas = function(width, height) { return Object.assign(document.createElement("canvas"), { width, height }); }; exports.createImageData = function(array, width, height) { switch (arguments.length) { case 0: return new ImageData(); case 1: return new ImageData(array); case 2: return new ImageData(array, width); default: return new ImageData(array, width, height); } }; exports.loadImage = function(src, options) { return new Promise(function(resolve2, reject) { const image = Object.assign(document.createElement("img"), options); function cleanup() { image.onload = null; image.onerror = null; } image.onload = function() { cleanup(); resolve2(image); }; image.onerror = function() { cleanup(); reject(new Error('Failed to load the image "' + src + '"')); }; image.src = src; }); }; } }); // src/main.ts var main_exports = {}; __export(main_exports, { default: () => AIImageAnalyzerPlugin }); module.exports = __toCommonJS(main_exports); var import_obsidian4 = require("obsidian"); // node_modules/ollama/dist/index.mjs var import_fs = __toESM(require("fs"), 1); var import_path = require("path"); // node_modules/whatwg-fetch/fetch.js var g = typeof globalThis !== "undefined" && globalThis || typeof self !== "undefined" && self || // eslint-disable-next-line no-undef typeof global !== "undefined" && global || {}; var support = { searchParams: "URLSearchParams" in g, iterable: "Symbol" in g && "iterator" in Symbol, blob: "FileReader" in g && "Blob" in g && function() { try { new Blob(); return true; } catch (e) { return false; } }(), formData: "FormData" in g, arrayBuffer: "ArrayBuffer" in g }; function isDataView(obj) { return obj && DataView.prototype.isPrototypeOf(obj); } if (support.arrayBuffer) { viewClasses = [ "[object Int8Array]", "[object Uint8Array]", "[object Uint8ClampedArray]", "[object Int16Array]", "[object Uint16Array]", "[object Int32Array]", "[object Uint32Array]", "[object Float32Array]", "[object Float64Array]" ]; isArrayBufferView = ArrayBuffer.isView || function(obj) { return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1; }; } var viewClasses; var isArrayBufferView; function normalizeName(name) { if (typeof name !== "string") { name = String(name); } if (/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(name) || name === "") { throw new TypeError('Invalid character in header field name: "' + name + '"'); } return name.toLowerCase(); } function normalizeValue(value) { if (typeof value !== "string") { value = String(value); } return value; } function iteratorFor(items) { var iterator = { next: function() { var value = items.shift(); return { done: value === void 0, value }; } }; if (support.iterable) { iterator[Symbol.iterator] = function() { return iterator; }; } return iterator; } function Headers(headers) { this.map = {}; if (headers instanceof Headers) { headers.forEach(function(value, name) { this.append(name, value); }, this); } else if (Array.isArray(headers)) { headers.forEach(function(header) { if (header.length != 2) { throw new TypeError("Headers constructor: expected name/value pair to be length 2, found" + header.length); } this.append(header[0], header[1]); }, this); } else if (headers) { Object.getOwnPropertyNames(headers).forEach(function(name) { this.append(name, headers[name]); }, this); } } Headers.prototype.append = function(name, value) { name = normalizeName(name); value = normalizeValue(value); var oldValue = this.map[name]; this.map[name] = oldValue ? oldValue + ", " + value : value; }; Headers.prototype["delete"] = function(name) { delete this.map[normalizeName(name)]; }; Headers.prototype.get = function(name) { name = normalizeName(name); return this.has(name) ? this.map[name] : null; }; Headers.prototype.has = function(name) { return this.map.hasOwnProperty(normalizeName(name)); }; Headers.prototype.set = function(name, value) { this.map[normalizeName(name)] = normalizeValue(value); }; Headers.prototype.forEach = function(callback, thisArg) { for (var name in this.map) { if (this.map.hasOwnProperty(name)) { callback.call(thisArg, this.map[name], name, this); } } }; Headers.prototype.keys = function() { var items = []; this.forEach(function(value, name) { items.push(name); }); return iteratorFor(items); }; Headers.prototype.values = function() { var items = []; this.forEach(function(value) { items.push(value); }); return iteratorFor(items); }; Headers.prototype.entries = function() { var items = []; this.forEach(function(value, name) { items.push([name, value]); }); return iteratorFor(items); }; if (support.iterable) { Headers.prototype[Symbol.iterator] = Headers.prototype.entries; } function consumed(body) { if (body._noBody) return; if (body.bodyUsed) { return Promise.reject(new TypeError("Already read")); } body.bodyUsed = true; } function fileReaderReady(reader) { return new Promise(function(resolve2, reject) { reader.onload = function() { resolve2(reader.result); }; reader.onerror = function() { reject(reader.error); }; }); } function readBlobAsArrayBuffer(blob) { var reader = new FileReader(); var promise = fileReaderReady(reader); reader.readAsArrayBuffer(blob); return promise; } function readBlobAsText(blob) { var reader = new FileReader(); var promise = fileReaderReady(reader); var match = /charset=([A-Za-z0-9_-]+)/.exec(blob.type); var encoding = match ? match[1] : "utf-8"; reader.readAsText(blob, encoding); return promise; } function readArrayBufferAsText(buf) { var view = new Uint8Array(buf); var chars = new Array(view.length); for (var i = 0; i < view.length; i++) { chars[i] = String.fromCharCode(view[i]); } return chars.join(""); } function bufferClone(buf) { if (buf.slice) { return buf.slice(0); } else { var view = new Uint8Array(buf.byteLength); view.set(new Uint8Array(buf)); return view.buffer; } } function Body() { this.bodyUsed = false; this._initBody = function(body) { this.bodyUsed = this.bodyUsed; this._bodyInit = body; if (!body) { this._noBody = true; this._bodyText = ""; } else if (typeof body === "string") { this._bodyText = body; } else if (support.blob && Blob.prototype.isPrototypeOf(body)) { this._bodyBlob = body; } else if (support.formData && FormData.prototype.isPrototypeOf(body)) { this._bodyFormData = body; } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { this._bodyText = body.toString(); } else if (support.arrayBuffer && support.blob && isDataView(body)) { this._bodyArrayBuffer = bufferClone(body.buffer); this._bodyInit = new Blob([this._bodyArrayBuffer]); } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) { this._bodyArrayBuffer = bufferClone(body); } else { this._bodyText = body = Object.prototype.toString.call(body); } if (!this.headers.get("content-type")) { if (typeof body === "string") { this.headers.set("content-type", "text/plain;charset=UTF-8"); } else if (this._bodyBlob && this._bodyBlob.type) { this.headers.set("content-type", this._bodyBlob.type); } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { this.headers.set("content-type", "application/x-www-form-urlencoded;charset=UTF-8"); } } }; if (support.blob) { this.blob = function() { var rejected = consumed(this); if (rejected) { return rejected; } if (this._bodyBlob) { return Promise.resolve(this._bodyBlob); } else if (this._bodyArrayBuffer) { return Promise.resolve(new Blob([this._bodyArrayBuffer])); } else if (this._bodyFormData) { throw new Error("could not read FormData body as blob"); } else { return Promise.resolve(new Blob([this._bodyText])); } }; } this.arrayBuffer = function() { if (this._bodyArrayBuffer) { var isConsumed = consumed(this); if (isConsumed) { return isConsumed; } else if (ArrayBuffer.isView(this._bodyArrayBuffer)) { return Promise.resolve( this._bodyArrayBuffer.buffer.slice( this._bodyArrayBuffer.byteOffset, this._bodyArrayBuffer.byteOffset + this._bodyArrayBuffer.byteLength ) ); } else { return Promise.resolve(this._bodyArrayBuffer); } } else if (support.blob) { return this.blob().then(readBlobAsArrayBuffer); } else { throw new Error("could not read as ArrayBuffer"); } }; this.text = function() { var rejected = consumed(this); if (rejected) { return rejected; } if (this._bodyBlob) { return readBlobAsText(this._bodyBlob); } else if (this._bodyArrayBuffer) { return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer)); } else if (this._bodyFormData) { throw new Error("could not read FormData body as text"); } else { return Promise.resolve(this._bodyText); } }; if (support.formData) { this.formData = function() { return this.text().then(decode); }; } this.json = function() { return this.text().then(JSON.parse); }; return this; } var methods = ["CONNECT", "DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT", "TRACE"]; function normalizeMethod(method) { var upcased = method.toUpperCase(); return methods.indexOf(upcased) > -1 ? upcased : method; } function Request(input, options) { if (!(this instanceof Request)) { throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.'); } options = options || {}; var body = options.body; if (input instanceof Request) { if (input.bodyUsed) { throw new TypeError("Already read"); } this.url = input.url; this.credentials = input.credentials; if (!options.headers) { this.headers = new Headers(input.headers); } this.method = input.method; this.mode = input.mode; this.signal = input.signal; if (!body && input._bodyInit != null) { body = input._bodyInit; input.bodyUsed = true; } } else { this.url = String(input); } this.credentials = options.credentials || this.credentials || "same-origin"; if (options.headers || !this.headers) { this.headers = new Headers(options.headers); } this.method = normalizeMethod(options.method || this.method || "GET"); this.mode = options.mode || this.mode || null; this.signal = options.signal || this.signal || function() { if ("AbortController" in g) { var ctrl = new AbortController(); return ctrl.signal; } }(); this.referrer = null; if ((this.method === "GET" || this.method === "HEAD") && body) { throw new TypeError("Body not allowed for GET or HEAD requests"); } this._initBody(body); if (this.method === "GET" || this.method === "HEAD") { if (options.cache === "no-store" || options.cache === "no-cache") { var reParamSearch = /([?&])_=[^&]*/; if (reParamSearch.test(this.url)) { this.url = this.url.replace(reParamSearch, "$1_=" + (/* @__PURE__ */ new Date()).getTime()); } else { var reQueryString = /\?/; this.url += (reQueryString.test(this.url) ? "&" : "?") + "_=" + (/* @__PURE__ */ new Date()).getTime(); } } } } Request.prototype.clone = function() { return new Request(this, { body: this._bodyInit }); }; function decode(body) { var form = new FormData(); body.trim().split("&").forEach(function(bytes) { if (bytes) { var split = bytes.split("="); var name = split.shift().replace(/\+/g, " "); var value = split.join("=").replace(/\+/g, " "); form.append(decodeURIComponent(name), decodeURIComponent(value)); } }); return form; } function parseHeaders(rawHeaders) { var headers = new Headers(); var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, " "); preProcessedHeaders.split("\r").map(function(header) { return header.indexOf("\n") === 0 ? header.substr(1, header.length) : header; }).forEach(function(line) { var parts = line.split(":"); var key = parts.shift().trim(); if (key) { var value = parts.join(":").trim(); try { headers.append(key, value); } catch (error) { console.warn("Response " + error.message); } } }); return headers; } Body.call(Request.prototype); function Response(bodyInit, options) { if (!(this instanceof Response)) { throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.'); } if (!options) { options = {}; } this.type = "default"; this.status = options.status === void 0 ? 200 : options.status; if (this.status < 200 || this.status > 599) { throw new RangeError("Failed to construct 'Response': The status provided (0) is outside the range [200, 599]."); } this.ok = this.status >= 200 && this.status < 300; this.statusText = options.statusText === void 0 ? "" : "" + options.statusText; this.headers = new Headers(options.headers); this.url = options.url || ""; this._initBody(bodyInit); } Body.call(Response.prototype); Response.prototype.clone = function() { return new Response(this._bodyInit, { status: this.status, statusText: this.statusText, headers: new Headers(this.headers), url: this.url }); }; Response.error = function() { var response = new Response(null, { status: 200, statusText: "" }); response.ok = false; response.status = 0; response.type = "error"; return response; }; var redirectStatuses = [301, 302, 303, 307, 308]; Response.redirect = function(url, status) { if (redirectStatuses.indexOf(status) === -1) { throw new RangeError("Invalid status code"); } return new Response(null, { status, headers: { location: url } }); }; var DOMException2 = g.DOMException; try { new DOMException2(); } catch (err) { DOMException2 = function(message, name) { this.message = message; this.name = name; var error = Error(message); this.stack = error.stack; }; DOMException2.prototype = Object.create(Error.prototype); DOMException2.prototype.constructor = DOMException2; } function fetch2(input, init) { return new Promise(function(resolve2, reject) { var request = new Request(input, init); if (request.signal && request.signal.aborted) { return reject(new DOMException2("Aborted", "AbortError")); } var xhr = new XMLHttpRequest(); function abortXhr() { xhr.abort(); } xhr.onload = function() { var options = { statusText: xhr.statusText, headers: parseHeaders(xhr.getAllResponseHeaders() || "") }; if (request.url.indexOf("file://") === 0 && (xhr.status < 200 || xhr.status > 599)) { options.status = 200; } else { options.status = xhr.status; } options.url = "responseURL" in xhr ? xhr.responseURL : options.headers.get("X-Request-URL"); var body = "response" in xhr ? xhr.response : xhr.responseText; setTimeout(function() { resolve2(new Response(body, options)); }, 0); }; xhr.onerror = function() { setTimeout(function() { reject(new TypeError("Network request failed")); }, 0); }; xhr.ontimeout = function() { setTimeout(function() { reject(new TypeError("Network request timed out")); }, 0); }; xhr.onabort = function() { setTimeout(function() { reject(new DOMException2("Aborted", "AbortError")); }, 0); }; function fixUrl(url) { try { return url === "" && g.location.href ? g.location.href : url; } catch (e) { return url; } } xhr.open(request.method, fixUrl(request.url), true); if (request.credentials === "include") { xhr.withCredentials = true; } else if (request.credentials === "omit") { xhr.withCredentials = false; } if ("responseType" in xhr) { if (support.blob) { xhr.responseType = "blob"; } else if (support.arrayBuffer) { xhr.responseType = "arraybuffer"; } } if (init && typeof init.headers === "object" && !(init.headers instanceof Headers || g.Headers && init.headers instanceof g.Headers)) { var names = []; Object.getOwnPropertyNames(init.headers).forEach(function(name) { names.push(normalizeName(name)); xhr.setRequestHeader(name, normalizeValue(init.headers[name])); }); request.headers.forEach(function(value, name) { if (names.indexOf(name) === -1) { xhr.setRequestHeader(name, value); } }); } else { request.headers.forEach(function(value, name) { xhr.setRequestHeader(name, value); }); } if (request.signal) { request.signal.addEventListener("abort", abortXhr); xhr.onreadystatechange = function() { if (xhr.readyState === 4) { request.signal.removeEventListener("abort", abortXhr); } }; } xhr.send(typeof request._bodyInit === "undefined" ? null : request._bodyInit); }); } fetch2.polyfill = true; if (!g.fetch) { g.fetch = fetch2; g.Headers = Headers; g.Request = Request; g.Response = Response; } // node_modules/ollama/dist/browser.mjs var version = "0.5.12"; var __defProp$1 = Object.defineProperty; var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField$1 = (obj, key, value) => { __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; var ResponseError = class _ResponseError extends Error { constructor(error, status_code) { super(error); this.error = error; this.status_code = status_code; this.name = "ResponseError"; if (Error.captureStackTrace) { Error.captureStackTrace(this, _ResponseError); } } }; var AbortableAsyncIterator = class { constructor(abortController, itr, doneCallback) { __publicField$1(this, "abortController"); __publicField$1(this, "itr"); __publicField$1(this, "doneCallback"); this.abortController = abortController; this.itr = itr; this.doneCallback = doneCallback; } abort() { this.abortController.abort(); } async *[Symbol.asyncIterator]() { for await (const message of this.itr) { if ("error" in message) { throw new Error(message.error); } yield message; if (message.done || message.status === "success") { this.doneCallback(); return; } } throw new Error("Did not receive done or success response in stream."); } }; var checkOk = async (response) => { var _a; if (response.ok) { return; } let message = `Error ${response.status}: ${response.statusText}`; let errorData = null; if ((_a = response.headers.get("content-type")) == null ? void 0 : _a.includes("application/json")) { try { errorData = await response.json(); message = errorData.error || message; } catch (error) { console.log("Failed to parse error response as JSON"); } } else { try { console.log("Getting text from response"); const textResponse = await response.text(); message = textResponse || message; } catch (error) { console.log("Failed to get text from error response"); } } throw new ResponseError(message, response.status); }; function getPlatform() { if (typeof window !== "undefined" && window.navigator) { return `${window.navigator.platform.toLowerCase()} Browser/${navigator.userAgent};`; } else if (typeof process !== "undefined") { return `${process.arch} ${process.platform} Node.js/${process.version}`; } return ""; } var fetchWithHeaders = async (fetch3, url, options = {}) => { const defaultHeaders = { "Content-Type": "application/json", Accept: "application/json", "User-Agent": `ollama-js/${version} (${getPlatform()})` }; if (!options.headers) { options.headers = {}; } const customHeaders = Object.fromEntries( Object.entries(options.headers).filter(([key]) => !Object.keys(defaultHeaders).some((defaultKey) => defaultKey.toLowerCase() === key.toLowerCase())) ); options.headers = { ...defaultHeaders, ...customHeaders }; return fetch3(url, options); }; var get = async (fetch3, host, options) => { const response = await fetchWithHeaders(fetch3, host, { headers: options == null ? void 0 : options.headers }); await checkOk(response); return response; }; var post = async (fetch3, host, data, options) => { const isRecord = (input) => { return input !== null && typeof input === "object" && !Array.isArray(input); }; const formattedData = isRecord(data) ? JSON.stringify(data) : data; const response = await fetchWithHeaders(fetch3, host, { method: "POST", body: formattedData, signal: options == null ? void 0 : options.signal, headers: options == null ? void 0 : options.headers }); await checkOk(response); return response; }; var del = async (fetch3, host, data, options) => { const response = await fetchWithHeaders(fetch3, host, { method: "DELETE", body: JSON.stringify(data), headers: options == null ? void 0 : options.headers }); await checkOk(response); return response; }; var parseJSON = async function* (itr) { var _a; const decoder = new TextDecoder("utf-8"); let buffer = ""; const reader = itr.getReader(); while (true) { const { done, value: chunk } = await reader.read(); if (done) { break; } buffer += decoder.decode(chunk); const parts = buffer.split("\n"); buffer = (_a = parts.pop()) != null ? _a : ""; for (const part of parts) { try { yield JSON.parse(part); } catch (error) { console.warn("invalid json: ", part); } } } for (const part of buffer.split("\n").filter((p) => p !== "")) { try { yield JSON.parse(part); } catch (error) { console.warn("invalid json: ", part); } } }; var formatHost = (host) => { if (!host) { return "http://127.0.0.1:11434"; } let isExplicitProtocol = host.includes("://"); if (host.startsWith(":")) { host = `http://127.0.0.1${host}`; isExplicitProtocol = true; } if (!isExplicitProtocol) { host = `http://${host}`; } const url = new URL(host); let port = url.port; if (!port) { if (!isExplicitProtocol) { port = "11434"; } else { port = url.protocol === "https:" ? "443" : "80"; } } let formattedHost = `${url.protocol}//${url.hostname}:${port}${url.pathname}`; if (formattedHost.endsWith("/")) { formattedHost = formattedHost.slice(0, -1); } return formattedHost; }; var __defProp2 = Object.defineProperty; var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField2 = (obj, key, value) => { __defNormalProp2(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; var Ollama$1 = class Ollama { constructor(config) { var _a, _b; __publicField2(this, "config"); __publicField2(this, "fetch"); __publicField2(this, "ongoingStreamedRequests", []); this.config = { host: "", headers: config == null ? void 0 : config.headers }; if (!(config == null ? void 0 : config.proxy)) { this.config.host = formatHost((_a = config == null ? void 0 : config.host) != null ? _a : "http://127.0.0.1:11434"); } this.fetch = (_b = config == null ? void 0 : config.fetch) != null ? _b : fetch; } // Abort any ongoing streamed requests to Ollama abort() { for (const request of this.ongoingStreamedRequests) { request.abort(); } this.ongoingStreamedRequests.length = 0; } /** * Processes a request to the Ollama server. If the request is streamable, it will return a * AbortableAsyncIterator that yields the response messages. Otherwise, it will return the response * object. * @param endpoint {string} - The endpoint to send the request to. * @param request {object} - The request object to send to the endpoint. * @protected {T | AbortableAsyncIterator} - The response object or a AbortableAsyncIterator that yields * response messages. * @throws {Error} - If the response body is missing or if the response is an error. * @returns {Promise>} - The response object or a AbortableAsyncIterator that yields the streamed response. */ async processStreamableRequest(endpoint, request) { var _a; request.stream = (_a = request.stream) != null ? _a : false; const host = `${this.config.host}/api/${endpoint}`; if (request.stream) { const abortController = new AbortController(); const response2 = await post(this.fetch, host, request, { signal: abortController.signal, headers: this.config.headers }); if (!response2.body) { throw new Error("Missing body"); } const itr = parseJSON(response2.body); const abortableAsyncIterator = new AbortableAsyncIterator( abortController, itr, () => { const i = this.ongoingStreamedRequests.indexOf(abortableAsyncIterator); if (i > -1) { this.ongoingStreamedRequests.splice(i, 1); } } ); this.ongoingStreamedRequests.push(abortableAsyncIterator); return abortableAsyncIterator; } const response = await post(this.fetch, host, request, { headers: this.config.headers }); return await response.json(); } /** * Encodes an image to base64 if it is a Uint8Array. * @param image {Uint8Array | string} - The image to encode. * @returns {Promise} - The base64 encoded image. */ async encodeImage(image) { if (typeof image !== "string") { const uint8Array = new Uint8Array(image); let byteString = ""; const len = uint8Array.byteLength; for (let i = 0; i < len; i++) { byteString += String.fromCharCode(uint8Array[i]); } return btoa(byteString); } return image; } /** * Generates a response from a text prompt. * @param request {GenerateRequest} - The request object. * @returns {Promise>} - The response object or * an AbortableAsyncIterator that yields response messages. */ async generate(request) { if (request.images) { request.images = await Promise.all(request.images.map(this.encodeImage.bind(this))); } return this.processStreamableRequest("generate", request); } /** * Chats with the model. The request object can contain messages with images that are either * Uint8Arrays or base64 encoded strings. The images will be base64 encoded before sending the * request. * @param request {ChatRequest} - The request object. * @returns {Promise>} - The response object or an * AbortableAsyncIterator that yields response messages. */ async chat(request) { if (request.messages) { for (const message of request.messages) { if (message.images) { message.images = await Promise.all( message.images.map(this.encodeImage.bind(this)) ); } } } return this.processStreamableRequest("chat", request); } /** * Creates a new model from a stream of data. * @param request {CreateRequest} - The request object. * @returns {Promise>} - The response object or a stream of progress responses. */ async create(request) { return this.processStreamableRequest("create", { ...request }); } /** * Pulls a model from the Ollama registry. The request object can contain a stream flag to indicate if the * response should be streamed. * @param request {PullRequest} - The request object. * @returns {Promise>} - The response object or * an AbortableAsyncIterator that yields response messages. */ async pull(request) { return this.processStreamableRequest("pull", { name: request.model, stream: request.stream, insecure: request.insecure }); } /** * Pushes a model to the Ollama registry. The request object can contain a stream flag to indicate if the * response should be streamed. * @param request {PushRequest} - The request object. * @returns {Promise>} - The response object or * an AbortableAsyncIterator that yields response messages. */ async push(request) { return this.processStreamableRequest("push", { name: request.model, stream: request.stream, insecure: request.insecure }); } /** * Deletes a model from the server. The request object should contain the name of the model to * delete. * @param request {DeleteRequest} - The request object. * @returns {Promise} - The response object. */ async delete(request) { await del( this.fetch, `${this.config.host}/api/delete`, { name: request.model }, { headers: this.config.headers } ); return { status: "success" }; } /** * Copies a model from one name to another. The request object should contain the name of the * model to copy and the new name. * @param request {CopyRequest} - The request object. * @returns {Promise} - The response object. */ async copy(request) { await post(this.fetch, `${this.config.host}/api/copy`, { ...request }, { headers: this.config.headers }); return { status: "success" }; } /** * Lists the models on the server. * @returns {Promise} - The response object. * @throws {Error} - If the response body is missing. */ async list() { const response = await get(this.fetch, `${this.config.host}/api/tags`, { headers: this.config.headers }); return await response.json(); } /** * Shows the metadata of a model. The request object should contain the name of the model. * @param request {ShowRequest} - The request object. * @returns {Promise} - The response object. */ async show(request) { const response = await post(this.fetch, `${this.config.host}/api/show`, { ...request }, { headers: this.config.headers }); return await response.json(); } /** * Embeds text input into vectors. * @param request {EmbedRequest} - The request object. * @returns {Promise} - The response object. */ async embed(request) { const response = await post(this.fetch, `${this.config.host}/api/embed`, { ...request }, { headers: this.config.headers }); return await response.json(); } /** * Embeds a text prompt into a vector. * @param request {EmbeddingsRequest} - The request object. * @returns {Promise} - The response object. */ async embeddings(request) { const response = await post(this.fetch, `${this.config.host}/api/embeddings`, { ...request }, { headers: this.config.headers }); return await response.json(); } /** * Lists the running models on the server * @returns {Promise} - The response object. * @throws {Error} - If the response body is missing. */ async ps() { const response = await get(this.fetch, `${this.config.host}/api/ps`, { headers: this.config.headers }); return await response.json(); } }; var browser = new Ollama$1(); // node_modules/ollama/dist/index.mjs var Ollama2 = class extends Ollama$1 { async encodeImage(image) { if (typeof image !== "string") { return Buffer.from(image).toString("base64"); } try { if (import_fs.default.existsSync(image)) { const fileBuffer = await import_fs.promises.readFile((0, import_path.resolve)(image)); return Buffer.from(fileBuffer).toString("base64"); } } catch (e) { } return image; } /** * checks if a file exists * @param path {string} - The path to the file * @private @internal * @returns {Promise} - Whether the file exists or not */ async fileExists(path) { try { await import_fs.promises.access(path); return true; } catch (e) { return false; } } async create(request) { if (request.from && await this.fileExists((0, import_path.resolve)(request.from))) { throw Error("Creating with a local path is not currently supported from ollama-js"); } if (request.stream) { return super.create(request); } else { return super.create(request); } } }; var index = new Ollama2(); // src/cache.ts var import_crypto = require("crypto"); // package.json var version2 = "0.2.0"; // node_modules/eventemitter3/index.mjs var import_index = __toESM(require_eventemitter3(), 1); // node_modules/p-timeout/index.js var TimeoutError = class extends Error { constructor(message) { super(message); this.name = "TimeoutError"; } }; var AbortError = class extends Error { constructor(message) { super(); this.name = "AbortError"; this.message = message; } }; var getDOMException = (errorMessage) => globalThis.DOMException === void 0 ? new AbortError(errorMessage) : new DOMException(errorMessage); var getAbortedReason = (signal) => { const reason = signal.reason === void 0 ? getDOMException("This operation was aborted.") : signal.reason; return reason instanceof Error ? reason : getDOMException(reason); }; function pTimeout(promise, options) { const { milliseconds, fallback, message, customTimers = { setTimeout, clearTimeout } } = options; let timer; let abortHandler; const wrappedPromise = new Promise((resolve2, reject) => { if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) { throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``); } if (options.signal) { const { signal } = options; if (signal.aborted) { reject(getAbortedReason(signal)); } abortHandler = () => { reject(getAbortedReason(signal)); }; signal.addEventListener("abort", abortHandler, { once: true }); } if (milliseconds === Number.POSITIVE_INFINITY) { promise.then(resolve2, reject); return; } const timeoutError = new TimeoutError(); timer = customTimers.setTimeout.call(void 0, () => { if (fallback) { try { resolve2(fallback()); } catch (error) { reject(error); } return; } if (typeof promise.cancel === "function") { promise.cancel(); } if (message === false) { resolve2(); } else if (message instanceof Error) { reject(message); } else { timeoutError.message = message != null ? message : `Promise timed out after ${milliseconds} milliseconds`; reject(timeoutError); } }, milliseconds); (async () => { try { resolve2(await promise); } catch (error) { reject(error); } })(); }); const cancelablePromise = wrappedPromise.finally(() => { cancelablePromise.clear(); if (abortHandler && options.signal) { options.signal.removeEventListener("abort", abortHandler); } }); cancelablePromise.clear = () => { customTimers.clearTimeout.call(void 0, timer); timer = void 0; }; return cancelablePromise; } // node_modules/p-queue/dist/lower-bound.js function lowerBound(array, value, comparator) { let first = 0; let count = array.length; while (count > 0) { const step = Math.trunc(count / 2); let it = first + step; if (comparator(array[it], value) <= 0) { first = ++it; count -= step + 1; } else { count = step; } } return first; } // node_modules/p-queue/dist/priority-queue.js var _queue; var PriorityQueue = class { constructor() { __privateAdd(this, _queue, []); } enqueue(run, options) { options = { priority: 0, ...options }; const element = { priority: options.priority, run }; if (this.size && __privateGet(this, _queue)[this.size - 1].priority >= options.priority) { __privateGet(this, _queue).push(element); return; } const index2 = lowerBound(__privateGet(this, _queue), element, (a, b) => b.priority - a.priority); __privateGet(this, _queue).splice(index2, 0, element); } dequeue() { const item = __privateGet(this, _queue).shift(); return item == null ? void 0 : item.run; } filter(options) { return __privateGet(this, _queue).filter((element) => element.priority === options.priority).map((element) => element.run); } get size() { return __privateGet(this, _queue).length; } }; _queue = new WeakMap(); // node_modules/p-queue/dist/index.js var _carryoverConcurrencyCount, _isIntervalIgnored, _intervalCount, _intervalCap, _interval, _intervalEnd, _intervalId, _timeoutId, _queue2, _queueClass, _pending, _concurrency, _isPaused, _throwOnTimeout, _PQueue_instances, doesIntervalAllowAnother_get, doesConcurrentAllowAnother_get, next_fn, onResumeInterval_fn, isIntervalPaused_get, tryToStartAnother_fn, initializeIntervalIfNeeded_fn, onInterval_fn, processQueue_fn, throwOnAbort_fn, onEvent_fn; var PQueue = class extends import_index.default { // TODO: The `throwOnTimeout` option should affect the return types of `add()` and `addAll()` constructor(options) { var _a, _b, _c, _d; super(); __privateAdd(this, _PQueue_instances); __privateAdd(this, _carryoverConcurrencyCount); __privateAdd(this, _isIntervalIgnored); __privateAdd(this, _intervalCount, 0); __privateAdd(this, _intervalCap); __privateAdd(this, _interval); __privateAdd(this, _intervalEnd, 0); __privateAdd(this, _intervalId); __privateAdd(this, _timeoutId); __privateAdd(this, _queue2); __privateAdd(this, _queueClass); __privateAdd(this, _pending, 0); // The `!` is needed because of https://github.com/microsoft/TypeScript/issues/32194 __privateAdd(this, _concurrency); __privateAdd(this, _isPaused); __privateAdd(this, _throwOnTimeout); /** Per-operation timeout in milliseconds. Operations fulfill once `timeout` elapses if they haven't already. Applies to each future operation. */ __publicField(this, "timeout"); options = { carryoverConcurrencyCount: false, intervalCap: Number.POSITIVE_INFINITY, interval: 0, concurrency: Number.POSITIVE_INFINITY, autoStart: true, queueClass: PriorityQueue, ...options }; if (!(typeof options.intervalCap === "number" && options.intervalCap >= 1)) { throw new TypeError(`Expected \`intervalCap\` to be a number from 1 and up, got \`${(_b = (_a = options.intervalCap) == null ? void 0 : _a.toString()) != null ? _b : ""}\` (${typeof options.intervalCap})`); } if (options.interval === void 0 || !(Number.isFinite(options.interval) && options.interval >= 0)) { throw new TypeError(`Expected \`interval\` to be a finite number >= 0, got \`${(_d = (_c = options.interval) == null ? void 0 : _c.toString()) != null ? _d : ""}\` (${typeof options.interval})`); } __privateSet(this, _carryoverConcurrencyCount, options.carryoverConcurrencyCount); __privateSet(this, _isIntervalIgnored, options.intervalCap === Number.POSITIVE_INFINITY || options.interval === 0); __privateSet(this, _intervalCap, options.intervalCap); __privateSet(this, _interval, options.interval); __privateSet(this, _queue2, new options.queueClass()); __privateSet(this, _queueClass, options.queueClass); this.concurrency = options.concurrency; this.timeout = options.timeout; __privateSet(this, _throwOnTimeout, options.throwOnTimeout === true); __privateSet(this, _isPaused, options.autoStart === false); } get concurrency() { return __privateGet(this, _concurrency); } set concurrency(newConcurrency) { if (!(typeof newConcurrency === "number" && newConcurrency >= 1)) { throw new TypeError(`Expected \`concurrency\` to be a number from 1 and up, got \`${newConcurrency}\` (${typeof newConcurrency})`); } __privateSet(this, _concurrency, newConcurrency); __privateMethod(this, _PQueue_instances, processQueue_fn).call(this); } async add(function_, options = {}) { options = { timeout: this.timeout, throwOnTimeout: __privateGet(this, _throwOnTimeout), ...options }; return new Promise((resolve2, reject) => { __privateGet(this, _queue2).enqueue(async () => { var _a; __privateWrapper(this, _pending)._++; __privateWrapper(this, _intervalCount)._++; try { (_a = options.signal) == null ? void 0 : _a.throwIfAborted(); let operation = function_({ signal: options.signal }); if (options.timeout) { operation = pTimeout(Promise.resolve(operation), { milliseconds: options.timeout }); } if (options.signal) { operation = Promise.race([operation, __privateMethod(this, _PQueue_instances, throwOnAbort_fn).call(this, options.signal)]); } const result = await operation; resolve2(result); this.emit("completed", result); } catch (error) { if (error instanceof TimeoutError && !options.throwOnTimeout) { resolve2(); return; } reject(error); this.emit("error", error); } finally { __privateMethod(this, _PQueue_instances, next_fn).call(this); } }, options); this.emit("add"); __privateMethod(this, _PQueue_instances, tryToStartAnother_fn).call(this); }); } async addAll(functions, options) { return Promise.all(functions.map(async (function_) => this.add(function_, options))); } /** Start (or resume) executing enqueued tasks within concurrency limit. No need to call this if queue is not paused (via `options.autoStart = false` or by `.pause()` method.) */ start() { if (!__privateGet(this, _isPaused)) { return this; } __privateSet(this, _isPaused, false); __privateMethod(this, _PQueue_instances, processQueue_fn).call(this); return this; } /** Put queue execution on hold. */ pause() { __privateSet(this, _isPaused, true); } /** Clear the queue. */ clear() { __privateSet(this, _queue2, new (__privateGet(this, _queueClass))()); } /** Can be called multiple times. Useful if you for example add additional items at a later time. @returns A promise that settles when the queue becomes empty. */ async onEmpty() { if (__privateGet(this, _queue2).size === 0) { return; } await __privateMethod(this, _PQueue_instances, onEvent_fn).call(this, "empty"); } /** @returns A promise that settles when the queue size is less than the given limit: `queue.size < limit`. If you want to avoid having the queue grow beyond a certain size you can `await queue.onSizeLessThan()` before adding a new item. Note that this only limits the number of items waiting to start. There could still be up to `concurrency` jobs already running that this call does not include in its calculation. */ async onSizeLessThan(limit) { if (__privateGet(this, _queue2).size < limit) { return; } await __privateMethod(this, _PQueue_instances, onEvent_fn).call(this, "next", () => __privateGet(this, _queue2).size < limit); } /** The difference with `.onEmpty` is that `.onIdle` guarantees that all work from the queue has finished. `.onEmpty` merely signals that the queue is empty, but it could mean that some promises haven't completed yet. @returns A promise that settles when the queue becomes empty, and all promises have completed; `queue.size === 0 && queue.pending === 0`. */ async onIdle() { if (__privateGet(this, _pending) === 0 && __privateGet(this, _queue2).size === 0) { return; } await __privateMethod(this, _PQueue_instances, onEvent_fn).call(this, "idle"); } /** Size of the queue, the number of queued items waiting to run. */ get size() { return __privateGet(this, _queue2).size; } /** Size of the queue, filtered by the given options. For example, this can be used to find the number of items remaining in the queue with a specific priority level. */ sizeBy(options) { return __privateGet(this, _queue2).filter(options).length; } /** Number of running items (no longer in the queue). */ get pending() { return __privateGet(this, _pending); } /** Whether the queue is currently paused. */ get isPaused() { return __privateGet(this, _isPaused); } }; _carryoverConcurrencyCount = new WeakMap(); _isIntervalIgnored = new WeakMap(); _intervalCount = new WeakMap(); _intervalCap = new WeakMap(); _interval = new WeakMap(); _intervalEnd = new WeakMap(); _intervalId = new WeakMap(); _timeoutId = new WeakMap(); _queue2 = new WeakMap(); _queueClass = new WeakMap(); _pending = new WeakMap(); _concurrency = new WeakMap(); _isPaused = new WeakMap(); _throwOnTimeout = new WeakMap(); _PQueue_instances = new WeakSet(); doesIntervalAllowAnother_get = function() { return __privateGet(this, _isIntervalIgnored) || __privateGet(this, _intervalCount) < __privateGet(this, _intervalCap); }; doesConcurrentAllowAnother_get = function() { return __privateGet(this, _pending) < __privateGet(this, _concurrency); }; next_fn = function() { __privateWrapper(this, _pending)._--; __privateMethod(this, _PQueue_instances, tryToStartAnother_fn).call(this); this.emit("next"); }; onResumeInterval_fn = function() { __privateMethod(this, _PQueue_instances, onInterval_fn).call(this); __privateMethod(this, _PQueue_instances, initializeIntervalIfNeeded_fn).call(this); __privateSet(this, _timeoutId, void 0); }; isIntervalPaused_get = function() { const now = Date.now(); if (__privateGet(this, _intervalId) === void 0) { const delay = __privateGet(this, _intervalEnd) - now; if (delay < 0) { __privateSet(this, _intervalCount, __privateGet(this, _carryoverConcurrencyCount) ? __privateGet(this, _pending) : 0); } else { if (__privateGet(this, _timeoutId) === void 0) { __privateSet(this, _timeoutId, setTimeout(() => { __privateMethod(this, _PQueue_instances, onResumeInterval_fn).call(this); }, delay)); } return true; } } return false; }; tryToStartAnother_fn = function() { if (__privateGet(this, _queue2).size === 0) { if (__privateGet(this, _intervalId)) { clearInterval(__privateGet(this, _intervalId)); } __privateSet(this, _intervalId, void 0); this.emit("empty"); if (__privateGet(this, _pending) === 0) { this.emit("idle"); } return false; } if (!__privateGet(this, _isPaused)) { const canInitializeInterval = !__privateGet(this, _PQueue_instances, isIntervalPaused_get); if (__privateGet(this, _PQueue_instances, doesIntervalAllowAnother_get) && __privateGet(this, _PQueue_instances, doesConcurrentAllowAnother_get)) { const job = __privateGet(this, _queue2).dequeue(); if (!job) { return false; } this.emit("active"); job(); if (canInitializeInterval) { __privateMethod(this, _PQueue_instances, initializeIntervalIfNeeded_fn).call(this); } return true; } } return false; }; initializeIntervalIfNeeded_fn = function() { if (__privateGet(this, _isIntervalIgnored) || __privateGet(this, _intervalId) !== void 0) { return; } __privateSet(this, _intervalId, setInterval(() => { __privateMethod(this, _PQueue_instances, onInterval_fn).call(this); }, __privateGet(this, _interval))); __privateSet(this, _intervalEnd, Date.now() + __privateGet(this, _interval)); }; onInterval_fn = function() { if (__privateGet(this, _intervalCount) === 0 && __privateGet(this, _pending) === 0 && __privateGet(this, _intervalId)) { clearInterval(__privateGet(this, _intervalId)); __privateSet(this, _intervalId, void 0); } __privateSet(this, _intervalCount, __privateGet(this, _carryoverConcurrencyCount) ? __privateGet(this, _pending) : 0); __privateMethod(this, _PQueue_instances, processQueue_fn).call(this); }; /** Executes all queued functions until it reaches the limit. */ processQueue_fn = function() { while (__privateMethod(this, _PQueue_instances, tryToStartAnother_fn).call(this)) { } }; throwOnAbort_fn = async function(signal) { return new Promise((_resolve, reject) => { signal.addEventListener("abort", () => { reject(signal.reason); }, { once: true }); }); }; onEvent_fn = async function(event, filter) { return new Promise((resolve2) => { const listener = () => { if (filter && !filter()) { return; } this.off(event, listener); resolve2(); }; this.on(event, listener); }); }; // src/globals.ts var libVersion = version2; var possibleModels = [ { name: "llava-llama3 (8B) [default]", model: "llava-llama3:latest" }, { name: "llama3.2-vision (11B)", model: "llama3.2-vision:11b" }, { name: "llama3.2-vision (90B)", model: "llama3.2-vision:90b" }, { name: "llava (7B)", model: "llava:latest" }, { name: "llava (13B)", model: "llava:13b" }, { name: "llava (34B)", model: "llava:34b" } ]; var imagesProcessQueue = new PQueue({ concurrency: 1, timeout: 6e5 }); // src/cache.ts function getCacheBasePath() { return `${app.vault.configDir}/plugins/ai-image-analyzer/cache`; } function getCachePath(file) { const hash = (0, import_crypto.createHash)("md5").update(file.path).digest("hex"); const folder = `${getCacheBasePath()}`; const filename = `${hash}.json`; return `${folder}/${filename}`; } async function isInCache(file) { const path = getCachePath(file); return await app.vault.adapter.exists(path); } async function writeCache(file, text) { const path = getCachePath(file); if (!await this.app.vault.adapter.exists(getCacheBasePath())) { await this.app.vault.adapter.mkdir(getCacheBasePath()); } const data = { path: file.path, text, libVersion }; await this.app.vault.adapter.write(path, JSON.stringify(data)); } async function readCache(file) { const path = getCachePath(file); try { if (await this.app.vault.adapter.exists(path)) { const raw = await this.app.vault.adapter.read(path); return JSON.parse(raw); } } catch (e) { console.error(e); } return null; } async function removeFromCache(file) { const path = getCachePath(file); if (await isInCache(file)) { return await this.app.vault.adapter.remove(path); } } async function clearCache() { const path = getCacheBasePath(); if (await this.app.vault.adapter.exists(path)) { return await this.app.vault.adapter.rmdir(path, true); } } // src/ollamaManager.ts var import_obsidian3 = require("obsidian"); // src/util.ts var import_obsidian2 = require("obsidian"); // src/settings.ts var import_obsidian = require("obsidian"); var DEFAULT_SETTINGS = { debug: false, ollamaURL: "http://127.0.0.1:11434", ollamaToken: "", ollamaModel: possibleModels[0], prompt: "Describe the image. Just use Keywords. For example: cat, dog, tree. This must be Computer readable. The provided pictures are used in an notebook. Please provide at least 5 Keywords. It will be used to search for the image later.", autoClearCache: true }; var settings = Object.assign({}, DEFAULT_SETTINGS); async function loadSettings(plugin) { settings = Object.assign({}, DEFAULT_SETTINGS, await plugin.loadData()); } async function saveSettings(plugin) { setOllama(new Ollama2({ host: settings.ollamaURL, headers: { "Authorization": `Bearer ${settings.ollamaToken}` } })); await plugin.saveData(settings); } var AIImageAnalyzerSettingsTab = class extends import_obsidian.PluginSettingTab { constructor(app2, plugin) { super(app2, plugin); this.plugin = plugin; } display() { const { containerEl } = this; containerEl.empty(); new import_obsidian.Setting(containerEl).setName("Model").setDesc("Select the model to use").addDropdown((dropdown) => dropdown.addOptions(possibleModels.reduce((acc, model) => { acc[model.name] = model.name; return acc; }, {})).setValue(possibleModels.find((model) => model.model === settings.ollamaModel.model).name).onChange(async (value) => { settings.ollamaModel = possibleModels.find((model) => model.name === value); await saveSettings(this.plugin); if (settings.autoClearCache) { await clearCache(); } })); new import_obsidian.Setting(containerEl).setName("Pull Model").setDesc("Pull the selected model").addButton((button) => button.setButtonText("Pull Model").onClick(async () => await pullImage())); new import_obsidian.Setting(containerEl).setName("Clear cache").setDesc("Clear the cache, reanalyzing images could take a while").addButton((button) => button.setButtonText("Clear cache").onClick(async () => { await clearCache(); new import_obsidian.Notice("Cache cleared"); })); new import_obsidian.Setting(containerEl).setName("Debug mode").setDesc("Enable debug mode to see logs in the console").addToggle((toggle) => toggle.setValue(settings.debug).onChange(async (value) => { settings.debug = value; await saveSettings(this.plugin); })); new import_obsidian.Setting(containerEl).setName("Ollama server").setHeading(); new import_obsidian.Setting(containerEl).setName("Ollama URL").setDesc("Set the URL for the Ollama server").addText((text) => text.setPlaceholder("Enter the host (http://127.0.0.1:11434)").setValue(settings.ollamaURL).onChange(async (value) => { if (value.length === 0) { value = DEFAULT_SETTINGS.ollamaURL; } settings.ollamaURL = value; await saveSettings(this.plugin); })); new import_obsidian.Setting(containerEl).setName("Ollama Token (Optional)").setDesc("Set the token for authentication with the Ollama server").addText((text) => text.setValue(settings.ollamaToken !== "" ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" : "").onChange(async (value) => { if (value.contains("\u2022")) { return; } settings.ollamaToken = value; await saveSettings(this.plugin); })); new import_obsidian.Setting(containerEl).setName("Advanced").setHeading(); new import_obsidian.Setting(containerEl).setName("Prompt").setDesc("Set the prompt for the Ollama model").addTextArea((text) => { text.inputEl.rows = 5; text.inputEl.cols = 50; return text.setPlaceholder("Enter the prompt").setValue(settings.prompt).onChange(async (value) => { if (value.length === 0) { value = DEFAULT_SETTINGS.prompt; } settings.prompt = value; await saveSettings(this.plugin); if (settings.autoClearCache) { await clearCache(); } }); }); new import_obsidian.Setting(containerEl).setName("Auto clear cache").setDesc("Clear the cache after changing the model or the prompt to reanalyze images (if toggled on the cache will be cleared)").addToggle((toggle) => toggle.setValue(settings.autoClearCache).onChange(async (value) => { settings.autoClearCache = value; if (value) { await clearCache(); new import_obsidian.Notice("Cache cleared"); } await saveSettings(this.plugin); })); } }; // src/util.ts var import_canvas = __toESM(require_browser()); function debugLog(message) { if (settings.debug) { console.log(message); } } function isImageFile(file) { const path = file.path; return path.endsWith(".png") || path.endsWith(".jpg") || path.endsWith(".jpeg") || path.endsWith(".webp") || path.endsWith(".svg"); } async function readFile(file) { if (file.path.endsWith(".svg")) { debugLog("Converting SVG to PNG"); try { const svgData = await this.app.vault.adapter.read(file.path); const canvas = (0, import_canvas.createCanvas)(1e3, 1e3); const context = canvas.getContext("2d"); const svgImage = await (0, import_canvas.loadImage)(`data:image/svg+xml;base64,${Buffer.from(svgData).toString("base64")}`); context.drawImage(svgImage, 0, 0, 1e3, 1e3); return canvas.toDataURL("image/png").split(",")[1]; } catch (error) { console.error("Error converting SVG to PNG:", error); throw error; } } else { return (0, import_obsidian2.arrayBufferToBase64)(await app.vault.readBinary(file)); } } // src/ollamaManager.ts var ollama; async function analyzeImage(file) { var _a; try { return (_a = await imagesProcessQueue.add(() => analyzeImageHandling(file))) != null ? _a : ""; } catch (e) { debugLog(e); return ""; } } async function analyzeImageHandling(file) { debugLog(`Analyzing image ${file.name}`); if (!isImageFile(file)) { return Promise.reject("File is not an image"); } if (await isInCache(file)) { debugLog("Cache hit"); const text = await readCache(file); if (text && text.text !== "") { debugLog("Reading from cache"); debugLog(`Image analyzed ${file.name}`); return Promise.resolve(text.text); } else { debugLog("Failed to read cache"); debugLog("Removing from cache"); await removeFromCache(file); } } debugLog(file); try { const data = await readFile(file); const response = await ollama.chat({ model: settings.ollamaModel.model, //llava:13b or llava or llava-llama3 messages: [{ role: "user", content: settings.prompt, images: [data] }] }); debugLog(response); await writeCache(file, response.message.content); debugLog(`Image analyzed ${file.name}`); return Promise.resolve(response.message.content); } catch (e) { debugLog(e); return Promise.reject("Failed to analyze image"); } } async function analyzeImageWithNotice(file) { try { const notice = new import_obsidian3.Notice("Analyzing image", 0); const text = await analyzeImage(file); notice.hide(); new import_obsidian3.Notice("Image analyzed"); return text; } catch (e) { debugLog(e); new import_obsidian3.Notice("Failed to analyze image"); new import_obsidian3.Notice(e.toString()); return ""; } } async function analyzeToClipboard(file) { try { const text = await analyzeImageWithNotice(file); await activeWindow.navigator.clipboard.writeText(text); new import_obsidian3.Notice("Text copied to clipboard"); } catch (e) { debugLog(e); } } async function pullImage() { let progressNotice; try { new import_obsidian3.Notice(`Pulling ${settings.ollamaModel.name} model started, this may take a while...`); const response = await ollama.pull({ model: settings.ollamaModel.model, stream: true }); progressNotice = new import_obsidian3.Notice(`Pulling ${settings.ollamaModel.name} model 0%`, 0); for await (const part of response) { debugLog(part); if (part.total !== null && part.completed !== null) { const percentage = part.completed / part.total * 100; if (!isNaN(percentage) && percentage !== Infinity && percentage !== -Infinity) { const roundedNumber = percentage.toFixed(2); const completed = (part.completed / 1e9).toFixed(2); const total = (part.total / 1e9).toFixed(2); progressNotice.setMessage(`Pulling ${settings.ollamaModel.name} model ${roundedNumber}% (${completed}GB/${total}GB)`); } } } progressNotice.hide(); new import_obsidian3.Notice(`${settings.ollamaModel.name} model pulled successfully`); } catch (e) { debugLog(e); progressNotice == null ? void 0 : progressNotice.hide(); new import_obsidian3.Notice(`Failed to pull ${settings.ollamaModel.name} model`); new import_obsidian3.Notice(e.toString()); } } async function checkOllama() { try { const models = await ollama.list(); debugLog(models); if (!models.models.some((model) => model.name === settings.ollamaModel.model)) { new import_obsidian3.Notice(`No ${settings.ollamaModel.name} model found, please make sure you have pulled it (you can pull it over the settings tab or choose another model)`); } } catch (e) { debugLog(e); new import_obsidian3.Notice("Failed to connect to Ollama."); new import_obsidian3.Notice(e.toString()); } } function setOllama(ollamaInstance) { ollama = ollamaInstance; } // src/main.ts var AIImageAnalyzerPlugin = class extends import_obsidian4.Plugin { constructor() { super(...arguments); this.api = { analyzeImage, canBeAnalyzed: isImageFile, isInCache }; } async onload() { debugLog("loading ai image analyzer plugin"); await loadSettings(this); setOllama(new Ollama2({ host: settings.ollamaURL, headers: { "Authorization": `Bearer ${settings.ollamaToken}` } })); await checkOllama(); this.addCommand({ id: "analyze-image-to-clipboard", name: "Analyze image to clipboard", checkCallback: (checking) => { const file = getActiveFile(); if (file != null && isImageFile(file)) { if (!checking) { analyzeToClipboard(file); } return true; } return false; } }); this.addCommand({ id: "analyze-image", name: "Analyze image", checkCallback: (checking) => { const file = getActiveFile(); if (file != null && isImageFile(file)) { if (!checking) { analyzeImageWithNotice(file); } return true; } return false; } }); this.addCommand({ id: "clear-cache-of-active-image", name: "Clear cache of active image", checkCallback: (checking) => { const file = getActiveFile(); if (file != null && isImageFile(file)) { if (!checking) { removeFromCache(file); new import_obsidian4.Notice("Cache cleared"); } return true; } return false; } }); this.registerEvent( this.app.workspace.on("file-menu", (menu, file, _source) => { if (file instanceof import_obsidian4.TFile && isImageFile(file)) { menu.addItem((item) => { item.setTitle("AI analyze image"); const submenu = item.setSubmenu(); submenu.addItem( (item2) => item2.setTitle("Analyze image to clipboard").setIcon("clipboard").onClick(() => { analyzeToClipboard(file); }) ); submenu.addItem( (item2) => item2.setTitle("Analyze image").setIcon("search").onClick(async () => { await removeFromCache(file); await analyzeImageWithNotice(file); }) ); submenu.addItem( (item2) => item2.setTitle("Clear cache").setIcon("trash").onClick(async () => { await removeFromCache(file); new import_obsidian4.Notice("Cache cleared"); }) ); }); } }) ); this.addSettingTab(new AIImageAnalyzerSettingsTab(this.app, this)); } onunload() { imagesProcessQueue.clear(); debugLog("unloading ai image analyzer plugin"); } }; function getActiveFile() { var _a, _b; return (_b = (_a = this.app.workspace.activeEditor) == null ? void 0 : _a.file) != null ? _b : this.app.workspace.getActiveFile(); } /* nosourcemap */