mirror of
https://github.com/svemagie/obsidian-micropub.git
synced 2026-05-14 19:38:50 +02:00
73a072e298
Sends evergreen-since as Micropub property so Indiekit writes it with the correct frontmatter key. Also fixes first-publish bug: falls back to today's date so the recentEvergreens collection filter never silently drops a newly promoted post. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
22 lines
31 KiB
JavaScript
22 lines
31 KiB
JavaScript
/*
|
|
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
|
if you want to view the source, please visit the github repository of this plugin
|
|
*/
|
|
|
|
"use strict";var Ct=Object.create;var $=Object.defineProperty;var Nt=Object.getOwnPropertyDescriptor;var $t=Object.getOwnPropertyNames;var Bt=Object.getPrototypeOf,Ft=Object.prototype.hasOwnProperty;var zt=(d,t)=>{for(var e in t)$(d,e,{get:t[e],enumerable:!0})},Tt=(d,t,e,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of $t(t))!Ft.call(d,i)&&i!==e&&$(d,i,{get:()=>t[i],enumerable:!(n=Nt(t,i))||n.enumerable});return d};var Ot=(d,t,e)=>(e=d!=null?Ct(Bt(d)):{},Tt(t||!d||!d.__esModule?$(e,"default",{value:d,enumerable:!0}):e,d)),It=d=>Tt($({},"__esModule",{value:!0}),d);var Gt={};zt(Gt,{default:()=>I});module.exports=It(Gt);var y=require("obsidian");var Et={micropubEndpoint:"",mediaEndpoint:"",accessToken:"",defaultSyndicateTo:[],autoDiscover:!1,siteUrl:"",authorizationEndpoint:"",tokenEndpoint:"",me:"",writeUrlToFrontmatter:!0,mapGardenTags:!0,defaultVisibility:"public",showSyndicationDialog:"when-needed"};var b=require("obsidian");var E=require("obsidian"),k=class{constructor(t,e,n){this.getEndpoint=t;this.getMediaEndpoint=e;this.getToken=n}async fetchConfig(){let t=`${this.getEndpoint()}?q=config`;return(await(0,E.requestUrl)({url:t,method:"GET",headers:this.authHeaders()})).json}async discoverEndpoints(t){let n=(await(0,E.requestUrl)({url:t,method:"GET"})).text,i=this.extractLinkRel(n,"micropub"),s=this.extractLinkRel(n,"token_endpoint"),r;if(i)try{r=(await this.fetchConfigFrom(i))["media-endpoint"]}catch(a){}return{micropubEndpoint:i,tokenEndpoint:s,mediaEndpoint:r}}async createPost(t){var n,i,s;let e={type:["h-entry"],properties:t};try{let r=await(0,E.requestUrl)({url:this.getEndpoint(),method:"POST",headers:{...this.authHeaders(),"Content-Type":"application/json"},body:JSON.stringify(e),throw:!1});if(r.status===201||r.status===202)return{success:!0,url:((n=r.headers)==null?void 0:n.location)||((i=r.headers)==null?void 0:i.Location)||((s=r.json)==null?void 0:s.url)};let a=this.extractError(r.text);return{success:!1,error:`HTTP ${r.status}: ${a}`}}catch(r){return{success:!1,error:String(r)}}}async updatePost(t,e){let n={action:"update",url:t,replace:e};try{let i=await(0,E.requestUrl)({url:this.getEndpoint(),method:"POST",headers:{...this.authHeaders(),"Content-Type":"application/json"},body:JSON.stringify(n),throw:!1});return i.status>=200&&i.status<300?{success:!0,url:t}:{success:!1,error:`HTTP ${i.status}: ${this.extractError(i.text)}`}}catch(i){return{success:!1,error:String(i)}}}async uploadMedia(t,e,n){var m,u,S;let i=this.getMediaEndpoint()||`${this.getEndpoint()}/media`,s=`----MicropubBoundary${Date.now()}`,r=`--${s}\r
|
|
Content-Disposition: form-data; name="file"; filename="${e}"\r
|
|
Content-Type: ${n}\r
|
|
\r
|
|
`,a=`\r
|
|
--${s}--\r
|
|
`,g=new TextEncoder().encode(r),l=new TextEncoder().encode(a),c=new Uint8Array(t),p=new Uint8Array(g.length+c.length+l.length);p.set(g,0),p.set(c,g.length),p.set(l,g.length+c.length);let h=await(0,E.requestUrl)({url:i,method:"POST",headers:{...this.authHeaders(),"Content-Type":`multipart/form-data; boundary=${s}`},body:p.buffer,throw:!1});if(h.status===201||h.status===202){let v=((m=h.headers)==null?void 0:m.location)||((u=h.headers)==null?void 0:u.Location)||((S=h.json)==null?void 0:S.url);if(v)return v}throw new Error(`Media upload failed (HTTP ${h.status}): ${this.extractError(h.text)}`)}authHeaders(){return{Authorization:`Bearer ${this.getToken()}`}}extractLinkRel(t,e){var s;let n=new RegExp(`<link[^>]+rel=["']${e}["'][^>]+href=["']([^"']+)["']|<link[^>]+href=["']([^"']+)["'][^>]+rel=["']${e}["']`,"i"),i=t.match(n);return(s=i==null?void 0:i[1])!=null?s:i==null?void 0:i[2]}async fetchConfigFrom(t){return(await(0,E.requestUrl)({url:`${t}?q=config`,method:"GET",headers:this.authHeaders()})).json}extractError(t){var e,n;try{let i=JSON.parse(t);return(n=(e=i.error_description)!=null?e:i.error)!=null?n:t.slice(0,200)}catch(i){return t.slice(0,200)}}};var M=Ot(require("crypto")),V=require("obsidian");var Pt={cmdPublish:"Publish to Micropub",cmdUpdate:"Update existing Micropub post",noticeOpenNote:"Open a Markdown note to publish.",noticeNoEndpoint:"\u26A0\uFE0F Micropub endpoint not configured. Open plugin settings to add it.",noticeNoToken:"\u26A0\uFE0F Access token not configured. Open plugin settings to add it.",noticePublishing:"Publishing\u2026",noticePublished:"\u2705 Published!",noticePublishFailed:"\u274C Publish failed: {error}",noticeError:"\u274C Error: {error}",noticeNoSyndTargets:"\u26A0\uFE0F Could not fetch syndication targets. Publishing without dialog.",settingsTitle:"Micropub Publisher",settingsAccount:"Account",settingsEndpoints:"Endpoints",settingsEndpointsHint:"These are filled automatically when you sign in. Only edit them manually if your server uses non-standard paths.",settingsPublishBehaviour:"Publish Behaviour",settingsDigitalGarden:"Digital Garden",settingMicropubEndpoint:"Micropub endpoint",settingMicropubEndpointDesc:"e.g. https://example.com/micropub",settingMicropubEndpointPlaceholder:"https://example.com/micropub",settingMediaEndpoint:"Media endpoint",settingMediaEndpointDesc:"For image uploads. Auto-discovered if blank.",settingMediaEndpointPlaceholder:"https://example.com/micropub/media",settingVisibility:"Default visibility",settingVisibilityDesc:"Applies when the note has no explicit visibility property.",visibilityPublic:"Public",visibilityUnlisted:"Unlisted",visibilityPrivate:"Private",settingWriteUrl:"Write URL back to note",settingWriteUrlDesc:"After publishing, store the post URL as `mp-url` in frontmatter. Subsequent publishes will update the existing post instead of creating a new one.",settingSyndDialog:"Syndication dialog",settingSyndDialogDesc:"When to show the cross-posting dialog before publishing. 'When needed' shows it only if the note has no mp-syndicate-to frontmatter.",syndDialogWhenNeeded:"When needed",syndDialogAlways:"Always",syndDialogNever:"Never",settingSyndDefaults:"Default syndication targets",settingSyndDefaultsNone:"None configured. Targets checked by default in the publish dialog.",btnClearDefaults:"Clear defaults",settingGardenTags:"Map #garden/* tags to gardenStage",settingGardenTagsDesc:"Obsidian tags like #garden/plant become a `garden-stage: plant` Micropub property. The blog renders these as growth stage badges at /garden/.",settingGardenStages:"Stages: plant \u{1F331} \xB7 cultivate \u{1F33F} \xB7 question \u2753 \xB7 repot \u{1FAB4} \xB7 revitalize \u2728 \xB7 revisit \u{1F504}",settingSiteUrl:"Site URL",settingSiteUrlDesc:"Your site's home page. Clicking Sign in opens your blog's login page in the browser \u2014 the same flow iA Writer uses.",settingSiteUrlPlaceholder:"https://example.com",btnSignIn:"Sign in",btnOpeningBrowser:"Opening browser\u2026",noticeEnterSiteUrl:"Enter your site URL first.",noticeSignedInAs:"\u2705 Signed in as {me}",noticeSignInFailed:"Sign-in failed: {error}",lblSignedIn:"Signed in",btnSignOut:"Sign out",manualTokenSummary:"Or paste a token manually",settingAccessToken:"Access token",settingAccessTokenDesc:"Bearer token from your Indiekit admin panel.",settingAccessTokenPlaceholder:"your-bearer-token",btnVerify:"Verify",noticeSetEndpointFirst:"Set the Micropub endpoint and token first.",noticeTokenValid:"\u2705 Token is valid!",noticeTokenCheckFailed:"Token check failed: {error}",syndDialogTitle:"Syndication targets",syndDialogSubtitle:"Choose where to cross-post this note.",btnCancel:"Cancel",btnPublish:"Publish",errSignInTimeout:"Sign-in timed out (5 min). Please try again."};var xt={cmdPublish:"An Micropub ver\xF6ffentlichen",cmdUpdate:"Bestehenden Micropub-Beitrag aktualisieren",noticeOpenNote:"\xD6ffne eine Markdown-Notiz zum Ver\xF6ffentlichen.",noticeNoEndpoint:"\u26A0\uFE0F Micropub-Endpunkt nicht konfiguriert. Bitte in den Plugin-Einstellungen eintragen.",noticeNoToken:"\u26A0\uFE0F Zugriffstoken nicht konfiguriert. Bitte in den Plugin-Einstellungen eintragen.",noticePublishing:"Wird ver\xF6ffentlicht\u2026",noticePublished:"\u2705 Ver\xF6ffentlicht!",noticePublishFailed:"\u274C Ver\xF6ffentlichung fehlgeschlagen: {error}",noticeError:"\u274C Fehler: {error}",noticeNoSyndTargets:"\u26A0\uFE0F Syndizierungsziele konnten nicht abgerufen werden. Ver\xF6ffentlichung ohne Dialog.",settingsTitle:"Micropub Publisher",settingsAccount:"Konto",settingsEndpoints:"Endpunkte",settingsEndpointsHint:"Diese werden beim Anmelden automatisch ausgef\xFCllt. Nur manuell bearbeiten, wenn der Server nicht standardm\xE4\xDFige Pfade verwendet.",settingsPublishBehaviour:"Ver\xF6ffentlichungsverhalten",settingsDigitalGarden:"Digitaler Garten",settingMicropubEndpoint:"Micropub-Endpunkt",settingMicropubEndpointDesc:"z. B. https://example.com/micropub",settingMicropubEndpointPlaceholder:"https://example.com/micropub",settingMediaEndpoint:"Medien-Endpunkt",settingMediaEndpointDesc:"F\xFCr Bild-Uploads. Wird automatisch ermittelt, wenn leer.",settingMediaEndpointPlaceholder:"https://example.com/micropub/media",settingVisibility:"Standard-Sichtbarkeit",settingVisibilityDesc:"Gilt, wenn die Notiz keine explizite Sichtbarkeits-Eigenschaft hat.",visibilityPublic:"\xD6ffentlich",visibilityUnlisted:"Nicht gelistet",visibilityPrivate:"Privat",settingWriteUrl:"URL zur\xFCck in Notiz schreiben",settingWriteUrlDesc:"Nach der Ver\xF6ffentlichung wird die Beitrags-URL als `mp-url` im Frontmatter gespeichert. Sp\xE4tere Ver\xF6ffentlichungen aktualisieren den Beitrag statt einen neuen zu erstellen.",settingSyndDialog:"Syndizierungsdialog",settingSyndDialogDesc:"Wann der Dialog zum Querverweis vor der Ver\xF6ffentlichung angezeigt wird. 'Bei Bedarf' zeigt ihn nur, wenn kein mp-syndicate-to im Frontmatter vorhanden ist.",syndDialogWhenNeeded:"Bei Bedarf",syndDialogAlways:"Immer",syndDialogNever:"Nie",settingSyndDefaults:"Standard-Syndizierungsziele",settingSyndDefaultsNone:"Keine konfiguriert. Im Ver\xF6ffentlichungsdialog standardm\xE4\xDFig aktivierte Ziele.",btnClearDefaults:"Standards l\xF6schen",settingGardenTags:"#garden/*-Tags zu gardenStage zuordnen",settingGardenTagsDesc:"Obsidian-Tags wie #garden/plant werden zur Micropub-Eigenschaft `garden-stage: plant`. Der Blog zeigt diese als Wachstumsstufen-Abzeichen unter /garden/ an.",settingGardenStages:"Stufen: plant \u{1F331} \xB7 cultivate \u{1F33F} \xB7 question \u2753 \xB7 repot \u{1FAB4} \xB7 revitalize \u2728 \xB7 revisit \u{1F504}",settingSiteUrl:"Website-URL",settingSiteUrlDesc:"Startseite deiner Website. Klick auf Anmelden \xF6ffnet die Login-Seite deines Blogs im Browser.",settingSiteUrlPlaceholder:"https://example.com",btnSignIn:"Anmelden",btnOpeningBrowser:"Browser wird ge\xF6ffnet\u2026",noticeEnterSiteUrl:"Bitte zuerst die Website-URL eingeben.",noticeSignedInAs:"\u2705 Angemeldet als {me}",noticeSignInFailed:"Anmeldung fehlgeschlagen: {error}",lblSignedIn:"Angemeldet",btnSignOut:"Abmelden",manualTokenSummary:"Oder Token manuell einf\xFCgen",settingAccessToken:"Zugriffstoken",settingAccessTokenDesc:"Bearer-Token aus deinem Indiekit-Adminbereich.",settingAccessTokenPlaceholder:"your-bearer-token",btnVerify:"Pr\xFCfen",noticeSetEndpointFirst:"Bitte zuerst Micropub-Endpunkt und Token eingeben.",noticeTokenValid:"\u2705 Token ist g\xFCltig!",noticeTokenCheckFailed:"Token-Pr\xFCfung fehlgeschlagen: {error}",syndDialogTitle:"Syndizierungsziele",syndDialogSubtitle:"Wo soll diese Notiz gleichzeitig ver\xF6ffentlicht werden?",btnCancel:"Abbrechen",btnPublish:"Ver\xF6ffentlichen",errSignInTimeout:"Anmeldung abgelaufen (5 Min.). Bitte erneut versuchen."};var G={en:Pt,de:xt};function o(d,t){var s,r,a,g,l;let e=((r=(s=window.moment)==null?void 0:s.locale())!=null?r:"en").split("-")[0],i=(l=(g=((a=G[e])!=null?a:G.en)[d])!=null?g:G.en[d])!=null?l:d;if(t)for(let[c,p]of Object.entries(t))i=i.split(`{${c}}`).join(p);return i}var Dt="https://svemagie.github.io/obsidian-micropub/",At="https://svemagie.github.io/obsidian-micropub/callback",Mt="create update media",Lt=300*1e3,A=null;function Ut(d){if(!A)return;let{resolve:t,state:e}=A;A=null,t(d)}var B=class d{static async discoverEndpoints(t){let n=(await(0,V.requestUrl)({url:t,method:"GET"})).text,i=d.extractLinkRel(n,"authorization_endpoint"),s=d.extractLinkRel(n,"token_endpoint"),r=d.extractLinkRel(n,"micropub");if(!i)throw new Error(`No <link rel="authorization_endpoint"> found at ${t}. Make sure Indiekit is running and SITE_URL is set correctly.`);if(!s)throw new Error(`No <link rel="token_endpoint"> found at ${t}.`);return{authorizationEndpoint:i,tokenEndpoint:s,micropubEndpoint:r}}static async signIn(t){var u,S,v,P,R,x;let{authorizationEndpoint:e,tokenEndpoint:n,micropubEndpoint:i}=await d.discoverEndpoints(t),s=d.base64url(M.randomBytes(16)),r=d.base64url(M.randomBytes(64)),a=d.base64url(M.createHash("sha256").update(r).digest()),g=new Promise((C,D)=>{let T=setTimeout(()=>{A=null,D(new Error(o("errSignInTimeout")))},Lt);A={state:s,resolve:N=>{clearTimeout(T),C(N)}}}),l=new URL(e);l.searchParams.set("response_type","code"),l.searchParams.set("client_id",Dt),l.searchParams.set("redirect_uri",At),l.searchParams.set("state",s),l.searchParams.set("code_challenge",a),l.searchParams.set("code_challenge_method","S256"),l.searchParams.set("scope",Mt),l.searchParams.set("me",t),window.open(l.toString());let c=await g;if(c.state!==s)throw new Error("State mismatch \u2014 possible CSRF attack. Please try again.");let p=c.code;if(!p)throw new Error((S=(u=c.error_description)!=null?u:c.error)!=null?S:"No authorization code received.");let h=await(0,V.requestUrl)({url:n,method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"},body:new URLSearchParams({grant_type:"authorization_code",code:p,client_id:Dt,redirect_uri:At,code_verifier:r}).toString(),throw:!1}),m=h.json;if(!m.access_token)throw new Error((P=(v=m.error_description)!=null?v:m.error)!=null?P:`Token exchange failed (HTTP ${h.status})`);return{accessToken:m.access_token,scope:(R=m.scope)!=null?R:Mt,me:(x=m.me)!=null?x:t,authorizationEndpoint:e,tokenEndpoint:n,micropubEndpoint:i}}static base64url(t){return t.toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}static extractLinkRel(t,e){var s;let n=new RegExp(`<link[^>]+rel=["'][^"']*\\b${e}\\b[^"']*["'][^>]+href=["']([^"']+)["']|<link[^>]+href=["']([^"']+)["'][^>]+rel=["'][^"']*\\b${e}\\b[^"']*["']`,"i"),i=t.match(n);return(s=i==null?void 0:i[1])!=null?s:i==null?void 0:i[2]}};var F=class extends b.PluginSettingTab{constructor(e,n){super(e,n);this.plugin=n}display(){let{containerEl:e}=this;e.empty(),e.createEl("h2",{text:o("settingsTitle")}),e.createEl("h3",{text:o("settingsAccount")}),this.plugin.settings.me&&this.plugin.settings.accessToken?this.renderSignedIn(e):this.renderSignedOut(e),e.createEl("h3",{text:o("settingsEndpoints")}),e.createEl("p",{text:o("settingsEndpointsHint"),cls:"setting-item-description"}),new b.Setting(e).setName(o("settingMicropubEndpoint")).setDesc(o("settingMicropubEndpointDesc")).addText(s=>s.setPlaceholder(o("settingMicropubEndpointPlaceholder")).setValue(this.plugin.settings.micropubEndpoint).onChange(async r=>{this.plugin.settings.micropubEndpoint=r.trim(),await this.plugin.saveSettings()})),new b.Setting(e).setName(o("settingMediaEndpoint")).setDesc(o("settingMediaEndpointDesc")).addText(s=>s.setPlaceholder(o("settingMediaEndpointPlaceholder")).setValue(this.plugin.settings.mediaEndpoint).onChange(async r=>{this.plugin.settings.mediaEndpoint=r.trim(),await this.plugin.saveSettings()})),e.createEl("h3",{text:o("settingsPublishBehaviour")}),new b.Setting(e).setName(o("settingVisibility")).setDesc(o("settingVisibilityDesc")).addDropdown(s=>s.addOption("public",o("visibilityPublic")).addOption("unlisted",o("visibilityUnlisted")).addOption("private",o("visibilityPrivate")).setValue(this.plugin.settings.defaultVisibility).onChange(async r=>{this.plugin.settings.defaultVisibility=r,await this.plugin.saveSettings()})),new b.Setting(e).setName(o("settingWriteUrl")).setDesc(o("settingWriteUrlDesc")).addToggle(s=>s.setValue(this.plugin.settings.writeUrlToFrontmatter).onChange(async r=>{this.plugin.settings.writeUrlToFrontmatter=r,await this.plugin.saveSettings()})),new b.Setting(e).setName(o("settingSyndDialog")).setDesc(o("settingSyndDialogDesc")).addDropdown(s=>s.addOption("when-needed",o("syndDialogWhenNeeded")).addOption("always",o("syndDialogAlways")).addOption("never",o("syndDialogNever")).setValue(this.plugin.settings.showSyndicationDialog).onChange(async r=>{this.plugin.settings.showSyndicationDialog=r,await this.plugin.saveSettings()}));let n=this.plugin.settings.defaultSyndicateTo,i=new b.Setting(e).setName(o("settingSyndDefaults")).setDesc(n.length>0?n.join(", "):o("settingSyndDefaultsNone"));n.length>0&&i.addButton(s=>s.setButtonText(o("btnClearDefaults")).setWarning().onClick(async()=>{this.plugin.settings.defaultSyndicateTo=[],await this.plugin.saveSettings(),this.display()})),e.createEl("h3",{text:o("settingsDigitalGarden")}),new b.Setting(e).setName(o("settingGardenTags")).setDesc(o("settingGardenTagsDesc")).addToggle(s=>s.setValue(this.plugin.settings.mapGardenTags).onChange(async r=>{this.plugin.settings.mapGardenTags=r,await this.plugin.saveSettings()})),e.createEl("p",{text:o("settingGardenStages"),cls:"setting-item-description"})}renderSignedOut(e){new b.Setting(e).setName(o("settingSiteUrl")).setDesc(o("settingSiteUrlDesc")).addText(i=>i.setPlaceholder(o("settingSiteUrlPlaceholder")).setValue(this.plugin.settings.siteUrl).onChange(async s=>{this.plugin.settings.siteUrl=s.trim(),await this.plugin.saveSettings()})).addButton(i=>{i.setButtonText(o("btnSignIn")).setCta().onClick(async()=>{let s=this.plugin.settings.siteUrl.trim();if(!s){new b.Notice(o("noticeEnterSiteUrl"));return}i.setDisabled(!0),i.setButtonText(o("btnOpeningBrowser"));try{let r=await B.signIn(s);if(this.plugin.settings.accessToken=r.accessToken,this.plugin.settings.me=r.me,this.plugin.settings.authorizationEndpoint=r.authorizationEndpoint,this.plugin.settings.tokenEndpoint=r.tokenEndpoint,r.micropubEndpoint&&(this.plugin.settings.micropubEndpoint=r.micropubEndpoint),r.mediaEndpoint&&(this.plugin.settings.mediaEndpoint=r.mediaEndpoint),await this.plugin.saveSettings(),!this.plugin.settings.mediaEndpoint)try{let g=await new k(()=>this.plugin.settings.micropubEndpoint,()=>this.plugin.settings.mediaEndpoint,()=>this.plugin.settings.accessToken).fetchConfig();g["media-endpoint"]&&(this.plugin.settings.mediaEndpoint=g["media-endpoint"],await this.plugin.saveSettings())}catch(a){}new b.Notice(o("noticeSignedInAs",{me:r.me})),this.display()}catch(r){new b.Notice(o("noticeSignInFailed",{error:String(r)}),8e3),i.setDisabled(!1),i.setButtonText(o("btnSignIn"))}})});let n=e.createEl("details");n.createEl("summary",{text:o("manualTokenSummary"),cls:"setting-item-description"}),n.style.marginTop="8px",n.style.marginBottom="8px",new b.Setting(n).setName(o("settingAccessToken")).setDesc(o("settingAccessTokenDesc")).addText(i=>{i.setPlaceholder(o("settingAccessTokenPlaceholder")).setValue(this.plugin.settings.accessToken).onChange(async s=>{this.plugin.settings.accessToken=s.trim(),await this.plugin.saveSettings()}),i.inputEl.type="password"}).addButton(i=>i.setButtonText(o("btnVerify")).onClick(async()=>{if(!this.plugin.settings.micropubEndpoint||!this.plugin.settings.accessToken){new b.Notice(o("noticeSetEndpointFirst"));return}i.setDisabled(!0);try{await new k(()=>this.plugin.settings.micropubEndpoint,()=>this.plugin.settings.mediaEndpoint,()=>this.plugin.settings.accessToken).fetchConfig(),new b.Notice(o("noticeTokenValid"))}catch(s){new b.Notice(o("noticeTokenCheckFailed",{error:String(s)}))}finally{i.setDisabled(!1)}}))}renderSignedIn(e){let n=this.plugin.settings.me,i=e.createDiv({cls:"micropub-auth-banner"});i.style.cssText="display:flex;align-items:center;gap:12px;padding:12px 16px;border:1px solid var(--background-modifier-border);border-radius:8px;margin-bottom:16px;background:var(--background-secondary);";let s=i.createDiv();s.style.cssText="width:40px;height:40px;border-radius:50%;background:var(--interactive-accent);display:flex;align-items:center;justify-content:center;font-size:1.2rem;flex-shrink:0;",s.textContent="\u{1F310}";let r=i.createDiv();r.createEl("div",{text:o("lblSignedIn"),attr:{style:"font-size:.75rem;color:var(--text-muted);margin-bottom:2px"}}),r.createEl("div",{text:n,attr:{style:"font-weight:500;word-break:break-all"}}),new b.Setting(e).setName(o("settingSiteUrl")).addText(a=>a.setValue(this.plugin.settings.siteUrl).setDisabled(!0)).addButton(a=>a.setButtonText(o("btnSignOut")).setWarning().onClick(async()=>{this.plugin.settings.accessToken="",this.plugin.settings.me="",this.plugin.settings.authorizationEndpoint="",this.plugin.settings.tokenEndpoint="",await this.plugin.saveSettings(),this.display()}))}};var Rt=require("obsidian");var W="garden/",z=class{constructor(t,e){this.app=t;this.settings=e;this.client=new k(()=>e.micropubEndpoint,()=>e.mediaEndpoint,()=>e.accessToken)}async publish(t,e){let n=await this.app.vault.read(t),{frontmatter:i,body:s}=this.parseFrontmatter(n),r=i["mp-url"]!=null?String(i["mp-url"]):i.url!=null?String(i.url):void 0,{content:a,uploadedUrls:g}=await this.processImages(s),l=this.resolveWikilinks(a,t.path),c=this.buildProperties(i,l,g,t.basename,t.path,e),p;if(r){let h={};for(let[m,u]of Object.entries(c))h[m]=Array.isArray(u)?u:[u];p=await this.client.updatePost(r,h)}else p=await this.client.createPost(c);return p.success&&this.settings.writeUrlToFrontmatter&&(p.url?await this.writeUrlToNote(t,n,p.url,e):e!==void 0&&await this.writeSyndicateToNote(t,n,e)),p}buildProperties(t,e,n,i,s,r){var J,K,Z,Q,X,tt,et,it,nt,st,rt,ot,at,lt,ct,dt,gt,pt,ut,ht,mt,ft,bt,yt,St,wt,vt,kt;let a={},g=e.trim(),l=(J=t.bookmarkOf)!=null?J:t["bookmark-of"],c=(K=t.likeOf)!=null?K:t["like-of"],p=(Z=t.inReplyTo)!=null?Z:t["in-reply-to"],h=(Q=t.repostOf)!=null?Q:t["repost-of"];l&&(a["bookmark-of"]=[String(l)]),c&&(a["like-of"]=[String(c)]),p&&(a["in-reply-to"]=[String(p)]),h&&(a["repost-of"]=[String(h)]),(c||h)&&!g||(a.content=[g]);let u=(et=(tt=(X=t.postType)!=null?X:t.posttype)!=null?tt:t["post-type"])!=null?et:t.type;if(u==="article"||!u&&!!((it=t.title)!=null?it:t.name)){let f=(st=(nt=t.title)!=null?nt:t.name)!=null?st:i;a.name=[String(f)]}((rt=t.summary)!=null?rt:t.excerpt)&&(a.summary=[String((ot=t.summary)!=null?ot:t.excerpt)]);let v=(at=t.created)!=null?at:t.date;if(v){let f=String(v),w;if(/^\d{4}-\d{2}-\d{2}$/.test(f)){let L=new Date;w=new Date(`${f}T${String(L.getHours()).padStart(2,"0")}:${String(L.getMinutes()).padStart(2,"0")}:${String(L.getSeconds()).padStart(2,"0")}`)}else w=new Date(f);a.published=[w.toISOString()]}let P=[...this.resolveArray(t.tags),...this.resolveArray(t.category)],R=this.extractGardenStage(P),x=P.filter(f=>!f.startsWith(W)&&f!=="garden");if(x.length>0&&(a.category=[...new Set(x)]),this.settings.mapGardenTags){let f=(lt=t.gardenStage)!=null?lt:R;if(f&&(a.gardenStage=[f],f==="evergreen")){let w=(ct=t["evergreen-since"])!=null?ct:new Date().toISOString().slice(0,10);a["evergreen-since"]=[w]}}let C=r!==void 0?r:[...new Set([...this.settings.defaultSyndicateTo,...this.resolveArray((dt=t["mp-syndicate-to"])!=null?dt:t.mpSyndicateTo)])];C.length>0&&(a["mp-syndicate-to"]=C);let D=(gt=t.visibility)!=null?gt:this.settings.defaultVisibility;D&&D!=="public"&&(a.visibility=[D]),a["post-status"]=[(pt=t["post-status"])!=null?pt:"draft"];let T=t.ai&&typeof t.ai=="object"?t.ai:{},N=(ht=(ut=t["ai-text-level"])!=null?ut:t.aiTextLevel)!=null?ht:T.textLevel,j=(ft=(mt=t["ai-code-level"])!=null?mt:t.aiCodeLevel)!=null?ft:T.codeLevel,_=(St=(yt=(bt=t["ai-tools"])!=null?bt:t.aiTools)!=null?yt:T.aiTools)!=null?St:T.tools,H=(kt=(vt=(wt=t["ai-description"])!=null?wt:t.aiDescription)!=null?vt:T.aiDescription)!=null?kt:T.description;N!=null&&(a["ai-text-level"]=[String(N)]),j!=null&&(a["ai-code-level"]=[String(j)]),_!=null&&(a["ai-tools"]=[String(_)]),H!=null&&(a["ai-description"]=[String(H)]);let q=this.resolvePhotoArray(t.photo);q.length>0&&(a.photo=q);let Y=this.resolveArray(t.related);if(Y.length>0){let f=Y.map(w=>this.resolveWikilinkToUrl(w,s)).filter(w=>w!==null);f.length>0&&(a.related=f)}for(let[f,w]of Object.entries(t))f.startsWith("mp-")&&f!=="mp-url"&&f!=="mp-syndicate-to"&&(a[f]=this.resolveArray(w));return a}resolvePhotoArray(t){return t?(Array.isArray(t)?t:[t]).map(n=>{var i,s;if(typeof n=="string")return{value:n};if(typeof n=="object"&&n!==null){let r=n,a=String((s=(i=r.url)!=null?i:r.value)!=null?s:"");return a?r.alt?{value:a,alt:String(r.alt)}:{value:a}:null}return null}).filter(n=>n!==null):[]}extractGardenStage(t){for(let e of t){let n=e.replace(/^#/,"");if(n.startsWith(W)){let i=n.slice(W.length);if(["plant","cultivate","evergreen","question","repot","revitalize","revisit"].includes(i))return i}}}async processImages(t){let e=[],n=/!\[\[([^\]]+\.(png|jpg|jpeg|gif|webp|svg))\]\]/gi,i=/!\[([^\]]*)\]\(([^)]+\.(png|jpg|jpeg|gif|webp|svg))\)/gi,s=t,r=[...t.matchAll(n)];for(let g of r){let l=g[1];try{let c=await this.uploadLocalFile(l);c&&(e.push(c),s=s.replace(g[0],``))}catch(c){console.warn(`[micropub] Failed to upload ${l}:`,c)}}let a=[...s.matchAll(i)];for(let g of a){let l=g[1],c=g[2];if(!c.startsWith("http"))try{let p=await this.uploadLocalFile(c);p&&(e.push(p),s=s.replace(g[0],``))}catch(p){console.warn(`[micropub] Failed to upload ${c}:`,p)}}return{content:s,uploadedUrls:e}}async uploadLocalFile(t){let e=this.app.vault.getFiles().find(s=>s.name===t||s.path===t);if(!e)return;let n=await this.app.vault.readBinary(e),i=this.guessMimeType(e.extension);return this.client.uploadMedia(n,e.name,i)}parseFrontmatter(t){var i;let e=t.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);if(!e)return{frontmatter:{},body:t};let n={};try{n=(i=(0,Rt.parseYaml)(e[1]))!=null?i:{}}catch(s){}return{frontmatter:n,body:e[2]}}async writeUrlToNote(t,e,n,i){var m;let s=new Date,r=[s.getFullYear(),String(s.getMonth()+1).padStart(2,"0"),String(s.getDate()).padStart(2,"0")].join("-")+"T"+String(s.getHours()).padStart(2,"0")+":"+String(s.getMinutes()).padStart(2,"0")+":"+String(s.getSeconds()).padStart(2,"0"),{frontmatter:a}=this.parseFrontmatter(e),g=a["post-status"]!=="published",l=[["mp-url",`"${n}"`],["published",r]];if(g||l.push(["post-status","published"]),i!==void 0&&l.push(["mp-syndicate-to",`[${i.join(", ")}]`]),this.settings.siteUrl)try{let u=new URL(this.settings.siteUrl).hostname.replace(/^www\./,"");l.push(["medium",`"[[${u}]]"`])}catch(u){}if(!a["evergreen-since"]){let u=[...this.resolveArray(a.tags),...this.resolveArray(a.category)];((m=a.gardenStage)!=null?m:this.extractGardenStage(u))==="evergreen"&&l.push(["evergreen-since",r])}let c=e.match(/^(---\r?\n[\s\S]*?\r?\n---\r?\n)([\s\S]*)$/);if(!c){let u=l.map(([S,v])=>`${S}: ${v}`).join(`
|
|
`);await this.app.vault.modify(t,`---
|
|
${u}
|
|
---
|
|
`+e);return}let p=c[1],h=c[2];for(let[u,S]of l)p=this.setFrontmatterField(p,u,S);await this.app.vault.modify(t,p+h)}async writeSyndicateToNote(t,e,n){let i=e.match(/^(---\r?\n[\s\S]*?\r?\n---\r?\n)([\s\S]*)$/),s=`[${n.join(", ")}]`;if(!i){await this.app.vault.modify(t,`---
|
|
mp-syndicate-to: ${s}
|
|
---
|
|
`+e);return}let r=this.setFrontmatterField(i[1],"mp-syndicate-to",s);await this.app.vault.modify(t,r+i[2])}setFrontmatterField(t,e,n){let i=new RegExp(`^${e}:.*$`,"m");return i.test(t)?t.replace(i,`${e}: ${n}`):t.replace(/(\r?\n---\r?\n)$/,`
|
|
${e}: ${n}$1`)}resolveWikilinks(t,e){return t.replace(/(?<!!)\[\[([^\]|#]+)(#[^\]|]*)?\|?([^\]]*)\]\]/g,(n,i,s,r)=>{let a=i.trim(),g=(r==null?void 0:r.trim())||a.split("/").pop()||a,l=this.resolveWikilinkToUrl(`[[${a}]]`,e);if(!l)return g;let c=s?s.toLowerCase().replace(/\s+/g,"-"):"";return`[${g}](${l}${c})`})}resolveWikilinkToUrl(t,e){var s,r,a;if(t.startsWith("http"))return t;let n=t.match(/^\[\[([^\]|#]+)(?:#[^\]|]*)?\|?[^\]]*\]\]$/);if(!n)return null;let i=this.app.metadataCache.getFirstLinkpathDest(n[1].trim(),e);return i&&(a=(r=(s=this.app.metadataCache.getFileCache(i))==null?void 0:s.frontmatter)==null?void 0:r["mp-url"])!=null?a:null}resolveArray(t){return t?Array.isArray(t)?t.map(String):[String(t)]:[]}guessMimeType(t){var n;return(n={png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",webp:"image/webp",svg:"image/svg+xml"}[t.toLowerCase()])!=null?n:"application/octet-stream"}};var U=require("obsidian");var O=class extends U.Modal{constructor(e,n,i){super(e);this.targets=n;this.resolvePromise=null;this.resolved=!1;this.selected=new Set(i.filter(s=>n.some(r=>r.uid===s)))}async awaitSelection(){return new Promise(e=>{this.resolvePromise=e,this.open()})}onOpen(){let{contentEl:e}=this;e.createEl("h2",{text:o("syndDialogTitle")}),e.createEl("p",{text:o("syndDialogSubtitle"),cls:"setting-item-description"});for(let n of this.targets)new U.Setting(e).setName(n.name).addToggle(i=>i.setValue(this.selected.has(n.uid)).onChange(s=>{s?this.selected.add(n.uid):this.selected.delete(n.uid)}));new U.Setting(e).addButton(n=>n.setButtonText(o("btnCancel")).onClick(()=>{this.finish(null)})).addButton(n=>n.setButtonText(o("btnPublish")).setCta().onClick(()=>{this.finish([...this.selected])}))}onClose(){this.finish(null),this.contentEl.empty()}finish(e){var n;this.resolved||(this.resolved=!0,(n=this.resolvePromise)==null||n.call(this,e),this.resolvePromise=null)}};var I=class extends y.Plugin{async onload(){await this.loadSettings(),this.addCommand({id:"publish-to-micropub",name:o("cmdPublish"),checkCallback:t=>{let e=this.app.workspace.getActiveFile();return!e||e.extension!=="md"?!1:(t||this.publishActiveNote(e),!0)}}),this.addCommand({id:"publish-to-micropub-update",name:o("cmdUpdate"),checkCallback:t=>{let e=this.app.workspace.getActiveFile();return!e||e.extension!=="md"?!1:(t||this.publishActiveNote(e),!0)}}),this.registerObsidianProtocolHandler("micropub-auth",t=>{Ut(t)}),this.addSettingTab(new F(this.app,this)),this.addRibbonIcon("send",o("cmdPublish"),()=>{let t=this.app.workspace.getActiveFile();if(!t||t.extension!=="md"){new y.Notice(o("noticeOpenNote"));return}this.publishActiveNote(t)})}onunload(){}async publishActiveNote(t){var i;if(!this.settings.micropubEndpoint){new y.Notice(o("noticeNoEndpoint"));return}if(!this.settings.accessToken){new y.Notice(o("noticeNoToken"));return}let e=await this.resolveSyndicationTargets(t);if(e===null)return;let n=new y.Notice(o("noticePublishing"),0);try{let r=await new z(this.app,this.settings).publish(t,e);if(n.hide(),r.success){let a=r.url?`
|
|
${r.url}`:"";new y.Notice(`${o("noticePublished")}${a}`,8e3)}else new y.Notice(o("noticePublishFailed",{error:(i=r.error)!=null?i:""}),1e4),console.error("[micropub] Publish failed:",r.error)}catch(s){n.hide();let r=s instanceof Error?s.message:String(s);new y.Notice(o("noticeError",{error:r}),1e4),console.error("[micropub] Unexpected error:",s)}}async resolveSyndicationTargets(t){var g,l;let e=this.settings.showSyndicationDialog;if(e==="never")return;let n=[];try{n=(g=(await new k(()=>this.settings.micropubEndpoint,()=>this.settings.mediaEndpoint,()=>this.settings.accessToken).fetchConfig())["syndicate-to"])!=null?g:[]}catch(c){new y.Notice(o("noticeNoSyndTargets"),4e3);return}if(n.length===0)return;let i;try{let p=(await this.app.vault.read(t)).match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n/);if(p){let m=((l=(0,y.parseYaml)(p[1]))!=null?l:{})["mp-syndicate-to"];m!==void 0&&(i=Array.isArray(m)?m.map(String):[String(m)])}}catch(c){}if(!(e==="always"||e==="when-needed"&&i===void 0||i!==void 0&&i.length===0))return;let r=i&&i.length>0?i:this.settings.defaultSyndicateTo;return new O(this.app,n,r).awaitSelection()}async loadSettings(){this.settings=Object.assign({},Et,await this.loadData())}async saveSettings(){await this.saveData(this.settings)}};
|