diff --git a/.obsidian/community-plugins.json b/.obsidian/community-plugins.json index d3f66fa..ea98e90 100644 --- a/.obsidian/community-plugins.json +++ b/.obsidian/community-plugins.json @@ -1,3 +1,5 @@ [ - "obsidian-git" + "obsidian-git", + "obsidian42-brat", + "qmd-search" ] \ No newline at end of file diff --git a/.obsidian/plugins/obsidian42-brat/brat-migrations.json b/.obsidian/plugins/obsidian42-brat/brat-migrations.json new file mode 100644 index 0000000..e51fb8b --- /dev/null +++ b/.obsidian/plugins/obsidian42-brat/brat-migrations.json @@ -0,0 +1,5 @@ +{ + "appliedMigrations": [ + "tokens-to-secretstorage-v1" + ] +} \ No newline at end of file diff --git a/.obsidian/plugins/obsidian42-brat/data.json b/.obsidian/plugins/obsidian42-brat/data.json new file mode 100644 index 0000000..cf3cbe7 --- /dev/null +++ b/.obsidian/plugins/obsidian42-brat/data.json @@ -0,0 +1,24 @@ +{ + "pluginList": [ + "achekulaev/obsidian-qmd" + ], + "pluginSubListFrozenVersion": [ + { + "repo": "achekulaev/obsidian-qmd", + "version": "" + } + ], + "themesList": [], + "updateAtStartup": true, + "updateThemesAtStartup": true, + "enableAfterInstall": true, + "loggingEnabled": false, + "loggingPath": "BRAT-log", + "loggingVerboseEnabled": false, + "debuggingMode": false, + "notificationsEnabled": true, + "globalTokenName": "", + "personalAccessToken": "", + "selectLatestPluginVersionByDefault": false, + "allowIncompatiblePlugins": false +} \ No newline at end of file diff --git a/.obsidian/plugins/obsidian42-brat/main.js b/.obsidian/plugins/obsidian42-brat/main.js new file mode 100644 index 0000000..932435c --- /dev/null +++ b/.obsidian/plugins/obsidian42-brat/main.js @@ -0,0 +1,45 @@ +"use strict";var tn=Object.create;var pe=Object.defineProperty;var nn=Object.getOwnPropertyDescriptor;var sn=Object.getOwnPropertyNames;var on=Object.getPrototypeOf,rn=Object.prototype.hasOwnProperty;var x=(s,e)=>()=>(e||s((e={exports:{}}).exports,e),e.exports),an=(s,e)=>{for(var t in e)pe(s,t,{get:e[t],enumerable:!0})},Ze=(s,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of sn(e))!rn.call(s,i)&&i!==t&&pe(s,i,{get:()=>e[i],enumerable:!(n=nn(e,i))||n.enumerable});return s};var ln=(s,e,t)=>(t=s!=null?tn(on(s)):{},Ze(e||!s||!s.__esModule?pe(t,"default",{value:s,enumerable:!0}):t,s)),un=s=>Ze(pe({},"__esModule",{value:!0}),s);var $e=x((ps,et)=>{"use strict";var cn=typeof process=="object"&&process.env&&process.env.NODE_DEBUG&&/\bsemver\b/i.test(process.env.NODE_DEBUG)?(...s)=>console.error("SEMVER",...s):()=>{};et.exports=cn});var xe=x((ms,tt)=>{"use strict";var gn="2.0.0",dn=Number.MAX_SAFE_INTEGER||9007199254740991,pn=16,mn=250,hn=["major","premajor","minor","preminor","patch","prepatch","prerelease"];tt.exports={MAX_LENGTH:256,MAX_SAFE_COMPONENT_LENGTH:pn,MAX_SAFE_BUILD_LENGTH:mn,MAX_SAFE_INTEGER:dn,RELEASE_TYPES:hn,SEMVER_SPEC_VERSION:gn,FLAG_INCLUDE_PRERELEASE:1,FLAG_LOOSE:2}});var Be=x((F,nt)=>{"use strict";var{MAX_SAFE_COMPONENT_LENGTH:Oe,MAX_SAFE_BUILD_LENGTH:fn,MAX_LENGTH:bn}=xe(),wn=$e();F=nt.exports={};var Tn=F.re=[],En=F.safeRe=[],h=F.src=[],yn=F.safeSrc=[],f=F.t={},vn=0,Fe="[a-zA-Z0-9-]",Pn=[["\\s",1],["\\d",bn],[Fe,fn]],Rn=s=>{for(let[e,t]of Pn)s=s.split(`${e}*`).join(`${e}{0,${t}}`).split(`${e}+`).join(`${e}{1,${t}}`);return s},y=(s,e,t)=>{let n=Rn(e),i=vn++;wn(s,i,e),f[s]=i,h[i]=e,yn[i]=n,Tn[i]=new RegExp(e,t?"g":void 0),En[i]=new RegExp(n,t?"g":void 0)};y("NUMERICIDENTIFIER","0|[1-9]\\d*");y("NUMERICIDENTIFIERLOOSE","\\d+");y("NONNUMERICIDENTIFIER",`\\d*[a-zA-Z-]${Fe}*`);y("MAINVERSION",`(${h[f.NUMERICIDENTIFIER]})\\.(${h[f.NUMERICIDENTIFIER]})\\.(${h[f.NUMERICIDENTIFIER]})`);y("MAINVERSIONLOOSE",`(${h[f.NUMERICIDENTIFIERLOOSE]})\\.(${h[f.NUMERICIDENTIFIERLOOSE]})\\.(${h[f.NUMERICIDENTIFIERLOOSE]})`);y("PRERELEASEIDENTIFIER",`(?:${h[f.NONNUMERICIDENTIFIER]}|${h[f.NUMERICIDENTIFIER]})`);y("PRERELEASEIDENTIFIERLOOSE",`(?:${h[f.NONNUMERICIDENTIFIER]}|${h[f.NUMERICIDENTIFIERLOOSE]})`);y("PRERELEASE",`(?:-(${h[f.PRERELEASEIDENTIFIER]}(?:\\.${h[f.PRERELEASEIDENTIFIER]})*))`);y("PRERELEASELOOSE",`(?:-?(${h[f.PRERELEASEIDENTIFIERLOOSE]}(?:\\.${h[f.PRERELEASEIDENTIFIERLOOSE]})*))`);y("BUILDIDENTIFIER",`${Fe}+`);y("BUILD",`(?:\\+(${h[f.BUILDIDENTIFIER]}(?:\\.${h[f.BUILDIDENTIFIER]})*))`);y("FULLPLAIN",`v?${h[f.MAINVERSION]}${h[f.PRERELEASE]}?${h[f.BUILD]}?`);y("FULL",`^${h[f.FULLPLAIN]}$`);y("LOOSEPLAIN",`[v=\\s]*${h[f.MAINVERSIONLOOSE]}${h[f.PRERELEASELOOSE]}?${h[f.BUILD]}?`);y("LOOSE",`^${h[f.LOOSEPLAIN]}$`);y("GTLT","((?:<|>)?=?)");y("XRANGEIDENTIFIERLOOSE",`${h[f.NUMERICIDENTIFIERLOOSE]}|x|X|\\*`);y("XRANGEIDENTIFIER",`${h[f.NUMERICIDENTIFIER]}|x|X|\\*`);y("XRANGEPLAIN",`[v=\\s]*(${h[f.XRANGEIDENTIFIER]})(?:\\.(${h[f.XRANGEIDENTIFIER]})(?:\\.(${h[f.XRANGEIDENTIFIER]})(?:${h[f.PRERELEASE]})?${h[f.BUILD]}?)?)?`);y("XRANGEPLAINLOOSE",`[v=\\s]*(${h[f.XRANGEIDENTIFIERLOOSE]})(?:\\.(${h[f.XRANGEIDENTIFIERLOOSE]})(?:\\.(${h[f.XRANGEIDENTIFIERLOOSE]})(?:${h[f.PRERELEASELOOSE]})?${h[f.BUILD]}?)?)?`);y("XRANGE",`^${h[f.GTLT]}\\s*${h[f.XRANGEPLAIN]}$`);y("XRANGELOOSE",`^${h[f.GTLT]}\\s*${h[f.XRANGEPLAINLOOSE]}$`);y("COERCEPLAIN",`(^|[^\\d])(\\d{1,${Oe}})(?:\\.(\\d{1,${Oe}}))?(?:\\.(\\d{1,${Oe}}))?`);y("COERCE",`${h[f.COERCEPLAIN]}(?:$|[^\\d])`);y("COERCEFULL",h[f.COERCEPLAIN]+`(?:${h[f.PRERELEASE]})?(?:${h[f.BUILD]})?(?:$|[^\\d])`);y("COERCERTL",h[f.COERCE],!0);y("COERCERTLFULL",h[f.COERCEFULL],!0);y("LONETILDE","(?:~>?)");y("TILDETRIM",`(\\s*)${h[f.LONETILDE]}\\s+`,!0);F.tildeTrimReplace="$1~";y("TILDE",`^${h[f.LONETILDE]}${h[f.XRANGEPLAIN]}$`);y("TILDELOOSE",`^${h[f.LONETILDE]}${h[f.XRANGEPLAINLOOSE]}$`);y("LONECARET","(?:\\^)");y("CARETTRIM",`(\\s*)${h[f.LONECARET]}\\s+`,!0);F.caretTrimReplace="$1^";y("CARET",`^${h[f.LONECARET]}${h[f.XRANGEPLAIN]}$`);y("CARETLOOSE",`^${h[f.LONECARET]}${h[f.XRANGEPLAINLOOSE]}$`);y("COMPARATORLOOSE",`^${h[f.GTLT]}\\s*(${h[f.LOOSEPLAIN]})$|^$`);y("COMPARATOR",`^${h[f.GTLT]}\\s*(${h[f.FULLPLAIN]})$|^$`);y("COMPARATORTRIM",`(\\s*)${h[f.GTLT]}\\s*(${h[f.LOOSEPLAIN]}|${h[f.XRANGEPLAIN]})`,!0);F.comparatorTrimReplace="$1$2$3";y("HYPHENRANGE",`^\\s*(${h[f.XRANGEPLAIN]})\\s+-\\s+(${h[f.XRANGEPLAIN]})\\s*$`);y("HYPHENRANGELOOSE",`^\\s*(${h[f.XRANGEPLAINLOOSE]})\\s+-\\s+(${h[f.XRANGEPLAINLOOSE]})\\s*$`);y("STAR","(<|>)?=?\\s*\\*");y("GTE0","^\\s*>=\\s*0\\.0\\.0\\s*$");y("GTE0PRE","^\\s*>=\\s*0\\.0\\.0-0\\s*$")});var it=x((hs,st)=>{"use strict";var In=Object.freeze({loose:!0}),Nn=Object.freeze({}),An=s=>s?typeof s!="object"?In:s:Nn;st.exports=An});var lt=x((fs,at)=>{"use strict";var ot=/^[0-9]+$/,rt=(s,e)=>{if(typeof s=="number"&&typeof e=="number")return s===e?0:srt(e,s);at.exports={compareIdentifiers:rt,rcompareIdentifiers:Cn}});var we=x((bs,ct)=>{"use strict";var me=$e(),{MAX_LENGTH:ut,MAX_SAFE_INTEGER:he}=xe(),{safeRe:fe,t:be}=Be(),Ln=it(),{compareIdentifiers:Me}=lt(),Ve=class s{constructor(e,t){if(t=Ln(t),e instanceof s){if(e.loose===!!t.loose&&e.includePrerelease===!!t.includePrerelease)return e;e=e.version}else if(typeof e!="string")throw new TypeError(`Invalid version. Must be a string. Got type "${typeof e}".`);if(e.length>ut)throw new TypeError(`version is longer than ${ut} characters`);me("SemVer",e,t),this.options=t,this.loose=!!t.loose,this.includePrerelease=!!t.includePrerelease;let n=e.trim().match(t.loose?fe[be.LOOSE]:fe[be.FULL]);if(!n)throw new TypeError(`Invalid Version: ${e}`);if(this.raw=e,this.major=+n[1],this.minor=+n[2],this.patch=+n[3],this.major>he||this.major<0)throw new TypeError("Invalid major version");if(this.minor>he||this.minor<0)throw new TypeError("Invalid minor version");if(this.patch>he||this.patch<0)throw new TypeError("Invalid patch version");n[4]?this.prerelease=n[4].split(".").map(i=>{if(/^[0-9]+$/.test(i)){let o=+i;if(o>=0&&oe.major?1:this.minore.minor?1:this.patche.patch?1:0}comparePre(e){if(e instanceof s||(e=new s(e,this.options)),this.prerelease.length&&!e.prerelease.length)return-1;if(!this.prerelease.length&&e.prerelease.length)return 1;if(!this.prerelease.length&&!e.prerelease.length)return 0;let t=0;do{let n=this.prerelease[t],i=e.prerelease[t];if(me("prerelease compare",t,n,i),n===void 0&&i===void 0)return 0;if(i===void 0)return 1;if(n===void 0)return-1;if(n===i)continue;return Me(n,i)}while(++t)}compareBuild(e){e instanceof s||(e=new s(e,this.options));let t=0;do{let n=this.build[t],i=e.build[t];if(me("build compare",t,n,i),n===void 0&&i===void 0)return 0;if(i===void 0)return 1;if(n===void 0)return-1;if(n===i)continue;return Me(n,i)}while(++t)}inc(e,t,n){if(e.startsWith("pre")){if(!t&&n===!1)throw new Error("invalid increment argument: identifier is empty");if(t){let i=`-${t}`.match(this.options.loose?fe[be.PRERELEASELOOSE]:fe[be.PRERELEASE]);if(!i||i[1]!==t)throw new Error(`invalid identifier: ${t}`)}}switch(e){case"premajor":this.prerelease.length=0,this.patch=0,this.minor=0,this.major++,this.inc("pre",t,n);break;case"preminor":this.prerelease.length=0,this.patch=0,this.minor++,this.inc("pre",t,n);break;case"prepatch":this.prerelease.length=0,this.inc("patch",t,n),this.inc("pre",t,n);break;case"prerelease":this.prerelease.length===0&&this.inc("patch",t,n),this.inc("pre",t,n);break;case"release":if(this.prerelease.length===0)throw new Error(`version ${this.raw} is not a prerelease`);this.prerelease.length=0;break;case"major":(this.minor!==0||this.patch!==0||this.prerelease.length===0)&&this.major++,this.minor=0,this.patch=0,this.prerelease=[];break;case"minor":(this.patch!==0||this.prerelease.length===0)&&this.minor++,this.patch=0,this.prerelease=[];break;case"patch":this.prerelease.length===0&&this.patch++,this.prerelease=[];break;case"pre":{let i=Number(n)?1:0;if(this.prerelease.length===0)this.prerelease=[i];else{let o=this.prerelease.length;for(;--o>=0;)typeof this.prerelease[o]=="number"&&(this.prerelease[o]++,o=-2);if(o===-1){if(t===this.prerelease.join(".")&&n===!1)throw new Error("invalid increment argument: identifier already exists");this.prerelease.push(i)}}if(t){let o=[t,i];n===!1&&(o=[t]),Me(this.prerelease[0],t)===0?isNaN(this.prerelease[1])&&(this.prerelease=o):this.prerelease=o}break}default:throw new Error(`invalid increment argument: ${e}`)}return this.raw=this.format(),this.build.length&&(this.raw+=`+${this.build.join(".")}`),this}};ct.exports=Ve});var Ue=x((ws,dt)=>{"use strict";var gt=we(),kn=(s,e,t)=>new gt(s,t).compare(new gt(e,t));dt.exports=kn});var ht=x((Ts,mt)=>{"use strict";var pt=we(),Sn=(s,e,t=!1)=>{if(s instanceof pt)return s;try{return new pt(s,e)}catch(n){if(!t)return null;throw n}};mt.exports=Sn});var _e=x((Es,ft)=>{"use strict";var Dn=we(),$n=ht(),{safeRe:Te,t:Ee}=Be(),xn=(s,e)=>{if(s instanceof Dn)return s;if(typeof s=="number"&&(s=String(s)),typeof s!="string")return null;e=e||{};let t=null;if(!e.rtl)t=s.match(e.includePrerelease?Te[Ee.COERCEFULL]:Te[Ee.COERCE]);else{let u=e.includePrerelease?Te[Ee.COERCERTLFULL]:Te[Ee.COERCERTL],g;for(;(g=u.exec(s))&&(!t||t.index+t[0].length!==s.length);)(!t||g.index+g[0].length!==t.index+t[0].length)&&(t=g),u.lastIndex=g.index+g[1].length+g[2].length;u.lastIndex=-1}if(t===null)return null;let n=t[2],i=t[3]||"0",o=t[4]||"0",a=e.includePrerelease&&t[5]?`-${t[5]}`:"",c=e.includePrerelease&&t[6]?`+${t[6]}`:"";return $n(`${n}.${i}.${o}${a}${c}`,e)};ft.exports=xn});var Kt=x(v=>{"use strict";Object.defineProperty(v,"__esModule",{value:!0});var I=require("obsidian"),qe="YYYY-MM-DD",Ye="gggg-[W]ww",Mt="YYYY-MM",Vt="YYYY-[Q]Q",Ut="YYYY";function oe(s){var t,n;let e=window.app.plugins.getPlugin("periodic-notes");return e&&((n=(t=e.settings)==null?void 0:t[s])==null?void 0:n.enabled)}function re(){var s,e,t,n;try{let{internalPlugins:i,plugins:o}=window.app;if(oe("daily")){let{format:g,folder:m,template:l}=((e=(s=o.getPlugin("periodic-notes"))==null?void 0:s.settings)==null?void 0:e.daily)||{};return{format:g||qe,folder:(m==null?void 0:m.trim())||"",template:(l==null?void 0:l.trim())||""}}let{folder:a,format:c,template:u}=((n=(t=i.getPluginById("daily-notes"))==null?void 0:t.instance)==null?void 0:n.options)||{};return{format:c||qe,folder:(a==null?void 0:a.trim())||"",template:(u==null?void 0:u.trim())||""}}catch(i){console.info("No custom daily note settings found!",i)}}function ae(){var s,e,t,n,i,o,a;try{let c=window.app.plugins,u=(s=c.getPlugin("calendar"))==null?void 0:s.options,g=(t=(e=c.getPlugin("periodic-notes"))==null?void 0:e.settings)==null?void 0:t.weekly;if(oe("weekly"))return{format:g.format||Ye,folder:((n=g.folder)==null?void 0:n.trim())||"",template:((i=g.template)==null?void 0:i.trim())||""};let m=u||{};return{format:m.weeklyNoteFormat||Ye,folder:((o=m.weeklyNoteFolder)==null?void 0:o.trim())||"",template:((a=m.weeklyNoteTemplate)==null?void 0:a.trim())||""}}catch(c){console.info("No custom weekly note settings found!",c)}}function le(){var e,t,n,i;let s=window.app.plugins;try{let o=oe("monthly")&&((t=(e=s.getPlugin("periodic-notes"))==null?void 0:e.settings)==null?void 0:t.monthly)||{};return{format:o.format||Mt,folder:((n=o.folder)==null?void 0:n.trim())||"",template:((i=o.template)==null?void 0:i.trim())||""}}catch(o){console.info("No custom monthly note settings found!",o)}}function ue(){var e,t,n,i;let s=window.app.plugins;try{let o=oe("quarterly")&&((t=(e=s.getPlugin("periodic-notes"))==null?void 0:e.settings)==null?void 0:t.quarterly)||{};return{format:o.format||Vt,folder:((n=o.folder)==null?void 0:n.trim())||"",template:((i=o.template)==null?void 0:i.trim())||""}}catch(o){console.info("No custom quarterly note settings found!",o)}}function ce(){var e,t,n,i;let s=window.app.plugins;try{let o=oe("yearly")&&((t=(e=s.getPlugin("periodic-notes"))==null?void 0:e.settings)==null?void 0:t.yearly)||{};return{format:o.format||Ut,folder:((n=o.folder)==null?void 0:n.trim())||"",template:((i=o.template)==null?void 0:i.trim())||""}}catch(o){console.info("No custom yearly note settings found!",o)}}function _t(...s){let e=[];for(let n=0,i=s.length;n{let C=n(),N=s.clone().set({hour:C.get("hour"),minute:C.get("minute"),second:C.get("second")});return b&&N.add(parseInt(w,10),p),T?N.format(T.substring(1).trim()):N.format(o)}).replace(/{{\s*yesterday\s*}}/gi,s.clone().subtract(1,"day").format(o)).replace(/{{\s*tomorrow\s*}}/gi,s.clone().add(1,"d").format(o)));return e.foldManager.save(l,u),l}catch(l){console.error(`Failed to create file: '${m}'`,l),new I.Notice("Unable to create new file.")}}function qn(s,e){var t;return(t=e[$(s,"day")])!=null?t:null}function Yn(){let{vault:s}=window.app,{folder:e}=re(),t=s.getAbstractFileByPath(I.normalizePath(e));if(!t)throw new Xe("Failed to find daily notes folder");let n={};return I.Vault.recurseChildren(t,i=>{if(i instanceof I.TFile){let o=X(i,"day");if(o){let a=$(o,"day");n[a]=i}}}),n}var We=class extends Error{};function Xn(){let{moment:s}=window,e=s.localeData()._week.dow,t=["sunday","monday","tuesday","wednesday","thursday","friday","saturday"];for(;e;)t.push(t.shift()),e--;return t}function Wn(s){return Xn().indexOf(s.toLowerCase())}async function zt(s){let{vault:e}=window.app,{template:t,format:n,folder:i}=ae(),[o,a]=await Y(t),c=s.format(n),u=await ge(i,c);try{let g=await e.create(u,o.replace(/{{\s*(date|time)\s*(([+-]\d+)([yqmwdhs]))?\s*(:.+?)?}}/gi,(m,l,r,d,b,w)=>{let p=window.moment(),T=s.clone().set({hour:p.get("hour"),minute:p.get("minute"),second:p.get("second")});return r&&T.add(parseInt(d,10),b),w?T.format(w.substring(1).trim()):T.format(n)}).replace(/{{\s*title\s*}}/gi,c).replace(/{{\s*time\s*}}/gi,window.moment().format("HH:mm")).replace(/{{\s*(sunday|monday|tuesday|wednesday|thursday|friday|saturday)\s*:(.*?)}}/gi,(m,l,r)=>{let d=Wn(l);return s.weekday(d).format(r.trim())}));return window.app.foldManager.save(g,a),g}catch(g){console.error(`Failed to create file: '${u}'`,g),new I.Notice("Unable to create new file.")}}function Jn(s,e){var t;return(t=e[$(s,"week")])!=null?t:null}function Kn(){let s={};if(!Yt())return s;let{vault:e}=window.app,{folder:t}=ae(),n=e.getAbstractFileByPath(I.normalizePath(t));if(!n)throw new We("Failed to find weekly notes folder");return I.Vault.recurseChildren(n,i=>{if(i instanceof I.TFile){let o=X(i,"week");if(o){let a=$(o,"week");s[a]=i}}}),s}var Je=class extends Error{};async function qt(s){let{vault:e}=window.app,{template:t,format:n,folder:i}=le(),[o,a]=await Y(t),c=s.format(n),u=await ge(i,c);try{let g=await e.create(u,o.replace(/{{\s*(date|time)\s*(([+-]\d+)([yqmwdhs]))?\s*(:.+?)?}}/gi,(m,l,r,d,b,w)=>{let p=window.moment(),T=s.clone().set({hour:p.get("hour"),minute:p.get("minute"),second:p.get("second")});return r&&T.add(parseInt(d,10),b),w?T.format(w.substring(1).trim()):T.format(n)}).replace(/{{\s*date\s*}}/gi,c).replace(/{{\s*time\s*}}/gi,window.moment().format("HH:mm")).replace(/{{\s*title\s*}}/gi,c));return window.app.foldManager.save(g,a),g}catch(g){console.error(`Failed to create file: '${u}'`,g),new I.Notice("Unable to create new file.")}}function Qn(s,e){var t;return(t=e[$(s,"month")])!=null?t:null}function Zn(){let s={};if(!Xt())return s;let{vault:e}=window.app,{folder:t}=le(),n=e.getAbstractFileByPath(I.normalizePath(t));if(!n)throw new Je("Failed to find monthly notes folder");return I.Vault.recurseChildren(n,i=>{if(i instanceof I.TFile){let o=X(i,"month");if(o){let a=$(o,"month");s[a]=i}}}),s}var Ke=class extends Error{};async function es(s){let{vault:e}=window.app,{template:t,format:n,folder:i}=ue(),[o,a]=await Y(t),c=s.format(n),u=await ge(i,c);try{let g=await e.create(u,o.replace(/{{\s*(date|time)\s*(([+-]\d+)([yqmwdhs]))?\s*(:.+?)?}}/gi,(m,l,r,d,b,w)=>{let p=window.moment(),T=s.clone().set({hour:p.get("hour"),minute:p.get("minute"),second:p.get("second")});return r&&T.add(parseInt(d,10),b),w?T.format(w.substring(1).trim()):T.format(n)}).replace(/{{\s*date\s*}}/gi,c).replace(/{{\s*time\s*}}/gi,window.moment().format("HH:mm")).replace(/{{\s*title\s*}}/gi,c));return window.app.foldManager.save(g,a),g}catch(g){console.error(`Failed to create file: '${u}'`,g),new I.Notice("Unable to create new file.")}}function ts(s,e){var t;return(t=e[$(s,"quarter")])!=null?t:null}function ns(){let s={};if(!Wt())return s;let{vault:e}=window.app,{folder:t}=ue(),n=e.getAbstractFileByPath(I.normalizePath(t));if(!n)throw new Ke("Failed to find quarterly notes folder");return I.Vault.recurseChildren(n,i=>{if(i instanceof I.TFile){let o=X(i,"quarter");if(o){let a=$(o,"quarter");s[a]=i}}}),s}var Qe=class extends Error{};async function ss(s){let{vault:e}=window.app,{template:t,format:n,folder:i}=ce(),[o,a]=await Y(t),c=s.format(n),u=await ge(i,c);try{let g=await e.create(u,o.replace(/{{\s*(date|time)\s*(([+-]\d+)([yqmwdhs]))?\s*(:.+?)?}}/gi,(m,l,r,d,b,w)=>{let p=window.moment(),T=s.clone().set({hour:p.get("hour"),minute:p.get("minute"),second:p.get("second")});return r&&T.add(parseInt(d,10),b),w?T.format(w.substring(1).trim()):T.format(n)}).replace(/{{\s*date\s*}}/gi,c).replace(/{{\s*time\s*}}/gi,window.moment().format("HH:mm")).replace(/{{\s*title\s*}}/gi,c));return window.app.foldManager.save(g,a),g}catch(g){console.error(`Failed to create file: '${u}'`,g),new I.Notice("Unable to create new file.")}}function is(s,e){var t;return(t=e[$(s,"year")])!=null?t:null}function os(){let s={};if(!Jt())return s;let{vault:e}=window.app,{folder:t}=ce(),n=e.getAbstractFileByPath(I.normalizePath(t));if(!n)throw new Qe("Failed to find yearly notes folder");return I.Vault.recurseChildren(n,i=>{if(i instanceof I.TFile){let o=X(i,"year");if(o){let a=$(o,"year");s[a]=i}}}),s}function rs(){var n,i;let{app:s}=window,e=s.internalPlugins.plugins["daily-notes"];if(e&&e.enabled)return!0;let t=s.plugins.getPlugin("periodic-notes");return t&&((i=(n=t.settings)==null?void 0:n.daily)==null?void 0:i.enabled)}function Yt(){var t,n;let{app:s}=window;if(s.plugins.getPlugin("calendar"))return!0;let e=s.plugins.getPlugin("periodic-notes");return e&&((n=(t=e.settings)==null?void 0:t.weekly)==null?void 0:n.enabled)}function Xt(){var t,n;let{app:s}=window,e=s.plugins.getPlugin("periodic-notes");return e&&((n=(t=e.settings)==null?void 0:t.monthly)==null?void 0:n.enabled)}function Wt(){var t,n;let{app:s}=window,e=s.plugins.getPlugin("periodic-notes");return e&&((n=(t=e.settings)==null?void 0:t.quarterly)==null?void 0:n.enabled)}function Jt(){var t,n;let{app:s}=window,e=s.plugins.getPlugin("periodic-notes");return e&&((n=(t=e.settings)==null?void 0:t.yearly)==null?void 0:n.enabled)}function as(s){let e={day:re,week:ae,month:le,quarter:ue,year:ce}[s];return e()}function ls(s,e){return{day:jt,month:qt,week:zt}[s](e)}v.DEFAULT_DAILY_NOTE_FORMAT=qe;v.DEFAULT_MONTHLY_NOTE_FORMAT=Mt;v.DEFAULT_QUARTERLY_NOTE_FORMAT=Vt;v.DEFAULT_WEEKLY_NOTE_FORMAT=Ye;v.DEFAULT_YEARLY_NOTE_FORMAT=Ut;v.appHasDailyNotesPluginLoaded=rs;v.appHasMonthlyNotesPluginLoaded=Xt;v.appHasQuarterlyNotesPluginLoaded=Wt;v.appHasWeeklyNotesPluginLoaded=Yt;v.appHasYearlyNotesPluginLoaded=Jt;v.createDailyNote=jt;v.createMonthlyNote=qt;v.createPeriodicNote=ls;v.createQuarterlyNote=es;v.createWeeklyNote=zt;v.createYearlyNote=ss;v.getAllDailyNotes=Yn;v.getAllMonthlyNotes=Zn;v.getAllQuarterlyNotes=ns;v.getAllWeeklyNotes=Kn;v.getAllYearlyNotes=os;v.getDailyNote=qn;v.getDailyNoteSettings=re;v.getDateFromFile=X;v.getDateFromPath=zn;v.getDateUID=$;v.getMonthlyNote=Qn;v.getMonthlyNoteSettings=le;v.getPeriodicNoteSettings=as;v.getQuarterlyNote=ts;v.getQuarterlyNoteSettings=ue;v.getTemplateInfo=Y;v.getWeeklyNote=Jn;v.getWeeklyNoteSettings=ae;v.getYearlyNote=is;v.getYearlyNoteSettings=ce});var us={};an(us,{default:()=>ke});module.exports=un(us);var en=require("obsidian");var k=require("obsidian");var J=require("obsidian"),Se=class extends J.Modal{constructor(t,n){super(t.app);this.resolve=n;this.isConfirmed=!1;let i={app:t.app,cancelButtonText:"Cancel",cssClass:"",message:t.message,okButtonText:"OK",title:""};this.options={...i,...t},this.containerEl.addClass("confirm-modal")}onClose(){super.onClose(),this.resolve(this.isConfirmed)}onOpen(){super.onOpen(),this.titleEl.setText(this.options.title),this.contentEl.createEl("p",{text:this.options.message});let t=new J.ButtonComponent(this.contentEl);t.setClass("ok-button"),t.setButtonText(this.options.okButtonText),t.setCta(),t.onClick(()=>{this.isConfirmed=!0,this.close()});let n=new J.ButtonComponent(this.contentEl);n.setButtonText(this.options.cancelButtonText),n.onClick(this.close.bind(this))}};async function De(s){return await new Promise(e=>{new Se(s,e).open()})}var D=class extends Error{constructor(t,n,i,o){let a=Math.ceil((i-Math.floor(Date.now()/1e3))/60);super(`GitHub API rate limit exceeded. Reset in ${a} minutes.`);this.limit=t;this.remaining=n;this.reset=i;this.requestUrl=o}getMinutesToReset(){return Math.ceil((this.reset-Math.floor(Date.now()/1e3))/60)}},O=class extends Error{constructor(e){var n,i;super(`GitHub API error ${e}: ${e.message}`),this.message=e.message;let t=e;this.status=(n=t.status)!=null?n:400,this.headers=(i=t.headers)!=null?i:{},this.name="GitHubResponseError"}};var V=require("obsidian");var On=Ue(),bt=_e();var ye=s=>{let e=s.replace(/https?:\/\/github\.com\//i,"");return e.endsWith("/")&&(e=e.slice(0,-1)),e.toLowerCase().endsWith(".git")&&(e=e.slice(0,-4)),e},wt=["ghp_","github_pat_"],Fn=/^(gh[ps]_[a-zA-Z0-9]{36}|github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59})$/,Tt=async(s,e)=>{var o,a,c,u,g,m,l,r,d,b,w;let t=["repo","public_repo","metadata=read"],n=wt.some(p=>s.toLowerCase().startsWith(p.toLowerCase())),i=Fn.test(s);if(!n||!i)return{validToken:!1,currentScopes:[],acceptedScopes:[],acceptedPermissions:[],expirationDate:null,rateLimit:{limit:0,remaining:0,reset:0,resource:"",used:0},error:{type:n?"invalid_format":"invalid_prefix",message:"Invalid token format",details:{validPrefixes:wt}}};try{let p=Date.now()%1e3,T=e||`user${p}/repo${p%100}`;if(await ve({url:`https://api.github.com/repos/${T}`,headers:{Authorization:`Token ${s}`,Accept:"application/vnd.github.v3+json"}}),e)return{validToken:!0,currentScopes:[],acceptedScopes:[],acceptedPermissions:[],expirationDate:null,rateLimit:{limit:0,remaining:0,reset:0,resource:"",used:0},error:{type:"none",message:"No error",details:{}}};throw new Error("Expected request to fail")}catch(p){if(!(p instanceof O))throw p;let T=p.headers;if(!T)throw new Error("No headers in GitHub response");let C=T["github-authentication-token-expiration"],N=C?new Date(C):null,R=N&&!Number.isNaN(N.getTime())?N.toISOString():null,P={validToken:!1,currentScopes:(a=(o=T["x-oauth-scopes"])==null?void 0:o.split(", "))!=null?a:[],acceptedScopes:(u=(c=T["x-accepted-oauth-scopes"])==null?void 0:c.split(", "))!=null?u:[],acceptedPermissions:(m=(g=T["x-accepted-github-permissions"])==null?void 0:g.split(", "))!=null?m:[],expirationDate:R,rateLimit:{limit:Number.parseInt((l=T["x-ratelimit-limit"])!=null?l:"0",10),remaining:Number.parseInt((r=T["x-ratelimit-remaining"])!=null?r:"0",10),reset:Number.parseInt((d=T["x-ratelimit-reset"])!=null?d:"0",10),resource:(b=T["x-ratelimit-resource"])!=null?b:"",used:Number.parseInt((w=T["x-ratelimit-used"])!=null?w:"0",10)},error:{type:"none",message:"No error",details:{}}};return P.expirationDate&&new Date(P.expirationDate)t.includes(M))||P.acceptedPermissions.some(M=>t.includes(M))?(P.validToken=p.status===404,P):(P.error={type:"insufficient_scope",message:"Token lacks required scopes. Check documentation for requirements.",details:{currentScopes:[...P.acceptedScopes,...P.acceptedPermissions]}},P)}},He=async(s,e=!0,t="")=>{let n=`https://api.github.com/repos/${s}`;try{return(await ve({url:n,headers:t?{Authorization:`Token ${t}`}:{}})).json.private}catch(i){if(i instanceof D)throw i;return e&&console.log("error in isPrivateRepo",n,i),!1}},Et=async(s,e=!0,t="")=>{let n=`https://api.github.com/repos/${s}/releases`;try{return(await ve({url:`${n}?per_page=100`,headers:t?{Authorization:`Token ${t}`}:{}})).json.map(a=>({version:a.tag_name,prerelease:a.prerelease}))}catch(i){if(i instanceof D||i instanceof O)throw i;return e&&console.log("Error in fetchReleaseVersions",n,i),null}},K=async(s,e,t=!0,n=!1,i="")=>{try{let o=s.assets.find(g=>g.name===e);if(!o)return null;let a={Accept:"application/octet-stream"};n&&i&&(a.Authorization=`Token ${i}`);let c=n?o.url:o.browser_download_url,u=await(0,V.requestUrl)({url:c,headers:a});return u.status!==200?null:u.text}catch(o){if(o instanceof D)throw o;return t&&console.log("error in grabReleaseFileFromRepository",s,o),null}},yt=async(s=!0)=>{let e="https://raw.githubusercontent.com/obsidianmd/obsidian-releases/HEAD/community-plugins.json";try{let t=await(0,V.requestUrl)({url:e});return t.status===404?null:t.json}catch(t){return s&&console.log("error in grabCommmunityPluginList",t),null}},vt=async(s=!0)=>{let e="https://raw.githubusercontent.com/obsidianmd/obsidian-releases/HEAD/community-css-themes.json";try{let t=await(0,V.requestUrl)({url:e});return t.status===404?null:t.json}catch(t){return s&&console.log("error in grabCommmunityThemesList",t),null}},G=async(s,e=!1,t=!1)=>{let n=`https://raw.githubusercontent.com/${s}/HEAD/theme${e?"-beta":""}.css`;try{let i=await(0,V.requestUrl)({url:n});return i.status===404?null:i.text}catch(i){return t&&console.log("error in grabCommmunityThemeCssFile",i),null}},Pt=async(s,e=!0)=>{let t=`https://raw.githubusercontent.com/${s}/HEAD/manifest.json`;try{let n=await(0,V.requestUrl)({url:t});return n.status===404?null:n.text}catch(n){return e&&console.log("error in grabCommmunityThemeManifestFile",n),null}},Bn=s=>{let e=0;for(let t=0;tBn(s).toString(),Z=async(s,e,t)=>{let n=await G(s,e,t);return n?Q(n):"0"},Mn=async(s,e,t=!0)=>{let n=`https://api.github.com/repos/${s}/commits?path=${e}&page=1&per_page=1`;try{let i=await(0,V.requestUrl)({url:n});return i.status===404?null:i.json}catch(i){return t&&console.log("error in grabLastCommitInfoForAFile",i),null}},Rt=async(s,e)=>{var n;let t=await Mn(s,e);return t&&t.length>0&&((n=t[0].commit.committer)!=null&&n.date)?t[0].commit.committer.date:""},Ge=async(s,e,t=!1,n=!1,i=!1,o)=>{var a;try{let c=e&&e!=="latest"?`https://api.github.com/repos/${s}/releases/tags/${e}`:`https://api.github.com/repos/${s}/releases`,u={Accept:"application/vnd.github.v3+json"};(i&&o||o)&&(u.Authorization=`Token ${o}`);let g=await ve({url:c,headers:u});if(g.status===404)return null;let m=e&&e!=="latest"?[g.json]:g.json;return n&&console.log(`grabReleaseFromRepository for ${s}:`,m),(a=m.sort((l,r)=>{try{let d=bt(l.tag_name,{includePrerelease:!0,loose:!0}),b=bt(r.tag_name,{includePrerelease:!0,loose:!0});return On(b,d)}catch(d){let b=new Date(l.published_at).getTime(),w=new Date(r.published_at).getTime();return bw?-1:0}}).filter(l=>t||!l.prerelease)[0])!=null?a:null}catch(c){throw n&&console.log(`Error in grabReleaseFromRepository for ${s}:`,c),c}},ve=async(s,e)=>{let t=0,n=0,i=0;s.headers={...s.headers,"User-Agent":"Obsidian/BRAT-Plugin"};try{return await(0,V.requestUrl)(s)}catch(o){let a=new O(o),c=a.headers;if(c&&(t=Number.parseInt(c["x-ratelimit-limit"],10),n=Number.parseInt(c["x-ratelimit-remaining"],10),i=Number.parseInt(c["x-ratelimit-reset"],10)),a.status===403&&n===0){let u=new D(t,n,i,s.url);throw e&&console.error(`BRAT +GitHub API rate limit exceeded:`,` +Request: ${u.requestUrl}`,` +Rate limits - Remaining: ${u.remaining}`,` +Reset in: ${u.getMinutesToReset()} minutes`),u}throw e&&console.log("GitHub request failed:",o),a}};var je={pluginList:[],pluginSubListFrozenVersion:[],themesList:[],updateAtStartup:!0,updateThemesAtStartup:!0,enableAfterInstall:!0,loggingEnabled:!1,loggingPath:"BRAT-log",loggingVerboseEnabled:!1,debuggingMode:!1,notificationsEnabled:!0,globalTokenName:"",personalAccessToken:"",selectLatestPluginVersionByDefault:!1,allowIncompatiblePlugins:!1};function It(s,e,t="latest",n=!1,i=""){let o=!1;s.settings.pluginList.contains(e)||(s.settings.pluginList.unshift(e),o=!0);let a=s.settings.pluginSubListFrozenVersion.find(c=>c.repo===e);a?(Object.assign(a,{repo:e,version:t,token:void 0,tokenName:i||a.tokenName,isIncompatible:n||void 0}),o=!0):(s.settings.pluginSubListFrozenVersion.unshift({repo:e,version:t,token:void 0,tokenName:i||void 0,isIncompatible:n||void 0}),o=!0),o&&s.saveSettings()}function Pe(s,e){return s.settings.pluginList.contains(e)}function Nt(s,e,t){let n={repo:e,lastUpdate:Q(t)};s.settings.themesList.unshift(n),s.saveSettings()}function At(s,e){return!!s.settings.themesList.find(n=>n.repo===e)}function ze(s,e,t){let n=s.settings.pluginSubListFrozenVersion.find(i=>i.repo===e);n&&(n.tokenName=t||void 0,s.saveSettings())}function Ct(s,e,t){for(let n of s.settings.themesList)n.repo===e&&(n.lastUpdate=t,s.saveSettings())}var L=require("obsidian");var j=class{constructor(e){this.statusEl=e}async validateToken(e,t){var n,i,o,a,c,u,g,m,l;if(!e)return(n=this.statusEl)==null||n.setText("No token provided"),(i=this.statusEl)==null||i.addClass("invalid"),(o=this.statusEl)==null||o.removeClass("valid"),!1;try{let r=await Tt(e,t);return(a=this.statusEl)==null||a.removeClass("invalid","valid"),(c=this.statusEl)==null||c.empty(),r.validToken?((u=this.statusEl)==null||u.addClass("valid"),this.showValidTokenInfo(r),!0):((g=this.statusEl)==null||g.addClass("invalid"),this.showErrorMessage(r.error),!1)}catch(r){return console.error("Token validation error:",r),(m=this.statusEl)==null||m.setText("Failed to validate token"),(l=this.statusEl)==null||l.addClass("invalid"),!1}}showValidTokenInfo(e){var n,i;let t=(n=this.statusEl)==null?void 0:n.createDiv({cls:"brat-token-details"});if(t&&(t.createDiv({text:"\u2713 Valid token",cls:"brat-token-status valid"}),(i=e.currentScopes)!=null&&i.length&&t.createDiv({text:`Scopes: ${e.currentScopes.join(", ")}`,cls:"brat-token-scopes"}),e.rateLimit&&t.createDiv({text:`Rate Limit: ${e.rateLimit.remaining}/${e.rateLimit.limit}`,cls:"brat-token-rate"}),e.expirationDate)){let o=new Date(e.expirationDate),a=Math.ceil((o.getTime()-Date.now())/(1e3*60*60*24));a<7&&t.createDiv({text:`\u26A0\uFE0F Token expires in ${a} days`,cls:"brat-token-warning"})}}showErrorMessage(e){var n,i,o;let t=(n=this.statusEl)==null?void 0:n.createDiv({cls:"brat-token-error"});if(t&&(t.createDiv({text:e.message}),e.details))switch(e.type){case"invalid_prefix":t.createDiv({text:`Valid prefixes: ${(i=e.details.validPrefixes)==null?void 0:i.join(", ")}`});break;case"insufficient_scope":t.createDiv({text:`Required scopes: ${(o=e.details.requiredScopes)==null?void 0:o.join(", ")}`});break}}};function ee(s,e){let t=new DocumentFragment,n=document.createElement("a");if(n.textContent=s,n.href=`https://github.com/${s}`,n.target="_blank",t.appendChild(n),e){let i=document.createTextNode(e);t.appendChild(i)}return t}function Lt({prependText:s,url:e,text:t,appendText:n}){let i=new DocumentFragment,o=document.createElement("a");if(o.textContent=t,o.href=e,s){let a=document.createTextNode(s);i.appendChild(a)}if(i.appendChild(o),n){let a=document.createTextNode(n);i.appendChild(a)}return i}var Re=require("obsidian");function E(s,e,t=10,n){if(!s.settings.notificationsEnabled)return;let i=n?Re.Platform.isDesktop?"(click=dismiss, right-click=Info)":"(click=dismiss)":"",o=new Re.Notice(`BRAT +${e} +${i}`,t*1e3);n&&(o.noticeEl.oncontextmenu=()=>{n()})}var z=(s,e=!0)=>{let t=s.createEl("div");t.style.float="right",e?(t.style.padding="15px",t.style.paddingLeft="15px",t.style.paddingRight="15px",t.style.marginLeft="15px"):(t.style.padding="10px",t.style.paddingLeft="15px",t.style.paddingRight="15px");let n=t.createDiv("coffee");n.addClass("ex-twitter-span"),n.style.paddingLeft="10px";let i=n.createDiv();i.innerText="Learn more about my work at:",n.appendChild(i);let o=n.createEl("a",{href:"https://tfthacker.com"});return o.innerText="https://tfthacker.com",t};var kt=require("obsidian"),Ie=class extends kt.SuggestModal{constructor(e,t,n,i,o){super(e),this.versions=n,this.selected=i,this.onChoose=o,this.setTitle("Select a version"),this.setPlaceholder(`Type to search for a version for ${t}`),this.setInstructions([{command:"\u2191\u2193",purpose:"Navigate versions"},{command:"\u21B5",purpose:"Select version"},{command:"esc",purpose:"Dismiss modal"}])}getSuggestions(e){let t=e.toLowerCase();return this.versions.filter(n=>n.version.toLowerCase().contains(t))}renderSuggestion(e,t){t.createEl("div",{text:`${e.version} ${e.prerelease?"(Prerelease)":""}`})}onChooseSuggestion(e){this.onChoose(e.version)}onNoSuggestion(){this.onChoose(this.selected?this.selected:""),this.close()}};var U=class extends L.Modal{constructor(t,n,i=!1,o=!1,a="",c="",u=""){super(t.app);this.versionSetting=null;this.repositoryAddressEl=null;this.tokenInputEl=null;this.validateButton=null;this.validator=null;this.addPluginButton=null;this.cancelButton=null;this.plugin=t,this.betaPlugins=n,this.address=a,this.version=c,this.secretName=u,this.openSettingsTabAfterwards=i,this.updateVersion=o,this.enableAfterInstall=t.settings.enableAfterInstall}async submitForm(){var o,a,c,u,g,m,l,r;if(this.address==="")return;let t=ye(this.address);if(this.plugin.settings.pluginSubListFrozenVersion.find(d=>d.repo===t)){await this.betaPlugins.addPlugin(t,!1,!1,!1,this.version,!0,this.enableAfterInstall,this.secretName)&&this.close(),(o=this.cancelButton)==null||o.setDisabled(!1),(a=this.addPluginButton)==null||a.setDisabled(!1),(c=this.addPluginButton)==null||c.setButtonText("Add Plugin"),(u=this.versionSetting)==null||u.setDisabled(!1);return}if(!this.version&&Pe(this.plugin,t)){E(this.plugin,"This plugin is already in the list for beta testing",10);return}await this.betaPlugins.addPlugin(t,!1,!1,!1,this.version,!1,this.enableAfterInstall,this.secretName)&&this.close(),(g=this.cancelButton)==null||g.setDisabled(!1),(m=this.addPluginButton)==null||m.setDisabled(!1),(l=this.addPluginButton)==null||l.setButtonText("Add Plugin"),(r=this.versionSetting)==null||r.setDisabled(!1)}updateVersionDropdown(t,n,i=""){let o;t.clear(),n.length>0&&!i&&this.plugin.settings.selectLatestPluginVersionByDefault?(o="latest",this.version="latest"):o=i,n.length<20||L.Platform.isMobile?t.addDropdown(c=>{c.addOption("","Select a version"),c.addOption("latest","Latest version");for(let u of n)c.addOption(u.version,`${u.version} ${u.prerelease?"(Prerelease)":""}`);c.onChange(u=>{var g;this.version=u,(g=this.addPluginButton)==null||g.setDisabled(this.version==="")}),c.setValue(o),c.selectEl.addClass("brat-version-selector"),c.selectEl.style.width="100%"}):t.addButton(c=>{c.setButtonText(o==="latest"?"Latest version":o||"Select a version...").setClass("brat-version-selector").setClass("button").onClick(()=>{let g=[{version:"latest",prerelease:!1},...n];new Ie(this.app,this.address,g,o,l=>{var r;this.version=l,c.setButtonText(l==="latest"?"Latest version":l||"Select a version..."),(r=this.addPluginButton)==null||r.setDisabled(this.version==="")}).open()})})}onOpen(){let t=this.contentEl.createEl("h4");this.address?(t.appendText("Change plugin version: "),t.appendChild(ee(this.address))):t.setText("Github repository for beta plugin:"),this.contentEl.createEl("form",{},n=>{var g;n.addClass("brat-modal"),(!this.address||!this.updateVersion)&&new L.Setting(n).setClass("repository-setting").then(l=>{l.addText(r=>{this.repositoryAddressEl=r,r.setPlaceholder("Repository (example: https://github.com/GitHubUserName/repository-name)"),r.setValue(this.address),r.onChange(d=>{var b,w;this.address=ye(d.trim()),this.version!==""&&(!this.address||!this.isGitHubRepositoryMatch(this.address))&&this.versionSetting&&(this.updateVersionDropdown(this.versionSetting,[]),this.versionSetting.settingEl.classList.add("disabled-setting"),this.versionSetting.setDisabled(!0),r.inputEl.classList.remove("valid-repository"),r.inputEl.classList.remove("invalid-repository")),this.version||(this.isGitHubRepositoryMatch(this.address)?(b=this.addPluginButton)==null||b.setDisabled(!1):(w=this.addPluginButton)==null||w.setDisabled(!0))}),r.inputEl.addEventListener("keydown",async d=>{var b,w,p;d.key==="Enter"&&(this.address&&(this.updateVersion&&this.version!==""||!this.updateVersion)&&(d.preventDefault(),(b=this.addPluginButton)==null||b.setDisabled(!0),(w=this.cancelButton)==null||w.setDisabled(!0),(p=this.versionSetting)==null||p.setDisabled(!0),this.submitForm()),await this.updateRepositoryVersionInfo(this.version,i))}),r.inputEl.addEventListener("blur",async()=>{await this.updateRepositoryVersionInfo(this.version,i)}),l.setDesc("Repository"),r.inputEl.style.width="100%"})});let i=n.createDiv("validation-status");this.address||i.setText("Enter a GitHub repository address to validate it."),this.versionSetting=new L.Setting(n).setClass("version-setting").setClass("disabled-setting"),this.updateVersionDropdown(this.versionSetting,[],this.version),this.versionSetting.setDisabled(!0);let o=n.createDiv("token-setting");if(new L.Setting(o).setName("GitHub Token").setDesc("Select a secret as token for this repository (optional)").addComponent(m=>new L.SecretComponent(this.plugin.app,m).setValue(this.secretName).onChange(async l=>{var d,b,w,p,T;if(this.secretName=(l==null?void 0:l.trim())||"",!this.secretName){this.address&&Pe(this.plugin,this.address)&&(ze(this.plugin,this.address,""),E(this.plugin,`Token setting cleared for ${this.address}`,3)),this.updateRepositoryVersionInfo(this.version,i);return}let r=this.secretName?this.plugin.app.secretStorage.getSecret(this.secretName):null;r&&(this.validToken=await((d=this.validator)==null?void 0:d.validateToken(r,this.address)),this.validToken?((p=this.validateButton)==null||p.setButtonText("Valid"),(T=this.validateButton)==null||T.setDisabled(!0),this.address&&(await this.updateRepositoryVersionInfo(this.version,i),Pe(this.plugin,this.address)&&(ze(this.plugin,this.address,this.secretName),E(this.plugin,`Token setting updated for ${this.address}`,3)))):((b=this.validateButton)==null||b.setButtonText("Invalid"),(w=this.validateButton)==null||w.setDisabled(!1)))})),this.validator=new j,this.secretName){let m=this.plugin.app.secretStorage.getSecret(this.secretName);m&&((g=this.validator)==null||g.validateToken(m,this.address).then(l=>{var r,d;this.validToken=l,this.validToken&&((r=this.validateButton)==null||r.setButtonText("Valid"),(d=this.validateButton)==null||d.setDisabled(!0))}))}n.createDiv("modal-button-container",m=>{var l;m.createEl("label",{cls:"mod-checkbox"},r=>{let d=r.createEl("input",{attr:{tabindex:-1},type:"checkbox"});d.checked=this.enableAfterInstall,d.addEventListener("click",()=>{this.enableAfterInstall=d.checked}),r.appendText("Enable after installing the plugin")}),this.cancelButton=new L.ButtonComponent(m).setButtonText("Never mind").setClass("mod-cancel").onClick(()=>{this.close()}),this.addPluginButton=new L.ButtonComponent(m).setButtonText(this.updateVersion&&this.address?"Change version":"Add plugin").setCta().onClick(()=>{var r,d,b,w;this.address!==""&&(this.updateVersion&&this.version!==""||!this.updateVersion)&&((r=this.addPluginButton)==null||r.setDisabled(!0),(d=this.addPluginButton)==null||d.setButtonText("Installing \u2026"),(b=this.cancelButton)==null||b.setDisabled(!0),(w=this.versionSetting)==null||w.setDisabled(!0),this.submitForm())}),(this.updateVersion||this.address==="")&&((l=this.addPluginButton)==null||l.setDisabled(!0))});let a=n.createDiv();a.style.borderTop="1px solid #ccc",a.style.marginTop="30px";let c=a.createSpan();c.createEl("a",{href:"https://bit.ly/o42-twitter",text:"TFTHacker"}),c.appendText(" and "),c.createEl("a",{href:"https://github.com/johannrichard",text:"johannrichard"}),c.style.fontStyle="italic",a.appendChild(c),z(a,!1);let u=n.querySelectorAll("button");for(let m of Array.from(u))m.setAttribute("type","button");n.addEventListener("submit",m=>{var l;m.preventDefault(),this.address!==""&&(this.updateVersion&&this.version!==""||!this.updateVersion)&&((l=this.addPluginButton)==null||l.setDisabled(!0),this.submitForm())})}),this.address&&window.setTimeout(async()=>{await this.updateRepositoryVersionInfo(this.version)},100)}async updateRepositoryVersionInfo(t="",n){var a,c,u,g,m,l;let i=this.repositoryAddressEl;if(this.plugin.settings.debuggingMode&&console.log(`[BRAT] Updating version dropdown for ${this.address} with selected version ${t}`),!this.address){n==null||n.setText("Repository address is required."),n==null||n.addClass("validation-status-error");return}n==null||n.setText("Validating repository address..."),n==null||n.removeClass("validation-status-error"),this.versionSetting&&this.updateVersion&&this.updateVersionDropdown(this.versionSetting,[]);let o=ye(this.address);try{let r="";if(this.secretName){let b=this.plugin.app.secretStorage.getSecret(this.secretName);b&&(r=b)}else if(this.plugin.settings.globalTokenName){let b=this.plugin.app.secretStorage.getSecret(this.plugin.settings.globalTokenName);b&&(r=b)}let d=await Et(o,this.plugin.settings.debuggingMode,r);d&&d.length>0?(i==null||i.inputEl.classList.remove("invalid-repository"),i==null||i.inputEl.classList.add("valid-repository"),n==null||n.setText(""),this.versionSetting&&(this.versionSetting.settingEl.classList.remove("disabled-setting"),this.versionSetting.setDisabled(!1),this.updateVersionDropdown(this.versionSetting,d,t))):(i==null||i.inputEl.classList.remove("valid-repository"),i==null||i.inputEl.classList.add("invalid-repository"),n==null||n.setText("Error: No releases found in this repository."),n==null||n.addClass("validation-status-error"),(a=this.versionSetting)==null||a.settingEl.classList.add("disabled-setting"),(c=this.versionSetting)==null||c.setDisabled(!0),(u=this.addPluginButton)==null||u.setDisabled(!0))}catch(r){if(r instanceof D&&(i==null||i.inputEl.classList.remove("valid-repository"),i==null||i.inputEl.classList.add("validation-error"),n==null||n.setText(`GitHub API rate limit exceeded. Try again in ${r.getMinutesToReset()} minutes.`),this.versionSetting&&(this.versionSetting.settingEl.classList.add("disabled-setting"),this.versionSetting.setDisabled(!0),(g=this.addPluginButton)==null||g.setDisabled(!0)),E(this.plugin,`${r.message} Consider adding a personal access token in BRAT settings for higher limits. See documentation for details.`,20,()=>{window.open("https://github.com/TfTHacker/obsidian42-brat/blob/main/BRAT-DEVELOPER-GUIDE.md#github-api-rate-limits")})),r instanceof O){let d=r;switch(d.status){case 404:n==null||n.setText("Repository not found. Check the address or provide a valid token for access to a private repository.");break;case 403:n==null||n.setText("Access denied. Check your personal access token.");break;default:n==null||n.setText(`Error: ${d.message}`);break}n==null||n.addClass("validation-status-error"),(m=this.versionSetting)==null||m.setDisabled(!0),(l=this.addPluginButton)==null||l.setDisabled(!0),E(this.plugin,`${d.message} `,20)}}}onClose(){this.openSettingsTabAfterwards&&(this.plugin.app.setting.open(),this.plugin.app.setting.openTabById(this.plugin.APP_ID))}isGitHubRepositoryMatch(t){let n=t.trim().replace(/\.git$/,"").toLowerCase();return/^(?:https?:\/\/github\.com\/)?([a-zA-Z0-9._-]+)\/([a-zA-Z0-9._-]+)$/i.test(n)}};var St=require("obsidian");async function Ne(){try{let s=await(0,St.requestUrl)(`https://obsidian.md/?${Math.random()}`);return s.status>=200&&s.status<300}catch(s){return!1}}var Dt=Ue(),Ae=_e(),te=class{constructor(e){this.plugin=e}displayAddNewPluginModal(e=!1,t=!1,n="",i="",o=""){new U(this.plugin,this,e,t,n,i,o).open()}async validateRepository(e,t=!1,n=!1,i="",o=""){let c=o;try{let u=await He(e,this.plugin.settings.debuggingMode,c),g=await Ge(e,i,t,this.plugin.settings.debuggingMode,u,c);if(!g)return n&&(E(this.plugin,`${e} +This does not seem to be an obsidian plugin with valid releases, as there are no releases available.`,15),console.error("BRAT: validateRepository",e,t,n)),null;let m=await K(g,"manifest.json",this.plugin.settings.debuggingMode,u,c);if(!m)return n&&(E(this.plugin,`${e} +This does not seem to be an obsidian plugin, as there is no manifest.json file.`,15),console.error("BRAT: validateRepository",e,t,n)),null;let l=JSON.parse(m);if(!("id"in l))return n&&E(this.plugin,`${e} +The plugin id attribute for the release is missing from the manifest file`,15),null;if(!("version"in l))return n&&E(this.plugin,`${e} +The version attribute for the release is missing from the manifest file`,15),null;try{let r=Ae(g.tag_name,{includePrerelease:!0,loose:!0}),d=Ae(l.version,{includePrerelease:!0,loose:!0});Dt(r,d)!==0&&(n&&E(this.plugin,`${e} +Version mismatch detected: +Release tag version: ${g.tag_name} +Manifest version: ${l.version} + +The release tag version will be used to ensure consistency.`,15),l.version=r.version)}catch(r){}return l}catch(u){if(u instanceof D){let g=`GitHub API rate limit exceeded. Reset in ${u.getMinutesToReset()} minutes.`;throw n&&E(this.plugin,g,15),console.error(`BRAT: validateRepository ${u}`),E(this.plugin,`${u.message} Consider adding a personal access token in BRAT settings for higher limits. See documentation for details.`,20,()=>{window.open("https://github.com/TfTHacker/obsidian42-brat/blob/main/BRAT-DEVELOPER-GUIDE.md#github-api-rate-limits")}),u}if(u instanceof O)throw n&&(u.status===401?E(this.plugin,`${e} +GitHub API Authentication error. Please verify that your personal access token is valid and set correctly.`,15):E(this.plugin,`${e} +GitHub API error ${u.status}: ${u.message}`,15)),console.error(`BRAT: validateRepository ${u}`),u;return n&&E(this.plugin,`${e} +Unspecified error encountered: ${u}, verify debug for more information.`,15),null}}async getAllReleaseFiles(e,t,n="",i=""){let o=i,a=await He(e,this.plugin.settings.debuggingMode,o),c=await Ge(e,n,t,this.plugin.settings.debuggingMode,a,o);if(!c)return Promise.reject("No release found");let u=t||n!=="";return console.log({reallyGetManifestOrNot:u,version:c.tag_name}),{mainJs:await K(c,"main.js",this.plugin.settings.debuggingMode,a,o),manifest:u?await K(c,"manifest.json",this.plugin.settings.debuggingMode,a,o):"",styles:await K(c,"styles.css",this.plugin.settings.debuggingMode,a,o)}}async writeReleaseFilesToPluginFolder(e,t){var o,a;let n=`${(0,k.normalizePath)(`${this.plugin.app.vault.configDir}/plugins/${e}`)}/`,{adapter:i}=this.plugin.app.vault;await i.exists(n)||await i.mkdir(n),await i.write(`${n}main.js`,(o=t.mainJs)!=null?o:""),await i.write(`${n}manifest.json`,(a=t.manifest)!=null?a:""),t.styles&&await i.write(`${n}styles.css`,t.styles)}async addPlugin(e,t=!1,n=!1,i=!1,o="",a=!1,c=this.plugin.settings.enableAfterInstall,u=""){try{this.plugin.settings.debuggingMode&&console.log("BRAT: addPlugin",e,t,n,i,o,a,c,u?"with secret":"public");let g="";u&&u.trim()!==""?(g=await this.plugin.app.secretStorage.getSecret(u)||"",g||E(this.plugin,`Secret not found for token name: ${u}. Please add it to SecretStorage or clear the token name for this plugin.`,10)):this.plugin.settings.globalTokenName&&(g=await this.plugin.app.secretStorage.getSecret(this.plugin.settings.globalTokenName)||"");let m=10,l=await this.validateRepository(e,!0,!0,o,g),r=!!l;if(r||(l=await this.validateRepository(e,!1,!0,o,g)),l===null){let w=`${e} +A manifest.json file does not exist in the latest release of the repository. This plugin cannot be installed.`;return await this.plugin.log(w,!0),E(this.plugin,w,m),!1}if(!Object.hasOwn(l,"version")){let w=`${e} +The manifest.json file in the latest release or pre-release of the repository does not have a version number in the file. This plugin cannot be installed.`;return await this.plugin.log(w,!0),E(this.plugin,w,m),!1}let d=!1;if(Object.hasOwn(l,"minAppVersion")&&!(0,k.requireApiVersion)(l.minAppVersion)){if(o===""||o==="latest"||!this.plugin.settings.allowIncompatiblePlugins){let p=`Plugin: ${e} + +The manifest.json for this plugin indicates that the Obsidian version of the app needs to be ${l.minAppVersion}, but this installation of Obsidian is ${k.apiVersion}. + +You will need to update your Obsidian to use this plugin or contact the plugin developer for more information.`;return await this.plugin.log(p,!0),E(this.plugin,p,30),!1}if(!await De({app:this.plugin.app,message:createFragment(p=>{p.appendText("Plugin: "),p.createEl("code",{text:e}),p.createEl("br"),p.appendText("The "),p.createEl("code",{text:"manifest.json"}),p.appendText(" for this plugin indicates that the Obsidian version of the app needs to be "),p.createEl("code",{text:l.minAppVersion}),p.appendText(", but this installation of Obsidian is "),p.createEl("code",{text:k.apiVersion}),p.appendText("."),p.createEl("br"),p.appendText("Using this plugin is not recommended and may not work as expected. Use at your own risk."),p.createEl("br"),p.appendText("Do you want to install it anyways?")})}))return!1;d=!0}let b=async()=>{var T,C;let w=await this.getAllReleaseFiles(e,r,o,g);console.log("rFiles",w),(r||w.manifest==="")&&(w.manifest=JSON.stringify(l));let p=JSON.parse((T=w.manifest)!=null?T:"");if(d&&(p.brat={isIncompatible:!0,minAppVersionOriginal:p.minAppVersion},p.minAppVersion=k.apiVersion),k.Platform.isMobile&&p.isDesktopOnly)if(this.plugin.settings.allowIncompatiblePlugins){if(!await De({app:this.plugin.app,message:createFragment(R=>{R.appendText("Plugin: "),R.createEl("code",{text:e}),R.createEl("br"),R.appendText("The "),R.createEl("code",{text:"manifest.json"}),R.appendText(" for this plugin indicates that the plugin has "),R.createEl("code",{text:"isDesktopOnly: true"}),R.appendText(", but you are using a mobile device."),R.createEl("br"),R.appendText("Using this plugin is not recommended and may not work as expected. Use at your own risk."),R.createEl("br"),R.appendText("Do you want to forcefully run it on mobile anyways?")})}))return null;p.isDesktopOnly=!1,(C=p.brat)!=null||(p.brat={}),p.brat.isDesktopOnlyOriginal=!0,p.brat.isIncompatible=!0,d=!0}else{let N=`Plugin: ${e} + +The manifest.json for this plugin indicates that the plugin has isDesktopOnly: true, but you are using a mobile device. + +The plugin will not be installed.`;return await this.plugin.log(N,!0),E(this.plugin,N,30),null}if(d&&(w.manifest=JSON.stringify(p)),this.plugin.settings.debuggingMode&&console.log("BRAT: rFiles.manifest",r,w),w.mainJs===null){let N=`${e} +The release is not complete and cannot be downloaded. main.js is missing from the Release`;return await this.plugin.log(N,!0),E(this.plugin,N,m),null}return w};if(!t||a){let w=await b();if(w===null)return!1;if(await this.writeReleaseFilesToPluginFolder(l.id,w),It(this.plugin,e,o,d,u),c){let{plugins:p}=this.plugin.app,T=(0,k.normalizePath)(`${p.getPluginFolder()}/${l.id}`);await p.loadManifest(T),await p.enablePluginAndSave(l.id)}if(await this.plugin.app.plugins.loadManifests(),a)await this.reloadPlugin(l.id),await this.plugin.log(`${e} reinstalled`,!0),E(this.plugin,`${e} +Plugin has been reinstalled and reloaded with version ${l.version}`,m);else{let p=o===""?"":` (version: ${o})`,T=`${e}${p} +The plugin has been registered with BRAT.`;c||(T+=" You may still need to enable it the Community Plugin List."),await this.plugin.log(T,!0),E(this.plugin,T,m)}}else{let w=`${this.plugin.app.vault.configDir}/plugins/${l.id}/`,p="";try{p=await this.plugin.app.vault.adapter.read(`${w}manifest.json`)}catch(R){if(R.errno===-4058||R.errno===-2)return await this.addPlugin(e,!1,r,!1,o,!1,c,u),!0;console.log("BRAT - Local Manifest Load",l.id,JSON.stringify(R,null,2))}if(o!==""&&o!=="latest")return E(this.plugin,`The version of ${e} is frozen, not updating.`,3),!1;let T=await JSON.parse(p),C=Ae(T.version,{includePrerelease:!0,loose:!0}),N=Ae(l.version,{includePrerelease:!0,loose:!0});if(Dt(C,N)===-1){let R=await b();if(R===null)return!1;if(n){let W=`There is an update available for ${l.id} from version ${T.version} to ${l.version}. `;return await this.plugin.log(`${W}[Release Info](https://github.com/${e}/releases/tag/${l.version})`,!0),E(this.plugin,W,30,()=>{l&&window.open(`https://github.com/${e}/releases/tag/${l.version}`)}),!1}await this.writeReleaseFilesToPluginFolder(l.id,R),await this.plugin.app.plugins.loadManifests(),await this.reloadPlugin(l.id);let P=`${l.id} +Plugin has been updated from version ${T.version} to ${l.version}. `;return await this.plugin.log(`${P}[Release Info](https://github.com/${e}/releases/tag/${l.version})`,!0),E(this.plugin,P,30,()=>{l&&window.open(`https://github.com/${e}/releases/tag/${l.version}`)}),!0}return i&&E(this.plugin,`No update available for ${e}`,3),!0}}catch(g){console.error(`BRAT: Error adding plugin ${e}:`,{error:g,updatePluginFiles:t,seeIfUpdatedOnly:n,specifyVersion:o,forceReinstall:a});let m=g instanceof Error?g.message:"Unknown error occurred";return await this.plugin.log(`Error ${t?"updating":"adding"} plugin ${e}: ${m}`,!0),!1}return!0}async reloadPlugin(e){let{plugins:t}=this.plugin.app;try{await t.disablePlugin(e),await t.enablePlugin(e)}catch(n){this.plugin.settings.debuggingMode&&console.log("reload plugin",n)}}async updatePlugin(e,t=!1,n=!1,i=!1,o=""){let a=await this.addPlugin(e,!0,t,n,"",i,!1,o);return!a&&!t&&E(this.plugin,`${e} +Update of plugin failed.`),a}async checkForPluginUpdatesAndInstallUpdates(e=!1,t=!1){if(!await Ne()){console.log("BRAT: No internet detected.");return}let n,i="Checking for plugin updates STARTED";await this.plugin.log(i,!0),e&&this.plugin.settings.notificationsEnabled&&(n=new k.Notice(`BRAT +${i}`,3e4));let o=new Map(this.plugin.settings.pluginSubListFrozenVersion.map(u=>[u.repo,u.version])),a=new Map(this.plugin.settings.pluginSubListFrozenVersion.map(u=>[u.repo,u.tokenName||""]));for(let u of this.plugin.settings.pluginList){let g=o.get(u);g&&g!=="latest"||await this.updatePlugin(u,t,!1,!1,a.get(u)||"")}let c="Checking for plugin updates COMPLETED";await this.plugin.log(c,!0),e&&(n&&n.hide(),E(this.plugin,c,10))}deletePlugin(e){let t=`Removed ${e} from BRAT plugin list`;this.plugin.log(t,!0),this.plugin.settings.pluginList=this.plugin.settings.pluginList.filter(n=>n!==e),this.plugin.settings.pluginSubListFrozenVersion=this.plugin.settings.pluginSubListFrozenVersion.filter(n=>n.repo!==e),this.plugin.saveSettings()}getEnabledDisabledPlugins(e){let t=this.plugin.app.plugins,n=Object.values(t.manifests),i=Object.values(t.plugins).map(o=>o.manifest);return e?n.filter(o=>i.find(a=>o.id===a.id)):n.filter(o=>!i.find(a=>o.id===a.id))}checkIncompatiblePlugins(){let e=this.plugin.settings.pluginSubListFrozenVersion.filter(t=>t.isIncompatible).map(t=>t.repo);e.length>0&&E(this.plugin,`The following incompatible plugins were forcefully installed by BRAT and may not work as expected: +${e.join(` +`)}`,30)}};var _=require("obsidian");var ne=async(s,e,t)=>{let n=await G(e,!0,s.settings.debuggingMode);if(n||(n=await G(e,!1,s.settings.debuggingMode)),!n)return E(s,"There is no theme.css or theme-beta.css file in the root path of this repository, so there is no theme to install."),!1;let i=await Pt(e,s.settings.debuggingMode);if(!i)return E(s,"There is no manifest.json file in the root path of this repository, so theme cannot be installed."),!1;let o=await JSON.parse(i),a=(0,_.normalizePath)(Vn(s)+o.name),{adapter:c}=s.app.vault;await c.exists(a)||await c.mkdir(a),await c.write((0,_.normalizePath)(`${a}/theme.css`),n),await c.write((0,_.normalizePath)(`${a}/manifest.json`),i),Ct(s,e,Q(n));let u="";return t?(Nt(s,e,n),u=`${o.name} theme installed from ${e}. `,setTimeout(()=>{s.app.customCss.setTheme(o.name)},500)):u=`${o.name} theme updated from ${e}.`,s.log(`${u}[Theme Info](https://github.com/${e})`,!1),E(s,u,20,()=>{window.open(`https://github.com/${e}`)}),!0},q=async(s,e)=>{if(!await Ne()){console.log("BRAT: No internet detected.");return}let t,n="Checking for beta theme updates STARTED";await s.log(n,!0),e&&s.settings.notificationsEnabled&&(t=new _.Notice(`BRAT +${n}`,3e4));for(let o of s.settings.themesList){let a=await Z(o.repo,!0,s.settings.debuggingMode);a==="0"&&(a=await Z(o.repo,!1,s.settings.debuggingMode)),console.log("BRAT: lastUpdateOnline",a),a!==o.lastUpdate&&await ne(s,o.repo,!1)}let i="Checking for beta theme updates COMPLETED";(async()=>await s.log(i,!0))(),e&&(s.settings.notificationsEnabled&&t&&t.hide(),E(s,i))},Ce=(s,e)=>{s.settings.themesList=s.settings.themesList.filter(n=>n.repo!==e),s.saveSettings();let t=`Removed ${e} from BRAT themes list and will no longer be updated. However, the theme files still exist in the vault. To remove them, go into Settings > Appearance and remove the theme.`;s.log(t,!0),E(s,t)},Vn=s=>`${(0,_.normalizePath)(`${s.app.vault.configDir}/themes`)}/`;var $t="brat-migrations";async function Un(s,e){try{let t=await s.vault.adapter.read(`${s.vault.configDir}/plugins/obsidian42-brat/${$t}.json`);return JSON.parse(t).appliedMigrations.includes(e)}catch(t){return!1}}async function _n(s,e){try{let t=`${s.vault.configDir}/plugins/obsidian42-brat/${$t}.json`,n={appliedMigrations:[]};try{let i=await s.vault.adapter.read(t);n=JSON.parse(i)}catch(i){}n.appliedMigrations.includes(e)||(n.appliedMigrations.push(e),await s.vault.adapter.write(t,JSON.stringify(n,null,2)))}catch(t){console.error(`BRAT: Failed to mark migration ${e} complete:`,t)}}async function xt(s,e,t){let n="tokens-to-secretstorage-v1";if(!await Un(s,n))try{let i=0,o=u=>{let m=`brat-gh-${u.toLowerCase().replace(/[^a-z0-9-]/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")}`;return m.length>64?m.substring(0,64).replace(/-$/,""):m},a=u=>{let g=s.secretStorage.listSecrets();for(let m of g)if(s.secretStorage.getSecret(m)===u)return m;return null},c=(u,g)=>{let m=a(u);return m?(console.log(`BRAT: Reusing existing secret "${m}"`),m):(s.secretStorage.setSecret(g,u),console.log(`BRAT: Created new secret "${g}"`),g)};if(e.personalAccessToken&&e.personalAccessToken.trim()!==""){let u=e.personalAccessToken.trim(),m=c(u,"brat-gh-global");e.globalTokenName=m,e.personalAccessToken="",i++}if(e.pluginSubListFrozenVersion){for(let u of e.pluginSubListFrozenVersion)if(u.token&&u.token.trim()!==""){let g=u.token.trim(),m=o(u.repo),l=c(g,m);u.tokenName=l,u.token=void 0,i++}}i>0&&(await t(),console.log(`BRAT: Migrated ${i} token(s) to SecretStorage`)),await _n(s,n)}catch(i){console.error("BRAT: Failed to migrate tokens to SecretStorage:",i)}}var H=require("obsidian");var B=class extends H.Modal{constructor(e,t=!1){super(e.app),this.plugin=e,this.address="",this.openSettingsTabAfterwards=t}async submitForm(){if(this.address==="")return;let e=this.address.replace("https://github.com/","");if(At(this.plugin,e)){E(this.plugin,"This theme is already in the list for beta testing",10);return}await ne(this.plugin,e,!0)&&this.close()}onOpen(){this.contentEl.createEl("h4",{text:"Github repository for beta theme:"}),this.contentEl.createEl("form",{},e=>{e.addClass("brat-modal"),new H.Setting(e).addText(i=>{i.setPlaceholder("Repository (example: https://github.com/GitHubUserName/repository-name"),i.setValue(this.address),i.onChange(o=>{this.address=o.trim()}),i.inputEl.addEventListener("keydown",o=>{o.key==="Enter"&&this.address!==" "&&(o.preventDefault(),this.submitForm())}),i.inputEl.style.width="100%",window.setTimeout(()=>{let o=document.querySelector(".setting-item-info");o&&o.remove(),i.inputEl.focus()},10)}),e.createDiv("modal-button-container",i=>{new H.ButtonComponent(i).setButtonText("Never mind").onClick(()=>{this.close()}),new H.ButtonComponent(i).setButtonText("Add theme").setCta().onClick(o=>{o.preventDefault(),console.log("Add theme button clicked"),this.address!==""&&this.submitForm()})});let t=e.createDiv();t.style.borderTop="1px solid #ccc",t.style.marginTop="30px";let n=t.createSpan();n.createEl("a",{href:"https://bit.ly/o42-twitter",text:"TFTHacker"}),n.appendText(" and "),n.createEl("a",{href:"https://github.com/johannrichard",text:"johannrichard"}),n.style.fontStyle="italic",t.appendChild(n),z(t,!1),window.setTimeout(()=>{let i=e.querySelectorAll(".brat-modal .setting-item-info");for(let o of Array.from(i))o.remove()},50)})}onClose(){this.openSettingsTabAfterwards&&(this.plugin.app.setting.openTab(),this.plugin.app.setting.openTabById(this.plugin.APP_ID))}};var Ot=require("obsidian");function Ft(){(0,Ot.addIcon)("BratIcon",'')}var Bt=require("obsidian"),S=class extends Bt.FuzzySuggestModal{constructor(t){super(t.app);this.data=[];this.scope.register(["Shift"],"Enter",n=>{this.enterTrigger(n)}),this.scope.register(["Ctrl"],"Enter",n=>{this.enterTrigger(n)})}setSuggesterData(t){this.data=t}display(t){this.callbackFunction=t,this.open()}getItems(){return this.data}getItemText(t){return t.display}onChooseItem(){}renderSuggestion(t,n){n.createEl("div",{text:t.item.display})}enterTrigger(t){var o;let n=(o=document.querySelector(".suggestion-item.is-selected div"))==null?void 0:o.textContent,i=this.data.find(a=>a.display===n);i&&(this.invokeCallback(i,t),this.close())}onChooseSuggestion(t,n){this.invokeCallback(t.item,n)}invokeCallback(t,n){typeof this.callbackFunction=="function"&&this.callbackFunction(t,n)}};var se=class{constructor(e){this.bratCommands=[{id:"AddBetaPlugin",icon:"BratIcon",name:"Plugins: Add a beta plugin for testing (with or without version)",showInRibbon:!0,callback:()=>{this.plugin.betaPlugins.displayAddNewPluginModal(!1,!0)}},{id:"checkForUpdatesAndUpdate",icon:"BratIcon",name:"Plugins: Check for updates to all beta plugins and UPDATE",showInRibbon:!0,callback:async()=>{await this.plugin.betaPlugins.checkForPluginUpdatesAndInstallUpdates(!0,!1)}},{id:"checkForUpdatesAndDontUpdate",icon:"BratIcon",name:"Plugins: Only check for updates to beta plugins, but don't Update",showInRibbon:!0,callback:async()=>{await this.plugin.betaPlugins.checkForPluginUpdatesAndInstallUpdates(!0,!0)}},{id:"updateOnePlugin",icon:"BratIcon",name:"Plugins: Choose a single plugin version to update",showInRibbon:!0,callback:()=>{let e=new Map(this.plugin.settings.pluginSubListFrozenVersion.map(i=>[i.repo,{version:i.version,token:i.token}])),t=Object.values(this.plugin.settings.pluginList).filter(i=>{let o=e.get(i);return!(o!=null&&o.version)||o.version==="latest"}).map(i=>({display:i,info:i})),n=new S(this.plugin);n.setSuggesterData(t),n.display(i=>{let o=`Checking for updates for ${i.info}`,a=e.get(i.info);this.plugin.log(o,!0),E(this.plugin,` +${o}`,3),this.plugin.betaPlugins.updatePlugin(i.info,!1,!0,!1,a==null?void 0:a.token)})}},{id:"reinstallOnePlugin",icon:"BratIcon",name:"Plugins: Choose a single plugin to reinstall",showInRibbon:!0,callback:()=>{let e=new Set(this.plugin.settings.pluginSubListFrozenVersion.map(i=>i.repo)),t=Object.values(this.plugin.settings.pluginList).filter(i=>!e.has(i)).map(i=>({display:i,info:i})),n=new S(this.plugin);n.setSuggesterData(t),n.display(i=>{let o=`Reinstalling ${i.info}`;E(this.plugin,` +${o}`,3),this.plugin.log(o,!0),this.plugin.betaPlugins.updatePlugin(i.info,!1,!1,!0)})}},{id:"restartPlugin",icon:"BratIcon",name:"Plugins: Restart a plugin that is already installed",showInRibbon:!0,callback:()=>{let e=Object.values(this.plugin.app.plugins.manifests).map(n=>({display:n.id,info:n.id})),t=new S(this.plugin);t.setSuggesterData(e),t.display(n=>{E(this.plugin,`${n.info} +Plugin reloading .....`,5),this.plugin.betaPlugins.reloadPlugin(n.info)})}},{id:"disablePlugin",icon:"BratIcon",name:"Plugins: Disable a plugin - toggle it off",showInRibbon:!0,callback:()=>{let e=this.plugin.betaPlugins.getEnabledDisabledPlugins(!0).map(n=>({display:`${n.name} (${n.id})`,info:n.id})),t=new S(this.plugin);t.setSuggesterData(e),t.display(n=>{this.plugin.log(`${n.display} plugin disabled`,!1),this.plugin.settings.debuggingMode&&console.log(n.info),this.plugin.app.plugins.disablePluginAndSave(n.info)})}},{id:"enablePlugin",icon:"BratIcon",name:"Plugins: Enable a plugin - toggle it on",showInRibbon:!0,callback:()=>{let e=this.plugin.betaPlugins.getEnabledDisabledPlugins(!1).map(n=>({display:`${n.name} (${n.id})`,info:n.id})),t=new S(this.plugin);t.setSuggesterData(e),t.display(n=>{this.plugin.log(`${n.display} plugin enabled`,!1),this.plugin.app.plugins.enablePluginAndSave(n.info)})}},{id:"openGitHubZRepository",icon:"BratIcon",name:"Plugins: Open the GitHub repository for a plugin",showInRibbon:!0,callback:async()=>{let e=await yt(this.plugin.settings.debuggingMode);if(e){let t=Object.values(e).map(o=>({display:`Plugin: ${o.name} (${o.repo})`,info:o.repo})),n=Object.values(this.plugin.settings.pluginList).map(o=>({display:`BRAT: ${o}`,info:o}));for(let o of t)n.push(o);let i=new S(this.plugin);i.setSuggesterData(n),i.display(o=>{o.info&&window.open(`https://github.com/${o.info}`)})}}},{id:"openGitHubRepoTheme",icon:"BratIcon",name:"Themes: Open the GitHub repository for a theme (appearance)",showInRibbon:!0,callback:async()=>{let e=await vt(this.plugin.settings.debuggingMode);if(e){let t=Object.values(e).map(i=>({display:`Theme: ${i.name} (${i.repo})`,info:i.repo})),n=new S(this.plugin);n.setSuggesterData(t),n.display(i=>{i.info&&window.open(`https://github.com/${i.info}`)})}}},{id:"opentPluginSettings",icon:"BratIcon",name:"Plugins: Open Plugin Settings Tab",showInRibbon:!0,callback:()=>{let e=this.plugin.app.setting,t=Object.values(e.pluginTabs).map(o=>({display:`Plugin: ${o.name}`,info:o.id})),n=new S(this.plugin),i=Object.values(e.settingTabs).map(o=>({display:`Core: ${o.name}`,info:o.id}));for(let o of t)i.push(o);n.setSuggesterData(i),n.display(o=>{e.open(),e.openTabById(o.info)})}},{id:"GrabBetaTheme",icon:"BratIcon",name:"Themes: Grab a beta theme for testing from a Github repository",showInRibbon:!0,callback:()=>{new B(this.plugin).open()}},{id:"updateBetaThemes",icon:"BratIcon",name:"Themes: Update beta themes",showInRibbon:!0,callback:async()=>{await q(this.plugin,!0)}},{id:"allCommands",icon:"BratIcon",name:"All Commands list",showInRibbon:!1,callback:()=>{this.ribbonDisplayCommands()}}];this.plugin=e;for(let t of this.bratCommands)this.plugin.addCommand({id:t.id,name:t.name,icon:t.icon,callback:()=>{t.callback()}})}ribbonDisplayCommands(){let e=[];for(let a of this.bratCommands)a.showInRibbon&&e.push({display:a.name,info:a.callback});let t=new S(this.plugin),n=this.plugin.app.setting,i=Object.values(n.settingTabs).map(a=>({display:`Core: ${a.name}`,info:()=>{n.open(),n.openTabById(a.id)}})),o=Object.values(n.pluginTabs).map(a=>({display:`Plugin: ${a.name}`,info:()=>{n.open(),n.openTabById(a.id)}}));e.push({display:"---- Core Plugin Settings ----",info:()=>{this.ribbonDisplayCommands()}});for(let a of i)e.push(a);e.push({display:"---- Plugin Settings ----",info:()=>{this.ribbonDisplayCommands()}});for(let a of o)e.push(a);t.setSuggesterData(e),t.display(a=>{typeof a.info=="function"&&a.info()})}};var A=require("obsidian");var Le=class extends A.PluginSettingTab{constructor(t,n){super(t,n);this.accessTokenSetting=null;this.accessTokenButton=null;this.tokenInfo=null;this.validator=null;this.plugin=n}display(){let{containerEl:t}=this;t.empty(),t.addClass("brat-settings"),new A.Setting(t).setName("Auto-enable plugins after installation").setDesc('If enabled beta plugins will be automatically enabled after installtion by default. Note: you can toggle this on and off for each plugin in the "Add Plugin" form.').addToggle(l=>{l.setValue(this.plugin.settings.enableAfterInstall).onChange(async r=>{this.plugin.settings.enableAfterInstall=r,await this.plugin.saveSettings()})}),new A.Setting(t).setName("Auto-update plugins at startup").setDesc("If enabled all beta plugins will be checked for updates each time Obsidian starts. Note: this does not update frozen version plugins.").addToggle(l=>{l.setValue(this.plugin.settings.updateAtStartup).onChange(async r=>{this.plugin.settings.updateAtStartup=r,await this.plugin.saveSettings()})}),new A.Setting(t).setName("Auto-update themes at startup").setDesc("If enabled all beta themes will be checked for updates each time Obsidian starts.").addToggle(l=>{l.setValue(this.plugin.settings.updateThemesAtStartup).onChange(async r=>{this.plugin.settings.updateThemesAtStartup=r,await this.plugin.saveSettings()})}),new A.Setting(t).setName("Select latest plugin version by default").setDesc("If enabled the latest version will be selected by default when adding a new plugin.").addToggle(l=>{l.setValue(this.plugin.settings.selectLatestPluginVersionByDefault).onChange(async r=>{this.plugin.settings.selectLatestPluginVersionByDefault=r,await this.plugin.saveSettings()})}),new A.Setting(t).setName("Allow incompatible plugins").setDesc("If enabled, plugins with higher app versions will be allowed to be installed. Also it allows desktop-only plugins to be installed on mobile devices.").addToggle(l=>{l.setValue(this.plugin.settings.allowIncompatiblePlugins).onChange(async r=>{this.plugin.settings.allowIncompatiblePlugins=r,await this.plugin.saveSettings()})}),z(t,!0),t.createEl("hr");let n=new Map(this.plugin.settings.pluginSubListFrozenVersion.map(l=>[l.repo,l])),i=new Map,o=new A.SettingGroup(t).setHeading("Beta plugin list");o.addSearch(l=>{l.setPlaceholder("Filter plugins"),l.onChange(r=>{let d=r.toLowerCase().trim();i.forEach(({container:b,pluginName:w})=>{d===""||w.includes(d)?b.removeAttribute("hidden"):b.setAttribute("hidden","true")})})}),o.addSetting(l=>{let r=document.createDocumentFragment();r.createEl("div",{text:'The following is a list of beta plugins added via the command "Add a beta plugin for testing". You can chose to add the latest version or a frozen version. A frozen version is a specific release of a plugin based on its release tag.'}),r.createEl("p"),r.createEl("div",{text:"Click the 'Edit' button next to a plugin to change the installed version and the x button next to a plugin to remove it from the list."}),r.createEl("p"),r.createEl("span").createEl("b",{text:"Note: "}),r.createSpan({text:"Removing from the list does not delete the plugin, this should be done from the Community Plugins tab in Settings."}),l.setDesc(r),l.addButton(d=>{d.setButtonText("Add beta plugin").setCta().onClick(()=>{this.plugin.betaPlugins.displayAddNewPluginModal(!0)})})});for(let l of this.plugin.settings.pluginList){let r=n.get(l);o.addSetting(d=>{let b=(r==null?void 0:r.tokenName)||"",w=b?this.plugin.app.secretStorage.getSecret(b):"",p=!!(b&&!w),T=document.createDocumentFragment(),C=r!=null&&r.version?` Tracked version: ${r.version} ${r.version==="latest"?"":"(frozen)"}`:"",N=r!=null&&r.isIncompatible?" (incompatible)":"";T.createDiv({text:`${C}${N}`}),p&&T.createDiv({text:` Secret not defined or empty: ${b}`,cls:"mod-warning",title:"Token name configured but secret is missing. Add the secret or update the plugin configuration."}),d.setName(ee(l)).setDesc(T);let R=d.settingEl;R.addClass("brat-plugin-item"),i.set(l,{container:R,pluginName:l.toLowerCase()}),(!(r!=null&&r.version)||r.version==="latest")&&d.addButton(P=>{p?P.setIcon("sync").setTooltip(`Secret missing: ${b}. Please add the secret or update the plugin configuration.`).setWarning().setDisabled(!0):P.setIcon("sync").setTooltip("Check and update plugin").onClick(async()=>{await this.plugin.betaPlugins.updatePlugin(l,!1,!0,!1,(r==null?void 0:r.tokenName)||"")})}),d.addButton(P=>{P.setIcon("edit").setTooltip("Change version and update settings"),p&&P.setWarning(),P.onClick(()=>{this.plugin.betaPlugins.displayAddNewPluginModal(!0,!0,l,r==null?void 0:r.version,(r==null?void 0:r.tokenName)||""),this.plugin.app.setting.updatePluginSection()})}).addButton(P=>{P.setIcon("cross").setTooltip("Remove this beta plugin").setWarning().onClick(()=>{if(P.buttonEl.textContent==="")P.setButtonText("Click once more to confirm removal");else{let{buttonEl:W}=P,{parentElement:M}=W;M!=null&&M.parentElement&&(M.parentElement.remove(),this.plugin.betaPlugins.deletePlugin(l))}})})})}let a=new Map,c=new A.SettingGroup(t).setHeading("Beta themes list");c.addSetting(l=>l.addButton(r=>{r.setButtonText("Add beta theme").setCta().onClick(()=>{this.plugin.app.setting.close(),new B(this.plugin).open()})})),c.addSearch(l=>{l.setPlaceholder("Filter themes"),l.onChange(r=>{let d=r.toLowerCase().trim();a.forEach(({container:b,themeName:w})=>{d===""||w.includes(d)?b.removeAttribute("hidden"):b.setAttribute("hidden","true")})})});for(let l of this.plugin.settings.themesList)c.addSetting(r=>{r.setName(ee(l.repo));let d=r.settingEl;d.addClass("brat-theme-item"),a.set(l.repo,{container:d,themeName:l.repo.toLowerCase()}),r.addButton(b=>{b.setIcon("cross").setTooltip("Delete this beta theme").onClick(()=>{if(b.buttonEl.textContent==="")b.setButtonText("Click once more to confirm removal");else{let{buttonEl:w}=b,{parentElement:p}=w;p!=null&&p.parentElement&&(p.parentElement.remove(),Ce(this.plugin,l.repo))}})})});new A.SettingGroup(t).setHeading("Monitoring").addSetting(l=>l.setName("Enable notifications").setDesc("BRAT will provide popup notifications for its various activities. Turn this off means no notifications from BRAT.").addToggle(r=>{r.setValue(this.plugin.settings.notificationsEnabled),r.onChange(async d=>{this.plugin.settings.notificationsEnabled=d,await this.plugin.saveSettings()})})).addSetting(l=>l.setName("Enable logging").setDesc("Plugin updates will be logged to a file in the log file.").addToggle(r=>{r.setValue(this.plugin.settings.loggingEnabled).onChange(async d=>{this.plugin.settings.loggingEnabled=d,await this.plugin.saveSettings()})})).addSetting(l=>l.setName("BRAT log file location").setDesc("Logs will be saved to this file. Don't add .md to the file name.").addSearch(r=>{r.setPlaceholder("Example: BRAT-log").setValue(this.plugin.settings.loggingPath).onChange(async d=>{this.plugin.settings.loggingPath=d,await this.plugin.saveSettings()})})).addSetting(l=>l.setName("Enable verbose logging").setDesc("Get a lot more information in the log.").addToggle(r=>{r.setValue(this.plugin.settings.loggingVerboseEnabled).onChange(async d=>{this.plugin.settings.loggingVerboseEnabled=d,await this.plugin.saveSettings()})})).addSetting(l=>l.setName("Debugging mode").setDesc("Atomic Bomb level console logging. Can be used for troubleshooting and development.").addToggle(r=>{r.setValue(this.plugin.settings.debuggingMode).onChange(async d=>{this.plugin.settings.debuggingMode=d,await this.plugin.saveSettings()})}));let g=new A.SettingGroup(t).setHeading("GitHub Personal Access Token"),m="";g.addSetting(l=>{l.setName("Personal access token").setDesc(Lt({prependText:"Set a personal access token to increase rate limits for public repositories on GitHub. You can create one in ",url:"https://github.com/settings/tokens/new?scopes=public_repo",text:"your GitHub account settings",appendText:" and then add it here. Please consult the documentation for more details."})),this.accessTokenSetting=new A.SecretComponent(this.plugin.app,l.controlEl),this.accessTokenSetting.setValue(this.plugin.settings.globalTokenName||"").onChange(async r=>{var b,w,p;let d=(r==null?void 0:r.trim())||"";this.plugin.settings.globalTokenName=d,await this.plugin.saveSettings(),d?(m=this.plugin.app.secretStorage.getSecret(d)||"",(b=this.accessTokenButton)==null||b.setDisabled(!1)):(m="",(w=this.accessTokenButton)==null||w.setDisabled(!0),await((p=this.validator)==null?void 0:p.validateToken("")))}),this.plugin.settings.globalTokenName&&(m=this.plugin.app.secretStorage.getSecret(this.plugin.settings.globalTokenName)||""),l.addExtraButton(r=>{r.setIcon("cross").setTooltip("Clear personal access token").onClick(async()=>{var d,b;this.plugin.settings.globalTokenName="",await this.plugin.saveSettings(),(d=this.accessTokenSetting)==null||d.setValue(""),m="",await((b=this.validator)==null?void 0:b.validateToken(""))})}).addButton(r=>{this.accessTokenButton=r,r.setButtonText("Validate").setCta().onClick(async()=>{var d;m&&await((d=this.validator)==null?void 0:d.validateToken(m))})}).then(()=>{var r;this.tokenInfo=this.createTokenInfoElement(t),this.validator=new j(this.tokenInfo),(r=this.validator)==null||r.validateToken(m).then(d=>{var b;(b=this.accessTokenButton)==null||b.setDisabled(d||!this.plugin.settings.globalTokenName)})})})}createTokenInfoElement(t){let n=t.createDiv({cls:"brat-token-info"});return n.createDiv({cls:"brat-token-status"}),n.createDiv({cls:"brat-token-details"}),n}};var ie=class{constructor(e){this.console=(e,...t)=>{console.log(`BRAT: ${e}`,...t)};this.themes={themeseCheckAndUpates:async e=>{await q(this.plugin,e)},themeInstallTheme:async e=>{let t=e.replace("https://github.com/","");await ne(this.plugin,t,!0)},themesDelete:e=>{let t=e.replace("https://github.com/","");Ce(this.plugin,t)},grabCommmunityThemeCssFile:async(e,t=!1)=>await G(e,t,this.plugin.settings.debuggingMode),grabChecksumOfThemeCssFile:async(e,t=!1)=>await Z(e,t,this.plugin.settings.debuggingMode),grabLastCommitDateForFile:async(e,t)=>await Rt(e,t)};this.plugin=e}};var de=require("obsidian"),Qt=ln(Kt());async function Zt(s,e,t=!1){if(s.settings.debuggingMode&&console.log(`BRAT: ${e}`),s.settings.loggingEnabled){if(!s.settings.loggingVerboseEnabled&&t)return;let n=`${s.settings.loggingPath}.md`,i=`[[${(0,de.moment)().format((0,Qt.getDailyNoteSettings)().format).toString()}]] ${(0,de.moment)().format("HH:mm")}`,o=window.require("os"),a=de.Platform.isDesktop?o.hostname():"MOBILE",c=`${i} ${a} ${e.replace(` +`," ")} +`,u=s.app.vault.getAbstractFileByPath(n);u?await s.app.vault.append(u,c):u=await s.app.vault.create(n,c)}}var ke=class extends en.Plugin{constructor(){super(...arguments);this.APP_NAME="BRAT";this.APP_ID="obsidian42-brat";this.settings=je;this.betaPlugins=new te(this);this.commands=new se(this);this.bratApi=new ie(this);this.obsidianProtocolHandler=t=>{if(!t.plugin&&!t.theme){E(this,"Could not locate the repository from the URL.",10);return}for(let n of["plugin","theme"])if(t[n]){let i;switch(n){case"plugin":i=new U(this,this.betaPlugins,!0,!1,t[n],t.version?t.version:void 0),i.open();break;case"theme":i=new B(this),i.address=t[n],i.open();break}return}}}onload(){console.log(`loading ${this.APP_NAME}`),Ft(),this.addRibbonIcon("BratIcon","BRAT",()=>{this.commands.ribbonDisplayCommands()}),this.loadSettings().then(async()=>{await xt(this.app,this.settings,()=>this.saveSettings()),this.app.workspace.onLayoutReady(()=>{this.addSettingTab(new Le(this.app,this)),this.registerObsidianProtocolHandler("brat",this.obsidianProtocolHandler),this.betaPlugins.checkIncompatiblePlugins(),this.settings.updateAtStartup&&setTimeout(()=>{this.betaPlugins.checkForPluginUpdatesAndInstallUpdates(!1)},6e4),this.settings.updateThemesAtStartup&&setTimeout(()=>{q(this,!1)},12e4),setTimeout(()=>{window.bratAPI=this.bratApi},500)})}).catch(t=>{console.error("Failed to load settings:",t)})}async log(t,n=!1){await Zt(this,t,n)}onunload(){console.log(`unloading ${this.APP_NAME}`)}async loadSettings(){this.settings=Object.assign({},je,await this.loadData())}async saveSettings(){await this.saveData(this.settings)}}; + +/* nosourcemap */ \ No newline at end of file diff --git a/.obsidian/plugins/obsidian42-brat/manifest.json b/.obsidian/plugins/obsidian42-brat/manifest.json new file mode 100644 index 0000000..e7591a9 --- /dev/null +++ b/.obsidian/plugins/obsidian42-brat/manifest.json @@ -0,0 +1,14 @@ +{ + "id": "obsidian42-brat", + "name": "BRAT", + "version": "2.0.2", + "minAppVersion": "1.11.4", + "description": "Easily install a beta version of a plugin for testing.", + "author": "TfTHacker", + "authorUrl": "https://github.com/TfTHacker/obsidian42-brat", + "helpUrl": "https://tfthacker.com/BRAT", + "isDesktopOnly": false, + "fundingUrl": { + "Visit my site": "https://tfthacker.com" + } +} diff --git a/.obsidian/plugins/obsidian42-brat/styles.css b/.obsidian/plugins/obsidian42-brat/styles.css new file mode 100644 index 0000000..b9a3684 --- /dev/null +++ b/.obsidian/plugins/obsidian42-brat/styles.css @@ -0,0 +1,152 @@ +.brat-modal .modal-button-container { + margin-top: 5px; +} + +.brat-modal .disabled-setting { + opacity: 0.5; +} + +.brat-modal .disabled-setting:hover { + cursor: not-allowed; +} + +/* Input validation styles */ +.brat-settings .valid-input, +.brat-modal .valid-repository { + border-color: var(--color-green); +} +.brat-settings .invalid-input, +.brat-modal .invalid-repository { + border-color: var(--color-red); +} +.brat-settings .validation-error, +.brat-modal .validation-error { + border-color: var(--color-orange); +} + +/* Version selector */ +.brat-version-selector { + width: 100%; + max-width: 400px; + justify-content: left; +} + +.brat-token-input { + min-width: 33%; +} + +/* Token info container styles */ +.brat-token-info { + margin-top: 8px; + font-size: 0.8em; + padding: 8px; + border-radius: 4px; + background-color: var(--background-secondary); +} + +/* Token status indicators */ +.brat-token-info.valid, +.brat-token-status.valid { + color: var(--color-green); +} + +.brat-token-info.invalid, +.brat-token-status.invalid { + color: var(--color-red); +} + +.brat-token-info.valid { + border-left: 3px solid var(--color-green); +} + +.brat-token-info.invalid { + border-left: 3px solid var(--color-red); +} + +/* Token details and status */ +.brat-token-status { + margin-bottom: 4px; +} + +.brat-token-details { + margin-top: 4px; + color: var(--text-muted); +} + +/* Token warnings */ +.brat-token-warning { + color: var(--color-orange); + margin-top: 4px; +} + +/* Token additional info */ +.brat-token-scopes, +.brat-token-rate { + color: var(--text-muted); + margin-top: 2px; +} + +/* Flex break utility */ +.brat-modal .break { + flex-basis: 100%; + height: 0; +} + +/* Validation status */ +.brat-modal .validation-status-error { + color: var(--text-error); +} + +.brat-modal .validation-status { + margin-top: 0.5em; + margin-bottom: 0.5em; + font-size: 0.8em; + text-align: left; +} + +.confirm-modal .ok-button { + margin-right: 10px; + margin-top: 20px; +} + +/* Hide filtered plugin items */ +.brat-plugin-item[hidden] { + display: none !important; +} + +/* Hide filtered theme items */ +.brat-theme-item[hidden] { + display: none !important; +} + +/* Filter and button layout */ +.brat-filter-and-button { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + margin: 0.75em 0; +} + +.brat-filter-input { + max-width: 300px; + padding: 4px 8px; + border: 1px solid var(--background-modifier-border); + border-radius: 4px; + background-color: var(--background-secondary); + color: var(--text-normal); +} + +.brat-filter-input:focus { + outline: none; + border-color: var(--interactive-accent); +} + +.brat-filter-and-button .setting-item { + border: none; + padding: 0; +} + +.brat-filter-and-button .setting-item-control { + justify-content: flex-end; +} diff --git a/.obsidian/plugins/qmd-search/data.json b/.obsidian/plugins/qmd-search/data.json new file mode 100644 index 0000000..33f5dd0 --- /dev/null +++ b/.obsidian/plugins/qmd-search/data.json @@ -0,0 +1,21 @@ +{ + "qmdBinaryPath": "/opt/homebrew/bin/qmd", + "collectionName": "", + "indexName": null, + "fileMask": "**/*.md", + "debounceMs": 45000, + "enablePeriodicUpdates": true, + "periodicUpdateMinutes": 15, + "defaultSearchMode": "semantic", + "fallbackOnSemanticFailure": true, + "fallbackOnZeroResults": false, + "showEmbeddingsBanner": true, + "autoGenerateEmbeddings": true, + "enableRibbonIcon": true, + "enableSearchPane": false, + "showScoresInResults": true, + "lastIndexUpdateTime": "2026-02-26T22:48:31.431Z", + "lastEmbeddingRunTime": "2026-02-26T22:48:32.734Z", + "lastSearchMode": "semantic", + "lastError": null +} \ No newline at end of file diff --git a/.obsidian/plugins/qmd-search/main.js b/.obsidian/plugins/qmd-search/main.js new file mode 100644 index 0000000..471d47a --- /dev/null +++ b/.obsidian/plugins/qmd-search/main.js @@ -0,0 +1,8 @@ +/* +THIS IS A GENERATED/BUNDLED FILE BY ESBUILD +if you want to view the source, please visit the github repository of this plugin +*/ + +var M=Object.defineProperty;var I=Object.getOwnPropertyDescriptor;var k=Object.getOwnPropertyNames;var q=Object.prototype.hasOwnProperty;var T=(h,i)=>{for(var e in i)M(h,e,{get:i[e],enumerable:!0})},N=(h,i,e,t)=>{if(i&&typeof i=="object"||typeof i=="function")for(let s of k(i))!q.call(h,s)&&s!==e&&M(h,s,{get:()=>i[s],enumerable:!(t=I(i,s))||t.enumerable});return h};var B=h=>N(M({},"__esModule",{value:!0}),h);var F={};T(F,{default:()=>w});module.exports=B(F);var d=require("obsidian");var y={qmdBinaryPath:"qmd",collectionName:"",indexName:null,fileMask:"**/*.md",debounceMs:45e3,enablePeriodicUpdates:!0,periodicUpdateMinutes:15,defaultSearchMode:"semantic",fallbackOnSemanticFailure:!0,fallbackOnZeroResults:!1,showEmbeddingsBanner:!0,autoGenerateEmbeddings:!0,enableRibbonIcon:!0,enableSearchPane:!1,showScoresInResults:!0,lastIndexUpdateTime:null,lastEmbeddingRunTime:null,lastSearchMode:null,lastError:null};function D(h){return h.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}var E=require("child_process"),P=require("util"),x=(0,P.promisify)(E.exec),u=class extends Error{constructor(i,e,t){super(i),this.name="QMDError",this.type=e,this.stderr=t}},f=class{constructor(i,e,t,s,n){this.commandQueue=[];this.isProcessing=!1;this.currentSearchProcess=null;this.binaryPath=i,this.collectionName=e,this.indexName=t,this.vaultPath=s,this.execAsync=n!=null?n:x}abortSearch(){this.currentSearchProcess&&(this.currentSearchProcess.kill(),this.currentSearchProcess=null)}updateConfig(i,e,t){this.binaryPath=i,this.collectionName=e,this.indexName=t}buildBinaryWithIndex(){let i=`"${this.binaryPath}"`;return this.indexName&&(i+=` --index "${this.indexName}"`),i}buildBaseCommand(i){return`${this.buildBinaryWithIndex()} ${i}`}async queueCommand(i){return new Promise((e,t)=>{let s=async()=>{try{let n=await i();e(n)}catch(n){t(n instanceof Error?n:new Error(String(n)))}};this.commandQueue.push(s),this.processQueue()})}async processQueue(){if(!(this.isProcessing||this.commandQueue.length===0)){for(this.isProcessing=!0;this.commandQueue.length>0;){let i=this.commandQueue.shift();i&&await i()}this.isProcessing=!1}}async execCommand(i){var e,t,s,n;try{let{stdout:r,stderr:o}=await this.execAsync(i,{maxBuffer:10485760,cwd:this.vaultPath});return{stdout:r,stderr:o}}catch(r){let o=r;throw o.code===127||(e=o.message)!=null&&e.includes("not found")?new u(`QMD binary not found at: ${this.binaryPath}`,"not_found",o.stderr):(t=o.stderr)!=null&&t.includes("no collection")?new u(`Collection "${this.collectionName}" not found`,"no_collection",o.stderr):(s=o.stderr)!=null&&s.includes("no embeddings")||(n=o.stderr)!=null&&n.includes("embeddings not found")?new u("Embeddings not generated for this collection","no_embeddings",o.stderr):new u(o.message||"Unknown QMD error","execution_error",o.stderr)}}execSearchCommand(i){return this.execAsync!==x?this.execCommand(i):new Promise((e,t)=>{this.currentSearchProcess=(0,E.exec)(i,{maxBuffer:10*1024*1024,cwd:this.vaultPath},(s,n,r)=>{var o;if(this.currentSearchProcess=null,s){if(s.killed||s.signal==="SIGTERM"){t(new u("Search cancelled","execution_error"));return}let a=s;if(a.code===127||(o=a.message)!=null&&o.includes("not found")){t(new u(`QMD binary not found at: ${this.binaryPath}`,"not_found",r));return}if(r!=null&&r.includes("no collection")){t(new u(`Collection "${this.collectionName}" not found`,"no_collection",r));return}if(r!=null&&r.includes("no embeddings")||r!=null&&r.includes("embeddings not found")){t(new u("Embeddings not generated for this collection","no_embeddings",r));return}t(new u(a.message||"Unknown QMD error","execution_error",r));return}e({stdout:n,stderr:r})})})}async testConnection(){return this.queueCommand(async()=>{try{let i=this.buildBaseCommand("status"),{stdout:e,stderr:t}=await this.execCommand(i);return{success:!0,data:this.parseStatusOutput(e),stderr:t}}catch(i){let e=i;return{success:!1,error:e.message,stderr:e.stderr}}})}parseStatusOutput(i){let e=i.match(/Total:\s+(\d+)\s+files?\s+indexed/i),t=e?parseInt(e[1],10):0,s=i.match(/Vectors:\s+(\d+)\s+embedded/i),n=s?parseInt(s[1],10):0,r=i.includes(this.collectionName)||i.includes(`qmd://${this.collectionName}/`),o=i.toLowerCase().includes("no collections");return{collection:r?this.collectionName:"",path:this.vaultPath,fileCount:t,indexedCount:t,embeddingsCount:n,hasEmbeddings:n>0,hasCollection:r&&!o}}async ensureCollection(i){return this.queueCommand(async()=>{try{let e=`${this.buildBinaryWithIndex()} collection list`;try{let{stdout:n}=await this.execCommand(e);if(n.includes(this.collectionName)||n.includes(`qmd://${this.collectionName}/`))return{success:!0}}catch(n){}let t=`${this.buildBinaryWithIndex()} collection add "${this.vaultPath}" --name "${this.collectionName}" --mask "${i}"`,{stderr:s}=await this.execCommand(t);return{success:!0,stderr:s}}catch(e){let t=e;return{success:!1,error:t.message,stderr:t.stderr}}})}async updateIndex(){return this.queueCommand(async()=>{try{let i=this.buildBaseCommand("update"),{stderr:e}=await this.execCommand(i);return{success:!0,stderr:e}}catch(i){let e=i;return{success:!1,error:e.message,stderr:e.stderr}}})}async generateEmbeddings(i=!1){return this.queueCommand(async()=>{try{let e=this.buildBaseCommand("embed");i&&(e+=" -f");let{stderr:t}=await this.execCommand(e);return{success:!0,stderr:t}}catch(e){let t=e;return{success:!1,error:t.message,stderr:t.stderr}}})}parseSearchResults(i){return i.map(e=>{let t=e.file;if(t.startsWith("qmd://")){let n=t.substring(6),r=n.indexOf("/");r!==-1&&(t=n.substring(r+1))}let s=e.snippet;return s&&(s=s.replace(/@@ -\d+,\d+ @@\s*\(\d+ before, \d+ after\)\s*/g,"").trim()),{path:t,score:e.score,title:e.title,snippet:s,docid:e.docid}})}extractJsonArray(i){let e=i.indexOf("[");if(e===-1)return"[]";let t=0,s=e;for(let n=e;n{try{let e=i.replace(/"/g,'\\"'),t=this.buildBaseCommand("vsearch")+` "${e}" --json`,{stdout:s,stderr:n}=await this.execSearchCommand(t);try{let r=this.extractJsonArray(s),o=JSON.parse(r);return{success:!0,data:this.parseSearchResults(o),stderr:n}}catch(r){throw new u("Failed to parse search results","parse_error",s)}}catch(e){let t=e;return{success:!1,error:t.message,stderr:t.stderr}}})}async keywordSearch(i){return this.queueCommand(async()=>{try{let e=i.replace(/"/g,'\\"'),t=this.buildBaseCommand("search")+` "${e}" --json`,{stdout:s,stderr:n}=await this.execSearchCommand(t);try{let r=this.extractJsonArray(s),o=JSON.parse(r);return{success:!0,data:this.parseSearchResults(o),stderr:n}}catch(r){throw new u("Failed to parse search results","parse_error",s)}}catch(e){let t=e;return{success:!1,error:t.message,stderr:t.stderr}}})}async hasEmbeddings(){var e;let i=await this.testConnection();return i.success&&((e=i.data)==null?void 0:e.hasEmbeddings)===!0}getQueueLength(){return this.commandQueue.length}isBusy(){return this.isProcessing}};var g=require("obsidian"),b=class extends g.SuggestModal{constructor(e,t,s,n){super(e);this.currentQuery="";this.lastCompletedQuery="";this.lastResults=[];this.isSearching=!1;this.searchId=0;this.usedFallback=!1;this.searchError=null;this.progressBar=null;this.searchModeIndicator=null;this.qmd=t,this.settings=s,this.onResultSelect=n.onResultSelect,this.onSearchModeUsed=n.onSearchModeUsed,this.onError=n.onError,this.setPlaceholder("Search your vault with QMD..."),this.setInstructions([{command:"\u2191\u2193",purpose:"Navigate"},{command:"\u21B5",purpose:"Open file"},{command:"esc",purpose:"Close"}]),this.debouncedSearch=(0,g.debounce)(r=>this.performSearch(r),1e3,!1)}async onOpen(){await super.onOpen(),this.injectStatusElements();let e=this.getInputElement();e&&(e.maxLength=50)}injectStatusElements(){let e=this.containerEl.querySelector(".prompt-input-container");e&&(this.progressBar||(this.progressBar=createDiv({cls:"qmd-progress-container qmd-hidden"}),this.progressBar.createDiv({cls:"qmd-progress-bar"}).createDiv({cls:"qmd-progress-bar-fill"}),this.progressBar.createSpan({cls:"qmd-progress-text",text:"Searching..."}),e.insertAdjacentElement("afterend",this.progressBar)),this.searchModeIndicator||(this.searchModeIndicator=createDiv({cls:"qmd-search-mode-indicator qmd-hidden"}),e.appendChild(this.searchModeIndicator)))}showProgressBar(){var e;(e=this.progressBar)==null||e.removeClass("qmd-hidden")}hideProgressBar(){var e;(e=this.progressBar)==null||e.addClass("qmd-hidden")}updateSearchModeIndicator(e,t){if(!this.searchModeIndicator)return;let s=t?"keyword (fallback)":e;this.searchModeIndicator.textContent=s,this.searchModeIndicator.removeClass("qmd-hidden");let n=this.getInputElement();n&&n.addClass("qmd-input-with-pill")}hideSearchModeIndicator(){var t;(t=this.searchModeIndicator)==null||t.addClass("qmd-hidden");let e=this.getInputElement();e&&e.removeClass("qmd-input-with-pill")}getSuggestions(e){return this.currentQuery=e,!e||e.trim().length<2?(this.searchError=null,this.lastCompletedQuery="",this.lastResults=[],this.hideProgressBar(),this.hideSearchModeIndicator(),[]):(this.isSearching&&(this.qmd.abortSearch(),this.searchId++),e!==this.lastCompletedQuery&&this.debouncedSearch(e),this.searchError&&this.lastResults.length===0?[{path:"",score:0,title:`Error: ${this.searchError}`,snippet:"Check that QMD is installed and accessible. Use 'Test QMD' in settings.",searchMode:this.settings.defaultSearchMode,isFallback:!1}]:this.lastResults)}async performSearch(e){var s;if(e!==this.currentQuery)return;let t=++this.searchId;this.isSearching=!0,this.usedFallback=!1,this.searchError=null,this.showProgressBar();try{let n=[],r=this.settings.defaultSearchMode;if(r==="semantic"){let a=await this.qmd.semanticSearch(e);if(t!==this.searchId)return;if(a.success&&a.data){if(a.data.length>0||!this.settings.fallbackOnZeroResults)n=this.mapResults(a.data,"semantic",!1);else if(this.settings.fallbackOnZeroResults){let l=await this.qmd.keywordSearch(e);if(t!==this.searchId)return;l.success&&l.data&&(n=this.mapResults(l.data,"keyword",!0),this.usedFallback=!0,r="keyword")}}else if(this.settings.fallbackOnSemanticFailure){(s=a.error)!=null&&s.includes("embeddings")&&this.showEmbeddingsNotice();let l=await this.qmd.keywordSearch(e);if(t!==this.searchId)return;l.success&&l.data&&(n=this.mapResults(l.data,"keyword",!0),this.usedFallback=!0,r="keyword",new g.Notice("Semantic search unavailable \u2014 using keyword search."))}else this.searchError=a.error||"Search failed",this.onError(this.searchError)}else{let a=await this.qmd.keywordSearch(e);if(t!==this.searchId)return;a.success&&a.data?n=this.mapResults(a.data,"keyword",!1):(this.searchError=a.error||"Search failed",this.onError(this.searchError))}this.lastResults=n,this.lastCompletedQuery=e,this.isSearching=!1,this.hideProgressBar(),n.length>0?this.updateSearchModeIndicator(r,this.usedFallback):this.hideSearchModeIndicator(),this.onSearchModeUsed(r,this.usedFallback);let o=this.getInputElement();if(o&&o.value===e){let a=this.containerEl.querySelector(".suggestion-container"),l=(a==null?void 0:a.scrollTop)||0;o.dispatchEvent(new Event("input")),a&&l>0&&queueMicrotask(()=>{a.scrollTop=l})}}catch(n){if(t!==this.searchId)return;let r=n instanceof Error?n.message:"Unknown error";this.searchError=r,this.onError(r)}finally{t===this.searchId&&(this.isSearching=!1,this.hideProgressBar())}}toSlug(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}buildSlugMap(){let e=new Map;for(let t of this.app.vault.getFiles()){let s=this.toSlug(t.basename);e.has(s)||e.set(s,t);let n=this.toSlug(t.path.replace(/\.md$/i,""));e.has(n)||e.set(n,t)}return e}mapResults(e,t,s){let n=this.buildSlugMap();return e.map(r=>{let o;if(r.title&&(o=this.app.vault.getFiles().find(a=>a.basename===r.title)),!o){let a=this.toSlug(r.path.replace(/\.md$/i,""));o=n.get(a)}return{...r,file:o,searchMode:t,isFallback:s}})}showEmbeddingsNotice(){this.settings.showEmbeddingsBanner&&new g.Notice("Semantic search requires embeddings. Run 'QMD: Generate embeddings' to enable.",1e4)}renderSuggestion(e,t){var o;let s=e.path===""&&((o=e.title)==null?void 0:o.startsWith("Error:")),n=t.createDiv({cls:s?"qmd-search-error":"qmd-search-result"}),r=n.createDiv({cls:"qmd-search-result-title"});if(r.createSpan({text:e.title||e.path.replace(/\.md$/i,"").split("/").pop()||e.path,cls:s?"qmd-search-error-title":"qmd-search-result-name"}),!s&&this.settings.showScoresInResults){let a=Math.round(e.score*100);r.createSpan({text:` (Score: ${a}%)`,cls:"qmd-search-result-score"})}e.snippet&&n.createDiv({text:e.snippet,cls:s?"qmd-search-error-hint":"qmd-search-result-snippet"})}onChooseSuggestion(e,t){var s;e.path===""&&((s=e.title)!=null&&s.startsWith("Error:"))||this.onResultSelect(e)}getInputElement(){return this.containerEl.querySelector("input")}};var m=require("obsidian"),p="qmd-search-view",v=class extends m.ItemView{constructor(e,t,s,n){super(e);this.searchInput=null;this.resultsContainer=null;this.statusContainer=null;this.currentQuery="";this.isSearching=!1;this.qmd=t,this.settings=s,this.onResultSelect=n.onResultSelect,this.onSearchModeUsed=n.onSearchModeUsed,this.onError=n.onError,this.debouncedSearch=(0,m.debounce)(r=>{this.performSearch(r)},300,!0)}getViewType(){return p}getDisplayText(){return"QMD search"}getIcon(){return"search"}async onOpen(){await super.onOpen();let e=this.containerEl.children[1];e.empty(),e.addClass("qmd-search-pane");let s=e.createDiv({cls:"qmd-search-input-container"}).createDiv({cls:"qmd-search-input-wrapper"}),n=s.createSpan({cls:"qmd-search-icon"});(0,m.setIcon)(n,"search");let r=s.createEl("input",{type:"text",placeholder:"Search with QMD...",cls:"qmd-search-input"});r.addEventListener("input",o=>{let a=o.target.value;this.currentQuery=a,a.trim().length>=2?this.debouncedSearch(a):this.clearResults()}),r.addEventListener("keydown",o=>{o.key==="Enter"&&this.currentQuery.trim().length>=2&&this.performSearch(this.currentQuery)}),this.statusContainer=e.createDiv({cls:"qmd-search-status"}),this.resultsContainer=e.createDiv({cls:"qmd-search-results"})}async onClose(){}updateReferences(e,t){this.qmd=e,this.settings=t}async performSearch(e){var t;if(!this.isSearching&&!(!e||e.trim().length<2)){this.isSearching=!0,this.showStatus("Searching...","loading");try{let s=[],n=this.settings.defaultSearchMode,r=!1;if(n==="semantic"){let a=await this.qmd.semanticSearch(e);if(a.success&&a.data){if(a.data.length>0||!this.settings.fallbackOnZeroResults)s=a.data;else if(this.settings.fallbackOnZeroResults){let l=await this.qmd.keywordSearch(e);l.success&&l.data&&(s=l.data,r=!0,n="keyword")}}else if(this.settings.fallbackOnSemanticFailure){(t=a.error)!=null&&t.includes("embeddings")&&this.showEmbeddingsNotice();let l=await this.qmd.keywordSearch(e);l.success&&l.data&&(s=l.data,r=!0,n="keyword",new m.Notice("Semantic search unavailable \u2014 using keyword search."))}else throw new Error(a.error||"Search failed")}else{let a=await this.qmd.keywordSearch(e);if(a.success&&a.data)s=a.data;else throw new Error(a.error||"Search failed")}this.onSearchModeUsed(n,r),this.renderResults(s,n,r);let o=r?`${s.length} results (keyword fallback)`:`${s.length} results (${n})`;this.showStatus(o,"success")}catch(s){let n=s instanceof Error?s.message:"Unknown error";this.onError(n),this.showStatus(`Error: ${n}`,"error")}finally{this.isSearching=!1}}}showEmbeddingsNotice(){this.settings.showEmbeddingsBanner&&new m.Notice("Semantic search requires embeddings. Run 'QMD: Generate embeddings' to enable.",1e4)}renderResults(e,t,s){if(this.resultsContainer){if(this.resultsContainer.empty(),e.length===0){this.resultsContainer.createDiv({text:"No results found",cls:"qmd-search-no-results"});return}for(let n of e){let r=this.resultsContainer.createDiv({cls:"qmd-search-result-item"}),o=this.app.vault.getFiles().find(C=>C.path===n.path||C.path.endsWith(n.path)),a={...n,file:o,searchMode:t,isFallback:s};r.createDiv({cls:"qmd-result-title"}).createSpan({text:n.title||n.path.split("/").pop()||n.path}),n.title&&r.createDiv({text:n.path,cls:"qmd-result-path"}),n.snippet&&r.createDiv({text:n.snippet,cls:"qmd-result-snippet"}),this.settings.showScoresInResults&&r.createDiv({text:`Score: ${n.score.toFixed(3)}`,cls:"qmd-result-score"}),r.addEventListener("click",()=>{this.onResultSelect(a)})}}}clearResults(){this.resultsContainer&&this.resultsContainer.empty(),this.hideStatus()}showStatus(e,t){if(this.statusContainer){if(this.statusContainer.empty(),this.statusContainer.removeClass("qmd-status-loading","qmd-status-success","qmd-status-error"),this.statusContainer.addClass(`qmd-status-${t}`),t==="loading"){let s=this.statusContainer.createDiv({cls:"qmd-pane-loading-row"});s.createSpan({cls:"qmd-pane-spinner"}),s.createSpan({text:e})}else this.statusContainer.setText(e);this.statusContainer.show()}}hideStatus(){this.statusContainer&&this.statusContainer.hide()}};var c=require("obsidian"),S=class extends c.PluginSettingTab{constructor(i,e){super(i,e),this.plugin=e}display(){let{containerEl:i}=this;i.empty(),new c.Setting(i).setName("QMD configuration").setHeading(),new c.Setting(i).setName("QMD binary path").setDesc("Path to the QMD executable. Use 'qmd' if it's in your PATH.").addText(t=>t.setPlaceholder("qmd").setValue(this.plugin.settings.qmdBinaryPath).onChange(async s=>{this.plugin.settings.qmdBinaryPath=s||"qmd",await this.plugin.saveSettings()})),new c.Setting(i).setName("Collection name").setDesc("Name for the QMD collection. Leave empty to use vault name.").addText(t=>t.setPlaceholder("(derived from vault name)").setValue(this.plugin.settings.collectionName).onChange(async s=>{this.plugin.settings.collectionName=s,await this.plugin.saveSettings()})),new c.Setting(i).setName("Index name").setDesc("Optional QMD index name override. Leave empty for default.").addText(t=>t.setPlaceholder("(default)").setValue(this.plugin.settings.indexName||"").onChange(async s=>{this.plugin.settings.indexName=s||null,await this.plugin.saveSettings()})),new c.Setting(i).setName("File mask").setDesc("Glob pattern for markdown files to index.").addText(t=>t.setPlaceholder("**/*.md").setValue(this.plugin.settings.fileMask).onChange(async s=>{this.plugin.settings.fileMask=s||"**/*.md",await this.plugin.saveSettings()})),new c.Setting(i).setName("Test QMD connection").setDesc("Verify that QMD is installed and accessible.").addButton(t=>t.setButtonText("Test QMD").setCta().onClick(async()=>{t.setButtonText("Testing..."),t.setDisabled(!0);let s=await this.plugin.testQMDConnection();s.success?(new c.Notice("QMD is working correctly."),s.data&&new c.Notice(`Collection: ${s.data.collection} +Files: ${s.data.fileCount} +Embeddings: ${s.data.hasEmbeddings?"Yes":"No"}`)):new c.Notice(`QMD error: ${s.error}`,1e4),t.setButtonText("Test QMD"),t.setDisabled(!1)})),new c.Setting(i).setName("Indexing & updates").setHeading(),new c.Setting(i).setName("Debounce delay (ms)").setDesc("Wait time after file changes before updating index.").addSlider(t=>t.setLimits(1e3,12e4,1e3).setValue(this.plugin.settings.debounceMs).setDynamicTooltip().onChange(async s=>{this.plugin.settings.debounceMs=s,await this.plugin.saveSettings()})),new c.Setting(i).setName("Enable periodic updates").setDesc("Automatically run index updates on a timer.").addToggle(t=>t.setValue(this.plugin.settings.enablePeriodicUpdates).onChange(async s=>{this.plugin.settings.enablePeriodicUpdates=s,await this.plugin.saveSettings(),this.plugin.setupPeriodicUpdates()})),new c.Setting(i).setName("Periodic update interval (minutes)").setDesc("How often to run automatic index updates.").addSlider(t=>t.setLimits(5,120,5).setValue(this.plugin.settings.periodicUpdateMinutes).setDynamicTooltip().onChange(async s=>{this.plugin.settings.periodicUpdateMinutes=s,await this.plugin.saveSettings(),this.plugin.setupPeriodicUpdates()})),new c.Setting(i).setName("Search behavior").setHeading(),new c.Setting(i).setName("Default search mode").setDesc("Primary search method. Semantic uses AI embeddings, keyword uses BM25.").addDropdown(t=>t.addOption("semantic","Semantic (AI)").addOption("keyword","Keyword (BM25)").setValue(this.plugin.settings.defaultSearchMode).onChange(async s=>{this.plugin.settings.defaultSearchMode=s,await this.plugin.saveSettings()})),new c.Setting(i).setName("Fallback on semantic failure").setDesc("Use keyword search if semantic search fails or has no embeddings.").addToggle(t=>t.setValue(this.plugin.settings.fallbackOnSemanticFailure).onChange(async s=>{this.plugin.settings.fallbackOnSemanticFailure=s,await this.plugin.saveSettings()})),new c.Setting(i).setName("Fallback on zero results").setDesc("Use keyword search if semantic search returns no results.").addToggle(t=>t.setValue(this.plugin.settings.fallbackOnZeroResults).onChange(async s=>{this.plugin.settings.fallbackOnZeroResults=s,await this.plugin.saveSettings()})),new c.Setting(i).setName("Show embeddings banner").setDesc("Display a notice when semantic search is unavailable due to missing embeddings.").addToggle(t=>t.setValue(this.plugin.settings.showEmbeddingsBanner).onChange(async s=>{this.plugin.settings.showEmbeddingsBanner=s,await this.plugin.saveSettings()})),new c.Setting(i).setName("Auto-generate embeddings").setDesc("Automatically generate embeddings when missing. First run downloads ~3GB of local AI models.").addToggle(t=>t.setValue(this.plugin.settings.autoGenerateEmbeddings).onChange(async s=>{this.plugin.settings.autoGenerateEmbeddings=s,await this.plugin.saveSettings()})),new c.Setting(i).setName("User interface").setHeading(),new c.Setting(i).setName("Show ribbon icon").setDesc("Display QMD search icon in the left sidebar.").addToggle(t=>t.setValue(this.plugin.settings.enableRibbonIcon).onChange(async s=>{this.plugin.settings.enableRibbonIcon=s,await this.plugin.saveSettings(),this.plugin.setupRibbonIcon()})),new c.Setting(i).setName("Enable search pane").setDesc("Allow opening QMD search as a persistent sidebar pane.").addToggle(t=>t.setValue(this.plugin.settings.enableSearchPane).onChange(async s=>{this.plugin.settings.enableSearchPane=s,await this.plugin.saveSettings()})),new c.Setting(i).setName("Show scores in results").setDesc("Display numeric relevance scores in search results.").addToggle(t=>t.setValue(this.plugin.settings.showScoresInResults).onChange(async s=>{this.plugin.settings.showScoresInResults=s,await this.plugin.saveSettings()})),new c.Setting(i).setName("Diagnostics").setHeading();let e=i.createDiv({cls:"qmd-diagnostics"});this.renderDiagnostics(e),new c.Setting(i).setName("Actions").setHeading(),new c.Setting(i).setName("Update index now").setDesc("Manually trigger an index update.").addButton(t=>t.setButtonText("Update index").onClick(async()=>{t.setButtonText("Updating..."),t.setDisabled(!0),await this.plugin.updateIndexNow(),t.setButtonText("Update index"),t.setDisabled(!1)})),new c.Setting(i).setName("Generate embeddings").setDesc("Build AI embeddings for semantic search. This may take a while for large vaults.").addButton(t=>t.setButtonText("Generate embeddings").onClick(async()=>{t.setButtonText("Generating..."),t.setDisabled(!0),await this.plugin.generateEmbeddings(!1),t.setButtonText("Generate embeddings"),t.setDisabled(!1)})),new c.Setting(i).setName("Force rebuild embeddings").setDesc("Rebuild all embeddings from scratch. Use if embeddings seem corrupted.").addButton(t=>t.setButtonText("Force rebuild").setWarning().onClick(async()=>{t.setButtonText("Rebuilding..."),t.setDisabled(!0),await this.plugin.generateEmbeddings(!0),t.setButtonText("Force rebuild"),t.setDisabled(!1)})),new c.Setting(i).setName("Ensure collection exists").setDesc("Create the QMD collection if it doesn't exist.").addButton(t=>t.setButtonText("Ensure collection").onClick(async()=>{t.setButtonText("Checking..."),t.setDisabled(!0),await this.plugin.ensureCollection(),t.setButtonText("Ensure collection"),t.setDisabled(!1)}))}renderDiagnostics(i){i.empty();let e=this.plugin.settings,t=[{label:"Last index update",value:e.lastIndexUpdateTime||"Never"},{label:"Last embedding run",value:e.lastEmbeddingRunTime||"Never"},{label:"Last search mode",value:e.lastSearchMode||"N/A"},{label:"Last error",value:e.lastError||"None"}];for(let s of t){let n=i.createDiv({cls:"qmd-diagnostic-row"});n.createSpan({text:s.label+":",cls:"qmd-diagnostic-label"}),n.createSpan({text:s.value,cls:"qmd-diagnostic-value"})}}};var R=require("fs"),Q=require("os"),U=["qmd",`${(0,Q.homedir)()}/.bun/bin/qmd`,"/usr/local/bin/qmd","/opt/homebrew/bin/qmd","/usr/bin/qmd"],w=class extends d.Plugin{constructor(e,t){super(e,t);this.settings=y;this.qmd=null;this.ribbonIcon=null;this.periodicUpdateInterval=null;this.debouncedUpdate=null;this.hasPendingChanges=!1}async onload(){if(await this.loadSettings(),!this.isDesktop()){new d.Notice("QMD requires desktop filesystem access \u2014 plugin disabled on mobile.");return}this.initializeQMD(),this.registerView(p,e=>new v(e,this.qmd,this.settings,{onResultSelect:t=>{this.openSearchResult(t)},onSearchModeUsed:(t,s)=>{this.recordSearchMode(t,s)},onError:t=>{this.recordError(t)}})),this.registerCommands(),this.setupRibbonIcon(),this.setupFileWatchers(),this.setupPeriodicUpdates(),this.addSettingTab(new S(this.app,this)),this.ensureCollectionOnLoad()}onunload(){this.periodicUpdateInterval!==null&&window.clearInterval(this.periodicUpdateInterval)}isDesktop(){return typeof this.app.vault.adapter.getBasePath=="function"}getVaultPath(){return this.app.vault.adapter.getBasePath()}findQMDBinary(){if(this.settings.qmdBinaryPath!=="qmd")return this.settings.qmdBinaryPath;for(let e of U)if(e!=="qmd")try{if((0,R.existsSync)(e))return e}catch(t){}return this.settings.qmdBinaryPath}initializeQMD(){let e=this.getVaultPath(),t=this.settings.collectionName||D(this.app.vault.getName()),s=this.findQMDBinary();s!==this.settings.qmdBinaryPath&&(this.settings.qmdBinaryPath=s,this.saveSettings()),this.qmd=new f(s,t,this.settings.indexName,e)}registerCommands(){this.addCommand({id:"search",name:"Search",callback:()=>this.openSearchModal()}),this.addCommand({id:"open-search-pane",name:"Open search pane",checkCallback:e=>this.settings.enableSearchPane?(e||this.openSearchPane(),!0):!1}),this.addCommand({id:"update-index",name:"Update index now",callback:()=>{this.updateIndexNow()}}),this.addCommand({id:"generate-embeddings",name:"Generate embeddings",callback:()=>{this.generateEmbeddings(!1)}}),this.addCommand({id:"force-rebuild-embeddings",name:"Force rebuild embeddings",callback:()=>{this.generateEmbeddings(!0)}}),this.addCommand({id:"ensure-collection",name:"Ensure collection",callback:()=>{this.ensureCollection()}})}setupRibbonIcon(){this.ribbonIcon&&(this.ribbonIcon.remove(),this.ribbonIcon=null),this.settings.enableRibbonIcon&&(this.ribbonIcon=this.addRibbonIcon("search","QMD search",()=>this.openSearchModal()))}setupFileWatchers(){this.debouncedUpdate=(0,d.debounce)(()=>{this.triggerIndexUpdate()},this.settings.debounceMs,!0);let e=t=>{var s;t instanceof d.TFile&&t.extension==="md"&&(this.hasPendingChanges=!0,(s=this.debouncedUpdate)==null||s.call(this))};this.registerEvent(this.app.vault.on("create",e)),this.registerEvent(this.app.vault.on("modify",e)),this.registerEvent(this.app.vault.on("delete",e)),this.registerEvent(this.app.vault.on("rename",e))}setupPeriodicUpdates(){if(this.periodicUpdateInterval!==null&&(window.clearInterval(this.periodicUpdateInterval),this.periodicUpdateInterval=null),this.settings.enablePeriodicUpdates){let e=this.settings.periodicUpdateMinutes*60*1e3;this.periodicUpdateInterval=window.setInterval(()=>{this.triggerIndexUpdate()},e)}}async triggerIndexUpdate(){if(!this.qmd||!this.hasPendingChanges)return;this.hasPendingChanges=!1;let e=await this.qmd.updateIndex();if(e.success){this.settings.lastIndexUpdateTime=new Date().toISOString(),await this.saveSettings();let t=await this.qmd.generateEmbeddings(!1);t.success?(this.settings.lastEmbeddingRunTime=new Date().toISOString(),await this.saveSettings()):t.error&&this.recordError(t.error)}else e.error&&this.recordError(e.error)}async ensureCollectionOnLoad(){var t,s,n,r;if(!this.qmd)return;let e=await this.qmd.testConnection();if(!e.success){if((t=e.error)!=null&&t.toLowerCase().includes("not found")&&((s=e.error)!=null&&s.toLowerCase().includes("binary"))){new d.Notice("QMD binary not found. Install QMD and configure the path in settings.",15e3),this.recordError(e.error);return}this.recordError(e.error||"Unknown QMD error");return}(n=e.data)!=null&&n.hasCollection||await this.ensureCollection(),(r=e.data)!=null&&r.hasEmbeddings||await this.autoGenerateEmbeddingsIfNeeded()}async autoGenerateEmbeddingsIfNeeded(){var n;if(!this.qmd||!this.settings.autoGenerateEmbeddings)return;let e=await this.qmd.testConnection();if(e.success&&((n=e.data)!=null&&n.hasEmbeddings))return;let t=!this.settings.lastEmbeddingRunTime;t?new d.Notice("QMD: generating embeddings for the first time. This will download ~300MB of AI models and may take several minutes. Semantic search will be available when complete.",15e3):new d.Notice("Generating embeddings for semantic search...",5e3);let s=await this.qmd.generateEmbeddings(!1);s.success?(this.settings.lastEmbeddingRunTime=new Date().toISOString(),await this.saveSettings(),new d.Notice("Embeddings generated. Semantic search is now available.")):(t&&new d.Notice(`Failed to generate embeddings: ${s.error}. Check that QMD is properly installed and you have internet access for model download.`,15e3),this.recordError(s.error||"Failed to auto-generate embeddings"))}async loadSettings(){this.settings=Object.assign({},y,await this.loadData())}async saveSettings(){if(await this.saveData(this.settings),this.qmd){let e=this.settings.collectionName||D(this.app.vault.getName());this.qmd.updateConfig(this.settings.qmdBinaryPath,e,this.settings.indexName)}}async testQMDConnection(){return this.qmd?this.qmd.testConnection():{success:!1,error:"QMD not initialized"}}async updateIndexNow(){if(!this.qmd){new d.Notice("QMD not initialized");return}new d.Notice("Updating QMD index...");let e=await this.qmd.updateIndex();e.success?(this.settings.lastIndexUpdateTime=new Date().toISOString(),await this.saveSettings(),new d.Notice("QMD index updated successfully.")):(new d.Notice(`Failed to update index: ${e.error}`,1e4),this.recordError(e.error||"Unknown error"))}async generateEmbeddings(e){if(!this.qmd){new d.Notice("QMD not initialized");return}let t=e?"Rebuilding":"Generating";new d.Notice(`${t} embeddings... this may take a while.`);let s=await this.qmd.generateEmbeddings(e);s.success?(this.settings.lastEmbeddingRunTime=new Date().toISOString(),await this.saveSettings(),new d.Notice("Embeddings generated successfully.")):(new d.Notice(`Failed to generate embeddings: ${s.error}`,1e4),this.recordError(s.error||"Unknown error"))}async ensureCollection(){if(!this.qmd){new d.Notice("QMD not initialized");return}new d.Notice("Ensuring QMD collection exists...");let e=await this.qmd.ensureCollection(this.settings.fileMask);e.success?new d.Notice("QMD collection is ready."):(new d.Notice(`Failed to ensure collection: ${e.error}`,1e4),this.recordError(e.error||"Unknown error"))}openSearchModal(){if(!this.qmd){new d.Notice("QMD not initialized");return}new b(this.app,this.qmd,this.settings,{onResultSelect:t=>{this.openSearchResult(t)},onSearchModeUsed:(t,s)=>{this.recordSearchMode(t,s)},onError:t=>{this.recordError(t)}}).open()}async openSearchPane(){let e=this.app.workspace.getLeavesOfType(p);if(e.length>0)await this.app.workspace.revealLeaf(e[0]);else{let t=this.app.workspace.getRightLeaf(!1);t&&(await t.setViewState({type:p,active:!0}),await this.app.workspace.revealLeaf(t))}}async openSearchResult(e){if(e.file){let t=this.app.workspace.getLeaf(!1);await t.openFile(e.file),e.line!==void 0&&e.line>0&&setTimeout(()=>{let s=t.view;if(s&&"editor"in s){let n=s.editor,r=e.line-1;n.setCursor({line:r,ch:0}),n.scrollIntoView({from:{line:r},to:{line:r}})}},100)}else new d.Notice(`File not found: ${e.path}`)}async recordSearchMode(e,t){this.settings.lastSearchMode=e,await this.saveSettings()}async recordError(e){this.settings.lastError=`${new Date().toISOString()}: ${e}`,await this.saveSettings()}}; diff --git a/.obsidian/plugins/qmd-search/manifest.json b/.obsidian/plugins/qmd-search/manifest.json new file mode 100644 index 0000000..d9b7e6c --- /dev/null +++ b/.obsidian/plugins/qmd-search/manifest.json @@ -0,0 +1 @@ +{"id":"qmd-search","name":"QMD Semantic Search","version":"1.1.3","minAppVersion":"1.11.0","description":"Semantic-first search for your vault using QMD (Quick Markdown Search). Provides vector-based semantic search with keyword fallback.","author":"Oleksii Chekulaiev","authorUrl":"https://github.com/achekulaev","isDesktopOnly":true} \ No newline at end of file diff --git a/.obsidian/plugins/qmd-search/styles.css b/.obsidian/plugins/qmd-search/styles.css new file mode 100644 index 0000000..40536e3 --- /dev/null +++ b/.obsidian/plugins/qmd-search/styles.css @@ -0,0 +1,312 @@ +/** + * QMD Semantic Search - Obsidian Plugin Styles + * + * These styles are loaded by Obsidian automatically when the plugin is enabled. + */ + +/* Utility */ +.qmd-hidden { + display: none !important; +} + +/* Search Modal - Input */ +.qmd-input-with-pill { + padding-right: 120px !important; +} + +/* Search Modal - Results */ +.qmd-search-result { + padding: 8px 12px; +} + +.qmd-search-result-title { + display: flex; + align-items: baseline; + font-weight: 500; + margin-bottom: 2px; +} + +.qmd-search-result-name { + color: var(--text-normal); +} + +.qmd-search-result-path { + font-size: 0.85em; + color: var(--text-muted); + margin-bottom: 4px; +} + +.qmd-search-result-snippet { + font-size: 0.9em; + color: var(--text-muted); + margin-bottom: 4px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.qmd-search-result-score { + font-weight: normal; + font-style: italic; + font-size: 0.75em; + color: var(--text-faint); +} + +.qmd-search-result-meta { + display: flex; + gap: 12px; + font-size: 0.8em; + color: var(--text-faint); +} + +.qmd-search-result-mode { + padding: 1px 6px; + border-radius: 3px; + background: var(--background-modifier-hover); +} + +.qmd-mode-semantic { + color: var(--text-accent); +} + +.qmd-mode-keyword { + color: var(--text-muted); +} + +/* Search mode pill inside input field */ +.qmd-search-mode-indicator { + position: absolute; + right: 44px; + top: 50%; + transform: translateY(-50%); + padding: 2px 8px; + font-size: 0.75em; + color: rgba(255, 255, 255, 0.85); + background: rgba(var(--interactive-accent-rgb, 124, 77, 255), 0.5); + border-radius: 10px; + pointer-events: none; + white-space: nowrap; +} + +/* Search Modal - Error display */ +.qmd-search-error { + padding: 12px; + background: var(--background-modifier-error); + border-radius: 4px; + cursor: default; +} + +.qmd-search-error-title { + color: var(--text-error); + font-weight: 500; +} + +.qmd-search-error-hint { + font-size: 0.9em; + color: var(--text-muted); + margin-top: 4px; +} + +/* Search Modal - Progress bar */ +.qmd-progress-container { + display: flex; + align-items: center; + gap: 8px; + padding: 6px 12px; + background: var(--background-secondary); + border-bottom: 1px solid var(--background-modifier-border); +} + +.qmd-progress-bar { + flex: 1; + height: 3px; + background: var(--background-modifier-border); + border-radius: 2px; + overflow: hidden; +} + +.qmd-progress-bar-fill { + height: 100%; + width: 30%; + background: var(--text-accent); + border-radius: 2px; + animation: qmd-progress-slide 1s ease-in-out infinite; +} + +@keyframes qmd-progress-slide { + 0% { transform: translateX(-100%); } + 50% { transform: translateX(333%); } + 100% { transform: translateX(-100%); } +} + +.qmd-progress-text { + font-size: 0.8em; + color: var(--text-muted); + white-space: nowrap; +} + +/* Search Pane */ +.qmd-search-pane { + display: flex; + flex-direction: column; + height: 100%; + padding: 8px; +} + +.qmd-search-input-container { + margin-bottom: 8px; +} + +.qmd-search-input-wrapper { + display: flex; + align-items: center; + background: var(--background-modifier-form-field); + border: 1px solid var(--background-modifier-border); + border-radius: 4px; + padding: 4px 8px; +} + +.qmd-search-icon { + color: var(--text-muted); + margin-right: 8px; +} + +.qmd-search-input { + flex: 1; + border: none; + background: transparent; + outline: none; + color: var(--text-normal); +} + +.qmd-search-status { + padding: 4px 8px; + font-size: 0.85em; + margin-bottom: 8px; + border-radius: 4px; +} + +.qmd-status-loading { + color: var(--text-muted); + background: var(--background-modifier-hover); +} + +.qmd-status-success { + color: var(--text-success); +} + +.qmd-status-error { + color: var(--text-error); + background: var(--background-modifier-error); +} + +.qmd-search-results { + flex: 1; + overflow-y: auto; +} + +.qmd-search-result-item { + padding: 8px; + margin-bottom: 4px; + border-radius: 4px; + cursor: pointer; + background: var(--background-secondary); +} + +.qmd-search-result-item:hover { + background: var(--background-modifier-hover); +} + +.qmd-result-title { + font-weight: 500; + color: var(--text-normal); + margin-bottom: 2px; +} + +.qmd-result-path { + font-size: 0.85em; + color: var(--text-muted); + margin-bottom: 4px; +} + +.qmd-result-snippet { + font-size: 0.9em; + color: var(--text-muted); + margin-bottom: 4px; + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; +} + +.qmd-result-score { + font-size: 0.8em; + color: var(--text-faint); +} + +.qmd-search-no-results { + padding: 16px; + text-align: center; + color: var(--text-muted); +} + +/* Search Pane - Loading */ +.qmd-pane-loading-row { + display: flex; + align-items: center; + gap: 8px; +} + +.qmd-pane-spinner { + width: 12px; + height: 12px; + border: 2px solid var(--background-modifier-border); + border-top-color: var(--text-accent); + border-radius: 50%; + animation: qmd-pane-spin 0.8s linear infinite; +} + +@keyframes qmd-pane-spin { + to { transform: rotate(360deg); } +} + +/* Settings Tab */ +.qmd-diagnostics { + padding: 12px; + background: var(--background-secondary); + border-radius: 4px; + margin-bottom: 16px; +} + +.qmd-diagnostic-row { + display: flex; + justify-content: space-between; + padding: 4px 0; +} + +.qmd-diagnostic-label { + font-weight: 500; + color: var(--text-muted); +} + +.qmd-diagnostic-value { + color: var(--text-normal); + font-family: var(--font-monospace); + font-size: 0.9em; +} + +/* Loading spinner animation */ +@keyframes qmd-spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +.qmd-loading-spinner { + width: 16px; + height: 16px; + border: 2px solid var(--background-modifier-border); + border-top-color: var(--text-accent); + border-radius: 50%; + animation: qmd-spin 0.8s linear infinite; +} diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json index 8f770f8..999b26a 100644 --- a/.obsidian/workspace.json +++ b/.obsidian/workspace.json @@ -13,12 +13,12 @@ "state": { "type": "markdown", "state": { - "file": "TOOLS.md", + "file": "memory/2026-02-26.md", "mode": "source", "source": false }, "icon": "lucide-file", - "title": "TOOLS" + "title": "2026-02-26" } } ] @@ -95,7 +95,7 @@ "state": { "type": "backlink", "state": { - "file": "TOOLS.md", + "file": "memory/2026-02-26-alice-debug.md", "collapseAll": false, "extraContext": false, "sortOrder": "alphabetical", @@ -105,7 +105,7 @@ "unlinkedCollapsed": true }, "icon": "links-coming-in", - "title": "Backlinks for TOOLS" + "title": "Backlinks for 2026-02-26-alice-debug" } }, { @@ -114,12 +114,12 @@ "state": { "type": "outgoing-link", "state": { - "file": "TOOLS.md", + "file": "memory/2026-02-26-alice-debug.md", "linksCollapsed": false, "unlinkedCollapsed": true }, "icon": "links-going-out", - "title": "Outgoing links from TOOLS" + "title": "Outgoing links from 2026-02-26-alice-debug" } }, { @@ -157,16 +157,27 @@ "state": { "type": "outline", "state": { - "file": "TOOLS.md", + "file": "memory/2026-02-26-alice-debug.md", "followCursor": false, "showSearch": false, "searchQuery": "" }, "icon": "lucide-list", - "title": "Outline of TOOLS" + "title": "Outline of 2026-02-26-alice-debug" + } + }, + { + "id": "353be6f48ab39071", + "type": "leaf", + "state": { + "type": "git-view", + "state": {}, + "icon": "git-pull-request", + "title": "Source Control" } } - ] + ], + "currentTab": 5 } ], "direction": "horizontal", @@ -181,15 +192,26 @@ "daily-notes:Open today's daily note": false, "templates:Insert template": false, "command-palette:Open command palette": false, - "bases:Create new base": false + "bases:Create new base": false, + "obsidian-git:Open Git source control": false, + "obsidian42-brat:BRAT": false, + "qmd-search:QMD search": false } }, - "active": "1914b3cb53aaf523", + "active": "09c562b295be8994", "lastOpenFiles": [ - "MEMORY.md", - "USER.md", + "scripts/daily-digest.sh", + "memory/2026-02-26-blog-fix.md", + "memory/2026-02-26-blog-api-fix.md", + "memory/2026-02-26-digest-failed.md", + "scripts/skrybe", + "scripts/skrybe.swift", "AGENTS.md", + "HEARTBEAT.md", + "MEMORY.md", + "memory/2026-02-26-alice-debug.md", "TOOLS.md", + "USER.md", "Untitled 2.base", "Untitled 1.base", "Untitled.base" diff --git a/memory/2026-02-26.md b/memory/2026-02-26.md index a7e1a30..fe5a8d6 100644 --- a/memory/2026-02-26.md +++ b/memory/2026-02-26.md @@ -11,6 +11,30 @@ Morning: Gantt Board for hardening (test if complete), git commit workspace. Bui --- +## [2026-02-26 16:46] ✅ Created daily-digest.sh script + +### What was requested +Matt wanted a shell script to generate the daily digest reliably (to fix the broken cron job). + +### What was done +- Created `/Users/mattbruce/.openclaw/workspace/scripts/daily-digest.sh` +- Script handles JSON escaping properly using `jq` +- Uses temp files to avoid argument length issues +- Checks for existing digest before creating +- Generates proper format with emojis and `[Read more →](URL)` links +- Can be run manually: `./daily-digest.sh [YYYY-MM-DD]` + +### Usage +```bash +# Run for today +./scripts/daily-digest.sh + +# Run for specific date +./scripts/daily-digest.sh 2026-02-26 +``` + +--- + ## [2026-02-26 15:52] ✅ Fixed blog-backup skill to match mission-control-docs pattern ### What was requested diff --git a/scripts/skrybe b/scripts/skrybe new file mode 100755 index 0000000..5b243f1 Binary files /dev/null and b/scripts/skrybe differ diff --git a/scripts/skrybe.swift b/scripts/skrybe.swift new file mode 100644 index 0000000..5eab38b --- /dev/null +++ b/scripts/skrybe.swift @@ -0,0 +1,202 @@ +import Foundation +import AppKit +import CoreGraphics +import ApplicationServices + +enum SkrybeError: LocalizedError { + case invalidUsage(String) + case unsupportedSubcommand(String) + case unsupportedQuotesOption(String) + case apiURLInvalid + case apiUnreachable(String) + case invalidHTTPStatus(Int) + case invalidResponse + case pasteboardWriteFailed + case accessibilityPermissionMissing + case eventCreationFailed + + var errorDescription: String? { + switch self { + case .invalidUsage(let message): + return message + case .unsupportedSubcommand(let command): + return "Unsupported subcommand: \(command)" + case .unsupportedQuotesOption(let option): + return "Unsupported quotes option: \(option)" + case .apiURLInvalid: + return "Invalid quotes API URL." + case .apiUnreachable(let reason): + return "Unable to reach quotes API: \(reason)" + case .invalidHTTPStatus(let status): + return "Quotes API returned HTTP status \(status)." + case .invalidResponse: + return "Quotes API returned an invalid response format." + case .pasteboardWriteFailed: + return "Failed to write quote text to the macOS pasteboard." + case .accessibilityPermissionMissing: + return "Accessibility permission is required to simulate paste (Cmd+V). Enable it in System Settings > Privacy & Security > Accessibility." + case .eventCreationFailed: + return "Unable to create keyboard events for paste operation." + } + } +} + +struct QuoteResponse: Decodable { + let quote: String + let author: String +} + +struct QuoteService { + private let endpoint = "http://localhost:3001/quote" + + func fetchRandomQuote(timeout: TimeInterval = 5.0) throws -> QuoteResponse { + guard let url = URL(string: endpoint) else { + throw SkrybeError.apiURLInvalid + } + + var request = URLRequest(url: url) + request.httpMethod = "GET" + request.timeoutInterval = timeout + + let semaphore = DispatchSemaphore(value: 0) + var responseData: Data? + var response: URLResponse? + var requestError: Error? + + let task = URLSession.shared.dataTask(with: request) { data, urlResponse, error in + responseData = data + response = urlResponse + requestError = error + semaphore.signal() + } + + task.resume() + _ = semaphore.wait(timeout: .now() + timeout + 1.0) + + if let requestError { + throw SkrybeError.apiUnreachable(requestError.localizedDescription) + } + + guard let http = response as? HTTPURLResponse else { + throw SkrybeError.invalidResponse + } + + guard (200...299).contains(http.statusCode) else { + throw SkrybeError.invalidHTTPStatus(http.statusCode) + } + + guard let responseData else { + throw SkrybeError.invalidResponse + } + + do { + let decoded = try JSONDecoder().decode(QuoteResponse.self, from: responseData) + return decoded + } catch { + throw SkrybeError.invalidResponse + } + } +} + +struct QuotePaster { + func paste(_ text: String) throws { + let pasteboard = NSPasteboard.general + pasteboard.clearContents() + + guard pasteboard.setString(text, forType: .string) else { + throw SkrybeError.pasteboardWriteFailed + } + + guard AXIsProcessTrusted() else { + throw SkrybeError.accessibilityPermissionMissing + } + + try sendCommandV() + } + + private func sendCommandV() throws { + guard + let source = CGEventSource(stateID: .hidSystemState), + let keyDown = CGEvent(keyboardEventSource: source, virtualKey: 9, keyDown: true), // 9 = 'v' + let keyUp = CGEvent(keyboardEventSource: source, virtualKey: 9, keyDown: false) + else { + throw SkrybeError.eventCreationFailed + } + + keyDown.flags = .maskCommand + keyUp.flags = .maskCommand + + keyDown.post(tap: .cghidEventTap) + keyUp.post(tap: .cghidEventTap) + } +} + +struct SkrybeCLI { + private let quoteService = QuoteService() + private let quotePaster = QuotePaster() + + func run(arguments: [String]) throws { + guard arguments.count >= 2 else { + throw SkrybeError.invalidUsage(usage()) + } + + let command = arguments[1] + switch command { + case "quotes": + try runQuotes(arguments: Array(arguments.dropFirst(2))) + case "--help", "-h", "help": + print(usage()) + default: + throw SkrybeError.unsupportedSubcommand(command) + } + } + + private func runQuotes(arguments: [String]) throws { + guard let option = arguments.first else { + throw SkrybeError.invalidUsage(quotesUsage()) + } + + switch option { + case "--paste": + let quote = try quoteService.fetchRandomQuote() + let rendered = "\(quote.quote) — \(quote.author)" + try quotePaster.paste(rendered) + print("✅ Pasted quote at cursor position") + case "--list", "--add": + throw SkrybeError.invalidUsage("\(option) is not implemented yet. Use `skrybe quotes --paste`.") + case "--help", "-h", "help": + print(quotesUsage()) + default: + throw SkrybeError.unsupportedQuotesOption(option) + } + } + + private func usage() -> String { + return """ + Usage: + skrybe quotes --paste + skrybe quotes --list (planned) + skrybe quotes --add (planned) + """ + } + + private func quotesUsage() -> String { + return """ + Quotes subcommand: + skrybe quotes --paste Fetch random quote from localhost:3001/quote and paste at current cursor + skrybe quotes --list Planned + skrybe quotes --add Planned + """ + } +} + +do { + try SkrybeCLI().run(arguments: CommandLine.arguments) +} catch { + if let localized = error as? LocalizedError, let message = localized.errorDescription { + fputs("❌ \(message)\n", stderr) + } else { + fputs("❌ \(error.localizedDescription)\n", stderr) + } + exit(EXIT_FAILURE) +}