@micacms/editor-ui 0.4.0 → 0.5.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/assets/ChangesView-CSjnT0fh.js +7 -0
  2. package/dist/assets/ChangesView-CSjnT0fh.js.map +1 -0
  3. package/dist/assets/{CollectionView-D3W4tRXa.js → CollectionView-BlLAhVVi.js} +2 -2
  4. package/dist/assets/{CollectionView-D3W4tRXa.js.map → CollectionView-BlLAhVVi.js.map} +1 -1
  5. package/dist/assets/{ConfirmDialog.vue_vue_type_script_setup_true_lang-BhOQwoeA.js → ConfirmDialog.vue_vue_type_script_setup_true_lang-C073kW3p.js} +2 -2
  6. package/dist/assets/{ConfirmDialog.vue_vue_type_script_setup_true_lang-BhOQwoeA.js.map → ConfirmDialog.vue_vue_type_script_setup_true_lang-C073kW3p.js.map} +1 -1
  7. package/dist/assets/DashboardView-BpelkVGe.js +2 -0
  8. package/dist/assets/DashboardView-BpelkVGe.js.map +1 -0
  9. package/dist/assets/EntryView-DZuhqJnd.js +2 -0
  10. package/dist/assets/EntryView-DZuhqJnd.js.map +1 -0
  11. package/dist/assets/{LoginView-BCrrdqnf.js → LoginView-BmnX2zOD.js} +2 -2
  12. package/dist/assets/{LoginView-BCrrdqnf.js.map → LoginView-BmnX2zOD.js.map} +1 -1
  13. package/dist/assets/{MediaView-KlQHfWAi.js → MediaView-Dp2botAQ.js} +2 -2
  14. package/dist/assets/{MediaView-KlQHfWAi.js.map → MediaView-Dp2botAQ.js.map} +1 -1
  15. package/dist/assets/{NotFoundView-C9bEHUGC.js → NotFoundView-ByEOgkxm.js} +2 -2
  16. package/dist/assets/{NotFoundView-C9bEHUGC.js.map → NotFoundView-ByEOgkxm.js.map} +1 -1
  17. package/dist/assets/{ObjectField-gxr2a2pp.js → ObjectField-BSITpnGH.js} +2 -2
  18. package/dist/assets/{ObjectField-gxr2a2pp.js.map → ObjectField-BSITpnGH.js.map} +1 -1
  19. package/dist/assets/{PageHeader.vue_vue_type_script_setup_true_lang-B-fBJAjn.js → PageHeader.vue_vue_type_script_setup_true_lang-DJWhe1Ye.js} +2 -2
  20. package/dist/assets/{PageHeader.vue_vue_type_script_setup_true_lang-B-fBJAjn.js.map → PageHeader.vue_vue_type_script_setup_true_lang-DJWhe1Ye.js.map} +1 -1
  21. package/dist/assets/{RepeaterField-obOWml_P.js → RepeaterField-BnXDf3wL.js} +2 -2
  22. package/dist/assets/{RepeaterField-obOWml_P.js.map → RepeaterField-BnXDf3wL.js.map} +1 -1
  23. package/dist/assets/SingletonView-Cb8Qq_gr.js +2 -0
  24. package/dist/assets/SingletonView-Cb8Qq_gr.js.map +1 -0
  25. package/dist/assets/{SystemView-CBvzzAoO.css → SystemView-AMhNiUgh.css} +1 -1
  26. package/dist/assets/SystemView-C6RAZvd2.js +3 -0
  27. package/dist/assets/SystemView-C6RAZvd2.js.map +1 -0
  28. package/dist/assets/{arrow-right-DJTAYrz5.js → arrow-right-DD5BWXHV.js} +2 -2
  29. package/dist/assets/{arrow-right-DJTAYrz5.js.map → arrow-right-DD5BWXHV.js.map} +1 -1
  30. package/dist/assets/{arrow-up-CibOjHj-.js → arrow-up-Bdxg9_RJ.js} +2 -2
  31. package/dist/assets/{arrow-up-CibOjHj-.js.map → arrow-up-Bdxg9_RJ.js.map} +1 -1
  32. package/dist/assets/{chevron-down-Bmt8sf-v.js → chevron-down-CcJtiSTR.js} +2 -2
  33. package/dist/assets/{chevron-down-Bmt8sf-v.js.map → chevron-down-CcJtiSTR.js.map} +1 -1
  34. package/dist/assets/{index-Colfqb-9.js → index-BsOOwFCf.js} +9 -9
  35. package/dist/assets/index-BsOOwFCf.js.map +1 -0
  36. package/dist/assets/{plus-D_aoCFbi.js → plus-CgaTSYYb.js} +2 -2
  37. package/dist/assets/{plus-D_aoCFbi.js.map → plus-CgaTSYYb.js.map} +1 -1
  38. package/dist/assets/{useField-BDSV5ZPo.js → useField-BxptRsz_.js} +2 -2
  39. package/dist/assets/{useField-BDSV5ZPo.js.map → useField-BxptRsz_.js.map} +1 -1
  40. package/dist/assets/{useUnsavedChanges-B0WVqgI4.js → useUnsavedChanges-FB5XcFfI.js} +4 -4
  41. package/dist/assets/{useUnsavedChanges-B0WVqgI4.js.map → useUnsavedChanges-FB5XcFfI.js.map} +1 -1
  42. package/dist/index.html +1 -1
  43. package/package.json +2 -2
  44. package/dist/assets/ChangesView-DoLhwPxu.js +0 -7
  45. package/dist/assets/ChangesView-DoLhwPxu.js.map +0 -1
  46. package/dist/assets/DashboardView-JZ2Eaw1_.js +0 -2
  47. package/dist/assets/DashboardView-JZ2Eaw1_.js.map +0 -1
  48. package/dist/assets/EntryView-BwERaZkJ.js +0 -2
  49. package/dist/assets/EntryView-BwERaZkJ.js.map +0 -1
  50. package/dist/assets/SingletonView-BNJCcl7V.js +0 -2
  51. package/dist/assets/SingletonView-BNJCcl7V.js.map +0 -1
  52. package/dist/assets/SystemView-CB8CT0Pa.js +0 -3
  53. package/dist/assets/SystemView-CB8CT0Pa.js.map +0 -1
  54. package/dist/assets/index-Colfqb-9.js.map +0 -1
@@ -0,0 +1,7 @@
1
+ import{D as be,d as re,h as s,c as n,b as v,a as o,F as y,k as R,C as U,t as c,e as B,g as P,q as te,o as ye,j as z,w as G,l as C,z as xe,s as ke,v as _e,m as we,r as T,i as D,A as E,n as Ce,x as se,R as $e,S as Te,y as Ae}from"./index-BsOOwFCf.js";import{h as Me}from"./useField-BxptRsz_.js";import{_ as Pe}from"./ConfirmDialog.vue_vue_type_script_setup_true_lang-C073kW3p.js";import{_ as Se}from"./PageHeader.vue_vue_type_script_setup_true_lang-DJWhe1Ye.js";/**
2
+ * @license @lucide/vue v1.37.0 - ISC
3
+ *
4
+ * This source code is licensed under the ISC license.
5
+ * See the LICENSE file in the root directory of this source tree.
6
+ */const Re=[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",key:"v9h5vc"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",key:"3uifl3"}],["path",{d:"M8 16H3v5",key:"1cv678"}]],je=be("refresh-cw",Re);function Ne(m,l,a){return Q(m,l,a,[],[])}function Q(m,l,a,u,p){const g=[];for(const[b,i]of Object.entries(m)){if(i.hidden)continue;const d=l===null?void 0:l[b],x=a===null?void 0:a[b],k=[...u,b],h=[...p,i.label??Me(b)];if(i.kind==="object"){g.push(...Q(i.fields,l===null?null:H(d)??{},a===null?null:H(x)??{},k,h));continue}if(i.kind==="repeater"){g.push(...De(i.fields,i.itemLabel,oe(d),oe(x),k,h,{before:l!==null,after:a!==null}));continue}J(d)!==J(x)&&g.push({path:k.join("."),label:h,kind:i.kind,before:l===null?null:ne(i,d),after:a===null?null:ne(i,x)})}return g}function De(m,l,a,u,p,g,b){const i=[],d=Math.max((a==null?void 0:a.length)??0,(u==null?void 0:u.length)??0);for(let x=0;x<d;x++){const k=(a==null?void 0:a[x])??(b.before?{}:null),h=(u==null?void 0:u[x])??(b.after?{}:null),A=_=>{const L=l?_==null?void 0:_[l]:void 0;return typeof L=="string"&&L!==""?L:`${x+1}`};i.push(...Q(m,k,h,[...p,String(x)],[...g,A(h??k)]))}return i}function ne(m,l){if(l==null||l==="")return"Empty";if(m.kind==="boolean")return l===!0?"Yes":"No";if(m.kind==="select")return(Array.isArray(l)?l:[l]).map(u=>{var p;return((p=m.options.find(g=>g.value===u))==null?void 0:p.label)??String(u)}).join(", ");if(m.kind==="media"){const a=Array.isArray(l)?l:[l];return a.length===0?"Empty":a.map(u=>String(u).split("/").at(-1)??String(u)).join(", ")}if(m.kind==="coordinates"){const a=H(l);if(a&&typeof a.lat=="number"&&typeof a.lng=="number")return`${a.lat.toFixed(5)}, ${a.lng.toFixed(5)}`}return Array.isArray(l)?l.map(String).join(", "):typeof l=="object"?J(l):String(l)}function H(m){return m!==null&&typeof m=="object"&&!Array.isArray(m)?m:null}function oe(m){return Array.isArray(m)?m:null}function J(m){return JSON.stringify(m??null,(l,a)=>{if(a===null||typeof a!="object"||Array.isArray(a))return a;const u=a,p={};for(const g of Object.keys(u).sort())p[g]=u[g];return p})}const Ee={class:"mt-3 rounded-md border border-line bg-canvas/60 p-3 text-xs"},Le={key:0,class:"mb-2 font-medium text-success"},Be={key:1,class:"mb-2 font-medium text-danger"},Ie={key:2,class:"grid grid-cols-2 gap-3"},Ve={class:"list-decimal space-y-0.5 pl-5"},Oe={class:"list-decimal space-y-0.5 pl-5"},Fe={key:0,class:"text-muted"},Ye={key:1,class:"space-y-3"},We={class:"mb-1 font-medium"},qe={key:0,class:"text-muted","aria-hidden":"true"},ze={key:0,class:"max-h-48 overflow-y-auto whitespace-pre-wrap break-words rounded-sm border border-danger/15 bg-danger-soft/60 p-2"},Ue={key:1,class:"max-h-48 overflow-y-auto whitespace-pre-wrap break-words rounded-sm border border-success/15 bg-success-soft/60 p-2"},ae=re({__name:"ChangeComparison",props:{comparison:{},fields:{}},setup(m){const l=m,a=P(()=>l.comparison.before===null?"created":l.comparison.after===null?"deleted":"changed"),u=P(()=>l.fields?Ne(l.fields,l.comparison.before,l.comparison.after):[]),p=P(()=>{if(l.fields)return null;const b=i=>Array.isArray(i==null?void 0:i.order)?i.order:[];return{before:b(l.comparison.before),after:b(l.comparison.after)}}),g=P(()=>{if(!p.value)return new Set;const b=new Map(p.value.before.map((i,d)=>[i.slug,d]));return new Set(p.value.after.filter((i,d)=>b.get(i.slug)!==d).map(i=>i.slug))});return(b,i)=>(s(),n("div",Ee,[a.value==="created"?(s(),n("p",Le," New — publishing puts this on the site for the first time. ")):a.value==="deleted"?(s(),n("p",Be," Publishing removes this from the site. ")):v("",!0),p.value?(s(),n("div",Ie,[o("div",null,[i[0]||(i[0]=o("p",{class:"mb-1.5 font-medium text-muted"},"Before",-1)),o("ol",Ve,[(s(!0),n(y,null,R(p.value.before,d=>(s(),n("li",{key:d.slug,class:U(g.value.has(d.slug)&&"font-medium text-ink")},c(d.title),3))),128))])]),o("div",null,[i[1]||(i[1]=o("p",{class:"mb-1.5 font-medium text-muted"},"After",-1)),o("ol",Oe,[(s(!0),n(y,null,R(p.value.after,d=>(s(),n("li",{key:d.slug,class:U(g.value.has(d.slug)&&"font-medium text-success")},c(d.title),3))),128))])])])):(s(),n(y,{key:3},[u.value.length===0?(s(),n("p",Fe," Nothing visible changed — files moved or were rewritten without a field changing its value. ")):(s(),n("dl",Ye,[(s(!0),n(y,null,R(u.value,d=>(s(),n("div",{key:d.path},[o("dt",We,[(s(!0),n(y,null,R(d.label,(x,k)=>(s(),n(y,{key:k},[k>0?(s(),n("span",qe," › ")):v("",!0),o("span",null,c(x),1)],64))),128))]),o("dd",{class:U(["grid gap-2",a.value==="changed"?"grid-cols-2":"grid-cols-1"])},[a.value!=="created"?(s(),n("div",ze,[i[2]||(i[2]=o("span",{class:"sr-only"},"Before: ",-1)),B(c(d.before),1)])):v("",!0),a.value!=="deleted"?(s(),n("div",Ue,[i[3]||(i[3]=o("span",{class:"sr-only"},"After: ",-1)),B(c(d.after),1)])):v("",!0)],2)]))),128))]))],64))]))}}),Ke=["disabled"],Ge={class:"mx-auto max-w-3xl space-y-5 px-4 py-6 sm:px-6"},He={key:0,class:"text-sm text-muted"},Je={key:0,class:"rounded-md border border-line bg-subtle p-4 text-sm text-muted"},Qe={class:"mt-1"},Xe={key:0,role:"alert",class:"rounded-md border border-warning/15 bg-warning-soft p-4"},Ze={key:1,role:"alert",class:"rounded-md border border-warning/15 bg-warning-soft p-4"},et={class:"text-sm font-medium text-warning"},tt={class:"mt-1 text-xs text-muted"},st=["disabled"],nt={key:2,role:"status",class:"rounded-md border border-line bg-subtle p-4"},ot={class:"mt-1 text-xs text-muted"},at={key:3,role:"alert",class:"rounded-md border border-danger/15 bg-danger-soft p-4"},rt={class:"mt-1 text-xs text-muted"},it={key:4,"aria-labelledby":"conflicts-heading",class:"space-y-3"},lt={class:"card divide-y divide-line"},ut={class:"text-sm font-medium"},dt={class:"mt-0.5 text-xs text-muted"},ct={key:0,class:"mt-2 text-xs text-muted"},ht={key:1,class:"mt-2 text-xs text-danger"},mt={key:1,role:"alert",class:"mt-2 text-xs text-danger"},pt={class:"mt-2.5 flex flex-wrap gap-2"},gt=["disabled","onClick"],ft=["disabled","onClick"],vt={key:2,role:"alert",class:"rounded-md border border-warning/15 bg-warning-soft p-4"},bt={class:"mt-1 text-xs text-muted"},yt=["disabled"],xt={key:3,role:"alert",class:"rounded-md border border-danger/15 bg-danger-soft p-4"},kt={class:"mt-1.5 space-y-1 text-xs text-danger"},_t={class:"font-medium"},wt={key:4,class:"card p-8 text-center"},Ct={class:"mt-1 text-sm text-muted"},$t={key:5,"aria-labelledby":"changes-heading"},Tt={class:"card divide-y divide-line"},At={class:"min-w-0 flex-1"},Mt={key:0,class:"mt-2 text-xs text-muted"},Pt={key:1,class:"mt-2 text-xs text-danger"},St={key:1,class:"mt-1 space-y-0.5"},Rt={class:"break-all font-mono"},jt=["onClick"],Nt={key:6,class:"card p-4","aria-labelledby":"publish-heading"},Dt={id:"publish-heading",class:"text-sm font-semibold"},Et={class:"mt-1 text-xs text-muted"},Lt=["disabled"],Bt=["disabled"],It={key:2,class:"text-sm text-muted"},Vt={key:0},Ot={key:1},zt=re({__name:"ChangesView",setup(m){const l=Ae(),a=we(),u=Ce(),p=T(!0),g=T(!1),b=T(!1),i=T(""),d=T(null),x=T(!1),k=T(null),h=a.status,A=a.edits,_=T({});function L(e){var t,f;return e.kind==="entry"?((t=l.collection(e.collection))==null?void 0:t.fields)??null:e.kind==="singleton"?((f=l.singleton(e.name))==null?void 0:f.fields)??null:null}async function X(e){if(e.target){_.value[e.key]={loading:!0,error:null,comparison:null};try{const t=await D.staging.compare(e.target);_.value[e.key]={loading:!1,error:null,comparison:t}}catch(t){_.value[e.key]={loading:!1,error:t instanceof E?t.message:"Could not load the comparison.",comparison:null}}}}te(A,e=>{for(const t of e)t.target&&!_.value[t.key]&&X(t)});const j=P(()=>{var e;return((e=h.value)==null?void 0:e.owner)==="developer"}),$=P(()=>{var e;return((e=h.value)==null?void 0:e.sync)??null}),ie=P(()=>h.value?j.value?h.value.branch?`Your working tree, on branch ${h.value.branch}`:"Your working tree":"Saved in Mica, waiting to be published":null),le=P(()=>{var e;return j.value&&(k.value!==null||(((e=h.value)==null?void 0:e.ahead)??0)>0)}),Z=P(()=>{var e,t,f;return(((e=h.value)==null?void 0:e.publish.enabled)??!1)&&(((t=h.value)==null?void 0:t.validation.ok)??!1)&&(((f=h.value)==null?void 0:f.changes.length)??0)>0&&!g.value}),ue={create:"Added",update:"Changed",delete:"Removed"};async function F(){_.value={},await a.refresh();for(const e of A.value)e.target&&!_.value[e.key]&&X(e);p.value=!1}ye(F);async function de(){var e,t;g.value=!0;try{const f=Object.fromEntries((((e=h.value)==null?void 0:e.changes)??[]).map(r=>[r.path,r.revision??null])),w=await D.staging.publish(i.value,f);w.state==="published"?(k.value=null,i.value="",u.success("Published.",w.revision?`Commit ${K(w.revision)}.`:void 0)):j.value?(k.value=w,i.value="",u.error("Committed, but the push failed.",w.pushError)):(i.value="",u.error("Published in Mica, not yet sent to the website.","Mica tries again every minute.")),await a.refresh()}catch(f){f instanceof E?(u.error(f.message,(t=f.issues[0])==null?void 0:t.message),await a.refresh()):u.error("Could not publish.")}finally{g.value=!1}}async function ce(){b.value=!0;try{const e=await D.staging.push();e.state==="published"?(k.value=null,u.success("Pushed.",e.revision?`Commit ${K(e.revision)}.`:void 0)):(k.value=e,u.error("The push failed again.",e.pushError)),await a.refresh()}catch(e){u.error(e instanceof E?e.message:"Could not push.")}finally{b.value=!1}}async function he(){const e=d.value;if(e){x.value=!0;try{await D.staging.discard(e.fileChanges.map(t=>t.path)),u.success(`Discarded the changes to ${e.label}.`),d.value=null,await a.refresh()}catch(t){u.error(t instanceof E?t.message:"Could not discard.")}finally{x.value=!1}}}const Y=T(!1),W=T(null),q=T(null);async function me(){Y.value=!0;try{await D.staging.sync(),await F()}catch(e){u.error(e instanceof E?e.message:"Could not reach the repository.")}finally{Y.value=!1}}function pe(e){var f;const t=new Set((((f=h.value)==null?void 0:f.changes)??[]).filter(w=>w.kind==="create").map(w=>w.path));return e.paths.some(w=>t.has(w))}async function ee(e,t){if(t==="website"&&pe(e)&&q.value!==e){q.value=e;return}q.value=null,W.value=e.group;try{await D.staging.resolve(e.group,t),u.success(t==="mine"?`Kept your version of ${e.label}.`:`Took the website's version of ${e.label}.`),await F()}catch(f){u.error(f instanceof E?f.message:"Could not resolve the conflict.")}finally{W.value=null}}const ge={changed:"was changed on the website while you edited it",deleted:"was deleted on the website while you edited it",added:"was also created on the website"},M=T({});async function fe(e){if(e.target){M.value[e.group]={loading:!0,error:null,comparison:null};try{const t=await D.staging.compare(e.target,"origin");M.value[e.group]={loading:!1,error:null,comparison:t}}catch(t){M.value[e.group]={loading:!1,error:t instanceof E?t.message:"Could not load the comparison.",comparison:null}}}}te(()=>{var e;return((e=$.value)==null?void 0:e.conflicts)??[]},e=>{for(const t of e)M.value[t.group]||fe(t)});function ve(e){return new Date(e).toLocaleString()}function K(e){return/^[0-9a-f]{20,}$/i.test(e)?e.slice(0,7):e}return(e,t)=>{var f,w;return s(),n(y,null,[z(Se,{title:"Publish",subtitle:ie.value},{default:G(()=>[o("button",{type:"button",class:"btn-ghost px-2",disabled:p.value,onClick:F},[z(C(je),{size:16,"aria-hidden":"true"}),t[3]||(t[3]=o("span",{class:"sr-only"},"Refresh the change list",-1))],8,Ke)]),_:1},8,["subtitle"]),o("div",Ge,[p.value?(s(),n("p",He,"Loading…")):C(h)?(s(),n(y,{key:1},[C(h).publish.enabled?v("",!0):(s(),n("div",Je,[t[4]||(t[4]=o("p",{class:"font-medium text-ink"},"Publishing is not available here",-1)),o("p",Qe,c(C(h).publish.reason),1)])),$.value?(s(),n(y,{key:1},[$.value.restartRequired?(s(),n("div",Xe,[...t[5]||(t[5]=[o("p",{class:"text-sm font-medium text-warning"},"The content model changed on the website",-1),o("p",{class:"mt-1 text-xs text-muted"}," You can go on saving. To publish, ask the person who runs Mica to restart it. ",-1)])])):v("",!0),$.value.error?(s(),n("div",Ze,[o("p",et,"Cannot reach the repository since "+c(ve($.value.error.since)),1),o("p",tt," You can go on saving. Mica tries again every minute. If this stays, tell the person who runs Mica: "+c($.value.error.message),1),o("button",{type:"button",class:"btn-secondary mt-2.5",disabled:Y.value,onClick:me},c(Y.value?"Trying…":"Try again"),9,st)])):v("",!0),$.value.pendingPush?(s(),n("div",nt,[t[6]||(t[6]=o("p",{class:"text-sm font-medium"},"Published in Mica, not yet sent to the website",-1)),o("p",ot," Mica tries again every minute. If this stays, tell the person who runs Mica: "+c($.value.pendingPush.reason),1)])):v("",!0),$.value.refused?(s(),n("div",at,[t[7]||(t[7]=o("p",{class:"text-sm font-medium text-danger"},"The repository refused the last publication",-1)),o("p",rt," Its changes are drafts again, listed below. Tell the person who runs Mica: "+c($.value.refused.reason),1)])):v("",!0),$.value.conflicts.length>0?(s(),n("section",it,[t[9]||(t[9]=o("h2",{id:"conflicts-heading",class:"text-sm font-semibold"}," Changed on the website while you edited ",-1)),t[10]||(t[10]=o("p",{class:"text-xs text-muted"}," Choose a version for each. Until you do, nothing can be published and the website's newer changes stay out of Mica. ",-1)),o("ul",lt,[(s(!0),n(y,null,R($.value.conflicts,r=>{var N,I,V,O;return s(),n("li",{key:r.group,class:"p-4"},[o("p",ut,c(r.label),1),o("p",dt,"It "+c(ge[r.upstream])+".",1),r.target?(s(),n(y,{key:0},[(N=M.value[r.group])!=null&&N.loading?(s(),n("p",ct,"Comparing…")):(I=M.value[r.group])!=null&&I.error?(s(),n("p",ht,c((V=M.value[r.group])==null?void 0:V.error),1)):(O=M.value[r.group])!=null&&O.comparison?(s(),n(y,{key:2},[t[8]||(t[8]=o("p",{class:"mt-2 text-xs text-muted"},"The website's version, then yours:",-1)),z(ae,{comparison:M.value[r.group].comparison,fields:L(r.target)},null,8,["comparison","fields"])],64)):v("",!0)],64)):v("",!0),q.value===r?(s(),n("p",mt," This deletes the entry you created. Press the button again to confirm. ")):v("",!0),o("div",pt,[o("button",{type:"button",class:"btn-secondary",disabled:W.value!==null,onClick:S=>ee(r,"mine")},c(r.upstream==="deleted"?"Restore it":"Keep my version"),9,gt),o("button",{type:"button",class:"btn-ghost",disabled:W.value!==null,onClick:S=>ee(r,"website")},c(r.upstream==="deleted"?"Accept the deletion":"Use the website's version"),9,ft)])])}),128))])])):v("",!0)],64)):v("",!0),le.value?(s(),n("div",vt,[t[11]||(t[11]=o("p",{class:"text-sm font-medium text-warning"},"Committed, waiting to be pushed",-1)),o("p",bt,c(((f=k.value)==null?void 0:f.pushError)??"This branch has commits the remote has not seen. Retrying pushes the existing commits — it never creates new ones."),1),o("button",{type:"button",class:"btn-secondary mt-2.5",disabled:b.value,onClick:ce},c(b.value?"Pushing…":"Retry push"),9,yt)])):v("",!0),C(h).validation.ok?v("",!0):(s(),n("div",xt,[t[12]||(t[12]=o("p",{class:"text-sm font-medium text-danger"}," These changes cannot be published until they are fixed ",-1)),o("ul",kt,[(s(!0),n(y,null,R(C(h).validation.problems,r=>(s(),n("li",{key:r.path},[o("span",_t,c(r.label),1),r.issues.length?(s(),n(y,{key:0},[B(" — "+c(r.issues.map(N=>N.message).join("; ")),1)],64)):v("",!0)]))),128))])])),C(A).length===0?(s(),n("div",wt,[t[13]||(t[13]=o("p",{class:"text-sm font-medium"},"Nothing waiting to be published",-1)),o("p",Ct," Saved changes collect here until somebody publishes them. Right now the "+c(j.value?"working tree":"draft workspace")+" matches the repository. ",1)])):(s(),n("section",$t,[t[14]||(t[14]=o("h2",{id:"changes-heading",class:"sr-only"},"Pending changes",-1)),o("ul",Tt,[(s(!0),n(y,null,R(C(A),r=>{var N,I,V,O;return s(),n("li",{key:r.key,class:"flex items-start gap-3 p-4"},[o("div",At,[(s(),se(Te(r.link?"RouterLink":"p"),$e({ref_for:!0},r.link?{to:r.link}:{},{class:["text-sm font-medium",r.link&&"hover:underline"]}),{default:G(()=>[B(c(r.label),1)]),_:2},1040,["class"])),r.target?(s(),n(y,{key:0},[(N=_.value[r.key])!=null&&N.loading?(s(),n("p",Mt," Comparing… ")):(I=_.value[r.key])!=null&&I.error?(s(),n("p",Pt,c((V=_.value[r.key])==null?void 0:V.error),1)):(O=_.value[r.key])!=null&&O.comparison?(s(),se(ae,{key:2,comparison:_.value[r.key].comparison,fields:L(r.target)},null,8,["comparison","fields"])):v("",!0)],64)):(s(),n("ul",St,[(s(!0),n(y,null,R(r.fileChanges,S=>(s(),n("li",{key:S.path,class:"flex items-baseline gap-2 text-xs text-muted"},[o("span",{class:U(["w-16 shrink-0 font-mono text-[11px]",S.kind==="delete"?"text-danger":S.kind==="create"?"text-success":""])},c(ue[S.kind]),3),o("span",Rt,c(S.path),1)]))),128))]))]),o("button",{type:"button",class:"btn-ghost shrink-0 text-xs",onClick:S=>d.value=r}," Discard ",8,jt)])}),128))])])),C(A).length>0&&C(h).publish.enabled?(s(),n("section",Nt,[o("h2",Dt," Publish "+c(C(A).length===1?"this edit":`these ${C(A).length} edits`),1),o("p",Et,[B(c(j.value?"One commit of exactly the files listed above, pushed with your own Git configuration.":"One commit to the content repository, which is what triggers your site's build.")+" ",1),j.value&&C(h).head?(s(),n(y,{key:0},[B(" Based on "+c(K(C(h).head))+".",1)],64)):v("",!0)]),o("form",{class:"mt-3 flex flex-wrap items-center gap-2",onSubmit:t[1]||(t[1]=xe(r=>Z.value&&i.value.trim()&&de(),["prevent"]))},[ke(o("input",{"onUpdate:modelValue":t[0]||(t[0]=r=>i.value=r),type:"text",class:"input min-w-0 flex-1",maxlength:"500",placeholder:"Describe this publication — it becomes the commit message","aria-label":"Commit message",disabled:g.value},null,8,Lt),[[_e,i.value]]),o("button",{type:"submit",class:"btn-primary",disabled:!Z.value||!i.value.trim()},c(g.value?"Publishing…":"Publish"),9,Bt)],32)])):v("",!0)],64)):(s(),n("p",It,"The change list could not be loaded. Reload to retry."))]),z(Pe,{open:d.value!==null,title:`Discard the changes to “${(w=d.value)==null?void 0:w.label}”?`,"confirm-label":"Discard",destructive:"",busy:x.value,onCancel:t[2]||(t[2]=r=>d.value=null),onConfirm:he},{default:G(()=>[j.value?(s(),n("p",Vt," This restores the files in your working tree to the last commit — new files are deleted, edited ones go back. It cannot be undone from here. ")):(s(),n("p",Ot," This forgets the saved changes; the repository stays exactly as it is. It cannot be undone from here. "))]),_:1},8,["open","title","busy"])],64)}}});export{zt as default};
7
+ //# sourceMappingURL=ChangesView-CSjnT0fh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChangesView-CSjnT0fh.js","sources":["../../../../common/temp/node_modules/.pnpm/@lucide+vue@1.37.0_vue@3.5.42_typescript@5.9.3_/node_modules/@lucide/vue/dist/esm/icons/refresh-cw.mjs","../../src/lib/compare.ts","../../src/components/ChangeComparison.vue","../../src/views/ChangesView.vue"],"sourcesContent":["/**\n * @license @lucide/vue v1.37.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [\n [\"path\", { d: \"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8\", key: \"v9h5vc\" }],\n [\"path\", { d: \"M21 3v5h-5\", key: \"1q7to0\" }],\n [\"path\", { d: \"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16\", key: \"3uifl3\" }],\n [\"path\", { d: \"M8 16H3v5\", key: \"1cv678\" }]\n];\nconst RefreshCw = createLucideIcon(\"refresh-cw\", __iconNode);\n\nexport { __iconNode, RefreshCw as default };\n//# sourceMappingURL=refresh-cw.mjs.map\n","/**\n * Turns a raw before/after pair into rows an editor reads.\n *\n * The server's comparison is two documents of field data; everything friendly\n * about it happens here, where the model is at hand: fields get their labels,\n * nesting becomes a breadcrumb, unchanged values disappear, and each value is\n * written the way its field type would show it — never as JSON with quotes.\n */\n\nimport type { AnyField, FieldMap } from \"@micacms/core\";\n\nimport { humanise } from \"@/composables/useField\";\n\n/** One changed value, named and rendered for both sides. */\nexport interface CompareRow {\n /** Unique among the rows — the dotted path of the leaf. */\n path: string;\n /** Breadcrumb of labels, e.g. `[\"Extra sections\", \"2\", \"Heading\"]`. */\n label: string[];\n /** The field's kind, as a rendering hint (`richText` wants more room). */\n kind: string;\n /** Display strings; null on the side where the document does not exist. */\n before: string | null;\n after: string | null;\n}\n\n/**\n * The changed fields between two documents, in model order.\n *\n * A side that is null means the document itself is absent there (a creation\n * or a deletion); every present value on the other side becomes a row, so the\n * comparison doubles as a summary of the new or removed document.\n */\nexport function compareRows(\n fields: FieldMap,\n before: Record<string, unknown> | null,\n after: Record<string, unknown> | null,\n): CompareRow[] {\n return walk(fields, before, after, [], []);\n}\n\nfunction walk(\n fields: FieldMap,\n before: Record<string, unknown> | null,\n after: Record<string, unknown> | null,\n path: string[],\n label: string[],\n): CompareRow[] {\n const rows: CompareRow[] = [];\n\n for (const [name, field] of Object.entries(fields)) {\n if (field.hidden) continue;\n\n const a = before === null ? undefined : before[name];\n const b = after === null ? undefined : after[name];\n const at = [...path, name];\n const named = [...label, field.label ?? humanise(name)];\n\n // Null means \"this side's document does not exist\" all the way down. A\n // *value* that is merely missing inside an existing document becomes an\n // empty record instead, so its leaves read as Empty rather than vanish.\n if (field.kind === \"object\") {\n rows.push(\n ...walk(\n field.fields,\n before === null ? null : (asRecord(a) ?? {}),\n after === null ? null : (asRecord(b) ?? {}),\n at,\n named,\n ),\n );\n continue;\n }\n\n if (field.kind === \"repeater\") {\n rows.push(\n ...repeaterRows(field.fields, field.itemLabel, asList(a), asList(b), at, named, {\n before: before !== null,\n after: after !== null,\n }),\n );\n continue;\n }\n\n if (canonical(a) === canonical(b)) continue;\n\n rows.push({\n path: at.join(\".\"),\n label: named,\n kind: field.kind,\n before: before === null ? null : displayValue(field, a),\n after: after === null ? null : displayValue(field, b),\n });\n }\n\n return rows;\n}\n\n/**\n * Repeater items compared by position. Position is what the editor sees and\n * what the file stores; chasing moved items across indexes would guess.\n */\nfunction repeaterRows(\n fields: FieldMap,\n itemLabel: string | undefined,\n before: Array<Record<string, unknown>> | null,\n after: Array<Record<string, unknown>> | null,\n path: string[],\n label: string[],\n present: { before: boolean; after: boolean },\n): CompareRow[] {\n const rows: CompareRow[] = [];\n const count = Math.max(before?.length ?? 0, after?.length ?? 0);\n\n for (let index = 0; index < count; index++) {\n const a = before?.[index] ?? (present.before ? {} : null);\n const b = after?.[index] ?? (present.after ? {} : null);\n\n // Name the item by its summary field where one is set, else its number.\n const caption = (item: Record<string, unknown> | null) => {\n const value = itemLabel ? item?.[itemLabel] : undefined;\n return typeof value === \"string\" && value !== \"\" ? value : `${index + 1}`;\n };\n\n rows.push(...walk(fields, a, b, [...path, String(index)], [...label, caption(b ?? a)]));\n }\n\n return rows;\n}\n\n/**\n * A field value as the editor would say it — `displayValue` never shows JSON.\n * Null on both ends of a row is prevented by the change filter, so \"Empty\"\n * only ever appears opposite a real value.\n */\nexport function displayValue(field: AnyField, value: unknown): string {\n if (value === null || value === undefined || value === \"\") return \"Empty\";\n\n if (field.kind === \"boolean\") return value === true ? \"Yes\" : \"No\";\n\n if (field.kind === \"select\") {\n const values = Array.isArray(value) ? value : [value];\n return values\n .map((entry) => field.options.find((option) => option.value === entry)?.label ?? String(entry))\n .join(\", \");\n }\n\n if (field.kind === \"media\") {\n const values = Array.isArray(value) ? value : [value];\n if (values.length === 0) return \"Empty\";\n return values.map((entry) => String(entry).split(\"/\").at(-1) ?? String(entry)).join(\", \");\n }\n\n if (field.kind === \"coordinates\") {\n const point = asRecord(value);\n if (point && typeof point[\"lat\"] === \"number\" && typeof point[\"lng\"] === \"number\") {\n return `${point[\"lat\"].toFixed(5)}, ${point[\"lng\"].toFixed(5)}`;\n }\n }\n\n if (Array.isArray(value)) return value.map(String).join(\", \");\n if (typeof value === \"object\") return canonical(value);\n\n return String(value);\n}\n\n/* ------------------------------------------------------------------ */\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : null;\n}\n\nfunction asList(value: unknown): Array<Record<string, unknown>> | null {\n return Array.isArray(value) ? (value as Array<Record<string, unknown>>) : null;\n}\n\n/** Canonical JSON — keys sorted, so a rebuilt object compares equal. */\nfunction canonical(value: unknown): string {\n return JSON.stringify(value ?? null, (_key, entry: unknown) => {\n if (entry === null || typeof entry !== \"object\" || Array.isArray(entry)) return entry;\n const record = entry as Record<string, unknown>;\n const sorted: Record<string, unknown> = {};\n for (const key of Object.keys(record).sort()) sorted[key] = record[key];\n return sorted;\n });\n}\n","<script setup lang=\"ts\">\n/**\n * What a pending change does, said as before and after.\n *\n * Not a diff: no line numbers, no `+`/`-`, no file contents. Each changed\n * field is one row with the stored value on the left and the staged one on\n * the right, worded by the field type — an editor who has never read a patch\n * can see exactly what publishing applies.\n */\n\nimport { computed } from \"vue\";\n\nimport type { FieldMap } from \"@micacms/core\";\n\nimport type { Comparison } from \"@/api\";\nimport { compareRows } from \"@/lib/compare\";\n\nconst props = defineProps<{\n comparison: Comparison;\n /** The owning document's fields; null for an order comparison. */\n fields: FieldMap | null;\n}>();\n\nconst mode = computed<\"created\" | \"deleted\" | \"changed\">(() => {\n if (props.comparison.before === null) return \"created\";\n if (props.comparison.after === null) return \"deleted\";\n return \"changed\";\n});\n\nconst rows = computed(() =>\n props.fields\n ? compareRows(props.fields, props.comparison.before, props.comparison.after)\n : [],\n);\n\n/** An order comparison — the collection's sequence on both sides. */\ninterface OrderItem {\n slug: string;\n title: string;\n}\n\nconst order = computed(() => {\n if (props.fields) return null;\n const side = (data: Record<string, unknown> | null): OrderItem[] =>\n Array.isArray(data?.[\"order\"]) ? (data[\"order\"] as OrderItem[]) : [];\n return { before: side(props.comparison.before), after: side(props.comparison.after) };\n});\n\n/** Slugs whose position changed, which is what an order change *is*. */\nconst moved = computed(() => {\n if (!order.value) return new Set<string>();\n const positions = new Map(order.value.before.map((item, index) => [item.slug, index]));\n return new Set(\n order.value.after\n .filter((item, index) => positions.get(item.slug) !== index)\n .map((item) => item.slug),\n );\n});\n</script>\n\n<template>\n <div class=\"mt-3 rounded-md border border-line bg-canvas/60 p-3 text-xs\">\n <p v-if=\"mode === 'created'\" class=\"mb-2 font-medium text-success\">\n New — publishing puts this on the site for the first time.\n </p>\n <p v-else-if=\"mode === 'deleted'\" class=\"mb-2 font-medium text-danger\">\n Publishing removes this from the site.\n </p>\n\n <!-- An order change: the sequence, before and after. -->\n <div v-if=\"order\" class=\"grid grid-cols-2 gap-3\">\n <div>\n <p class=\"mb-1.5 font-medium text-muted\">Before</p>\n <ol class=\"list-decimal space-y-0.5 pl-5\">\n <li\n v-for=\"item in order.before\"\n :key=\"item.slug\"\n :class=\"moved.has(item.slug) && 'font-medium text-ink'\"\n >\n {{ item.title }}\n </li>\n </ol>\n </div>\n <div>\n <p class=\"mb-1.5 font-medium text-muted\">After</p>\n <ol class=\"list-decimal space-y-0.5 pl-5\">\n <li\n v-for=\"item in order.after\"\n :key=\"item.slug\"\n :class=\"moved.has(item.slug) && 'font-medium text-success'\"\n >\n {{ item.title }}\n </li>\n </ol>\n </div>\n </div>\n\n <!-- Field changes. -->\n <template v-else>\n <p v-if=\"rows.length === 0\" class=\"text-muted\">\n Nothing visible changed — files moved or were rewritten without a field changing its value.\n </p>\n\n <dl v-else class=\"space-y-3\">\n <div v-for=\"row in rows\" :key=\"row.path\">\n <dt class=\"mb-1 font-medium\">\n <template v-for=\"(part, index) in row.label\" :key=\"index\">\n <span v-if=\"index > 0\" class=\"text-muted\" aria-hidden=\"true\"> › </span\n ><span>{{ part }}</span>\n </template>\n </dt>\n <dd :class=\"['grid gap-2', mode === 'changed' ? 'grid-cols-2' : 'grid-cols-1']\">\n <div\n v-if=\"mode !== 'created'\"\n class=\"max-h-48 overflow-y-auto whitespace-pre-wrap break-words rounded-sm border border-danger/15 bg-danger-soft/60 p-2\"\n >\n <span class=\"sr-only\">Before: </span>{{ row.before }}\n </div>\n <div\n v-if=\"mode !== 'deleted'\"\n class=\"max-h-48 overflow-y-auto whitespace-pre-wrap break-words rounded-sm border border-success/15 bg-success-soft/60 p-2\"\n >\n <span class=\"sr-only\">After: </span>{{ row.after }}\n </div>\n </dd>\n </div>\n </dl>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/**\n * The pending change set, and Publish. The whole site shares one pending set,\n * so everything on it is published together, whoever presses the button.\n * `owner` changes only the words, not the behaviour.\n */\n\nimport { computed, onMounted, ref, watch } from \"vue\";\nimport { RefreshCw } from \"@lucide/vue\";\n\nimport type { FieldMap } from \"@micacms/core\";\n\nimport {\n api,\n ApiError,\n type Comparison,\n type CompareTarget,\n type FileChange,\n type PublishOutcome,\n type SyncConflict,\n} from \"@/api\";\nimport ChangeComparison from \"@/components/ChangeComparison.vue\";\nimport type { Edit } from \"@/lib/edits\";\nimport ConfirmDialog from \"@/components/ConfirmDialog.vue\";\nimport PageHeader from \"@/components/PageHeader.vue\";\nimport { useSchema } from \"@/stores/schema\";\nimport { useStaging } from \"@/stores/staging\";\nimport { useToast } from \"@/stores/toast\";\n\nconst schema = useSchema();\nconst staging = useStaging();\nconst toast = useToast();\n\nconst loading = ref(true);\nconst publishing = ref(false);\nconst pushing = ref(false);\nconst message = ref(\"\");\nconst discardTarget = ref<Edit | null>(null);\nconst discarding = ref(false);\n\n/** The half-finished publication, when a local push failed. */\nconst pendingPush = ref<PublishOutcome | null>(null);\n\nconst status = staging.status;\n\n/** One row per edit, never per file — see `lib/edits.ts`. */\nconst edits = staging.edits;\n\n/* ------------------------------------------------- before/after */\n\ninterface LoadedComparison {\n loading: boolean;\n error: string | null;\n comparison: Comparison | null;\n}\n\nconst comparisons = ref<Record<string, LoadedComparison>>({});\n\n/** The compared document's fields — null for an order comparison. */\nfunction fieldsFor(target: CompareTarget): FieldMap | null {\n if (target.kind === \"entry\") return schema.collection(target.collection)?.fields ?? null;\n if (target.kind === \"singleton\") return schema.singleton(target.name)?.fields ?? null;\n return null;\n}\n\nasync function loadComparison(edit: Edit): Promise<void> {\n if (!edit.target) return;\n comparisons.value[edit.key] = { loading: true, error: null, comparison: null };\n try {\n const comparison = await api.staging.compare(edit.target);\n comparisons.value[edit.key] = { loading: false, error: null, comparison };\n } catch (error) {\n comparisons.value[edit.key] = {\n loading: false,\n error: error instanceof ApiError ? error.message : \"Could not load the comparison.\",\n comparison: null,\n };\n }\n}\n\n// Comparisons load with the change list, with nothing to click.\nwatch(edits, (next) => {\n for (const edit of next) {\n if (edit.target && !comparisons.value[edit.key]) void loadComparison(edit);\n }\n});\n\n/**\n * The developer's checkout, worded in Git's terms; otherwise Mica's own clone,\n * worded for editors.\n */\nconst isTree = computed(() => status.value?.owner === \"developer\");\nconst sync = computed(() => status.value?.sync ?? null);\n\nconst subtitle = computed(() => {\n if (!status.value) return null;\n if (isTree.value) {\n return status.value.branch\n ? `Your working tree, on branch ${status.value.branch}`\n : \"Your working tree\";\n }\n return \"Saved in Mica, waiting to be published\";\n});\n\n/**\n * Commits already made and not yet pushed — visible even across a restart. The\n * developer retries; Mica retries its own.\n */\nconst waitingToPush = computed(\n () => isTree.value && (pendingPush.value !== null || (status.value?.ahead ?? 0) > 0),\n);\n\nconst canPublish = computed(\n () =>\n (status.value?.publish.enabled ?? false) &&\n (status.value?.validation.ok ?? false) &&\n (status.value?.changes.length ?? 0) > 0 &&\n !publishing.value,\n);\n\nconst kindWord: Record<FileChange[\"kind\"], string> = {\n create: \"Added\",\n update: \"Changed\",\n delete: \"Removed\",\n};\n\nasync function refresh(): Promise<void> {\n // Forget the comparisons first, so the edits watcher reloads every one of\n // them against the refreshed change set instead of showing a stale panel.\n comparisons.value = {};\n await staging.refresh();\n for (const edit of edits.value) {\n if (edit.target && !comparisons.value[edit.key]) void loadComparison(edit);\n }\n loading.value = false;\n}\n\nonMounted(refresh);\n\nasync function publish(): Promise<void> {\n publishing.value = true;\n try {\n // Exactly what the editor sees here, at the revision they see it: a file\n // saved again since makes the server refuse rather than publish it unseen.\n const reviewed = Object.fromEntries(\n (status.value?.changes ?? []).map((change) => [change.path, change.revision ?? null]),\n );\n const outcome = await api.staging.publish(message.value, reviewed);\n\n if (outcome.state === \"published\") {\n pendingPush.value = null;\n message.value = \"\";\n toast.success(\"Published.\", outcome.revision ? `Commit ${short(outcome.revision)}.` : undefined);\n } else if (isTree.value) {\n // The commit is real; only the push failed. Say so, and keep the retry\n // visible — pressing Publish again must not create a second commit.\n pendingPush.value = outcome;\n message.value = \"\";\n toast.error(\"Committed, but the push failed.\", outcome.pushError);\n } else {\n message.value = \"\";\n toast.error(\"Published in Mica, not yet sent to the website.\", \"Mica tries again every minute.\");\n }\n\n await staging.refresh();\n } catch (error) {\n if (error instanceof ApiError) {\n toast.error(error.message, error.issues[0]?.message);\n // A conflict or a validation refusal means the list moved: show the truth.\n await staging.refresh();\n } else {\n toast.error(\"Could not publish.\");\n }\n } finally {\n publishing.value = false;\n }\n}\n\nasync function retryPush(): Promise<void> {\n pushing.value = true;\n try {\n const outcome = await api.staging.push();\n if (outcome.state === \"published\") {\n pendingPush.value = null;\n toast.success(\"Pushed.\", outcome.revision ? `Commit ${short(outcome.revision)}.` : undefined);\n } else {\n pendingPush.value = outcome;\n toast.error(\"The push failed again.\", outcome.pushError);\n }\n await staging.refresh();\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : \"Could not push.\");\n } finally {\n pushing.value = false;\n }\n}\n\nasync function discard(): Promise<void> {\n const edit = discardTarget.value;\n if (!edit) return;\n\n discarding.value = true;\n try {\n await api.staging.discard(edit.fileChanges.map((change) => change.path));\n toast.success(`Discarded the changes to ${edit.label}.`);\n discardTarget.value = null;\n await staging.refresh();\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : \"Could not discard.\");\n } finally {\n discarding.value = false;\n }\n}\n\n/* --------------------------------------------- Mica's own clone */\n\nconst syncing = ref(false);\nconst resolving = ref<string | null>(null);\n/** \"Use the website's version\" on a new entry deletes it, so it asks first. */\nconst confirmWebsite = ref<SyncConflict | null>(null);\n\nasync function syncNow(): Promise<void> {\n syncing.value = true;\n try {\n await api.staging.sync();\n await refresh();\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : \"Could not reach the repository.\");\n } finally {\n syncing.value = false;\n }\n}\n\n/** Whether taking the website's version would delete an entry the editor created. */\nfunction discardsNewWork(conflict: SyncConflict): boolean {\n const created = new Set(\n (status.value?.changes ?? []).filter((change) => change.kind === \"create\").map((change) => change.path),\n );\n return conflict.paths.some((path) => created.has(path));\n}\n\nasync function resolve(conflict: SyncConflict, choice: \"mine\" | \"website\"): Promise<void> {\n if (choice === \"website\" && discardsNewWork(conflict) && confirmWebsite.value !== conflict) {\n confirmWebsite.value = conflict;\n return;\n }\n confirmWebsite.value = null;\n resolving.value = conflict.group;\n try {\n await api.staging.resolve(conflict.group, choice);\n toast.success(choice === \"mine\" ? `Kept your version of ${conflict.label}.` : `Took the website's version of ${conflict.label}.`);\n await refresh();\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : \"Could not resolve the conflict.\");\n } finally {\n resolving.value = null;\n }\n}\n\nconst upstreamWords: Record<SyncConflict[\"upstream\"], string> = {\n changed: \"was changed on the website while you edited it\",\n deleted: \"was deleted on the website while you edited it\",\n added: \"was also created on the website\",\n};\n\nconst conflictComparisons = ref<Record<string, LoadedComparison>>({});\n\nasync function loadConflictComparison(conflict: SyncConflict): Promise<void> {\n if (!conflict.target) return;\n conflictComparisons.value[conflict.group] = { loading: true, error: null, comparison: null };\n try {\n const comparison = await api.staging.compare(conflict.target, \"origin\");\n conflictComparisons.value[conflict.group] = { loading: false, error: null, comparison };\n } catch (error) {\n conflictComparisons.value[conflict.group] = {\n loading: false,\n error: error instanceof ApiError ? error.message : \"Could not load the comparison.\",\n comparison: null,\n };\n }\n}\n\nwatch(\n () => sync.value?.conflicts ?? [],\n (conflicts) => {\n for (const conflict of conflicts) {\n if (!conflictComparisons.value[conflict.group]) void loadConflictComparison(conflict);\n }\n },\n);\n\nfunction since(iso: string): string {\n return new Date(iso).toLocaleString();\n}\n\nfunction short(revision: string): string {\n return /^[0-9a-f]{20,}$/i.test(revision) ? revision.slice(0, 7) : revision;\n}\n</script>\n\n<template>\n <PageHeader title=\"Publish\" :subtitle=\"subtitle\">\n <button type=\"button\" class=\"btn-ghost px-2\" :disabled=\"loading\" @click=\"refresh\">\n <RefreshCw :size=\"16\" aria-hidden=\"true\" />\n <span class=\"sr-only\">Refresh the change list</span>\n </button>\n </PageHeader>\n\n <div class=\"mx-auto max-w-3xl space-y-5 px-4 py-6 sm:px-6\">\n <p v-if=\"loading\" class=\"text-sm text-muted\">Loading…</p>\n\n <template v-else-if=\"status\">\n <!-- Publishing unavailable: still an honest, read-only change list. -->\n <div\n v-if=\"!status.publish.enabled\"\n class=\"rounded-md border border-line bg-subtle p-4 text-sm text-muted\"\n >\n <p class=\"font-medium text-ink\">Publishing is not available here</p>\n <p class=\"mt-1\">{{ status.publish.reason }}</p>\n </div>\n\n <!-- Mica's own clone: shown only when something needs attention. -->\n <template v-if=\"sync\">\n <div v-if=\"sync.restartRequired\" role=\"alert\" class=\"rounded-md border border-warning/15 bg-warning-soft p-4\">\n <p class=\"text-sm font-medium text-warning\">The content model changed on the website</p>\n <p class=\"mt-1 text-xs text-muted\">\n You can go on saving. To publish, ask the person who runs Mica to restart it.\n </p>\n </div>\n\n <div v-if=\"sync.error\" role=\"alert\" class=\"rounded-md border border-warning/15 bg-warning-soft p-4\">\n <p class=\"text-sm font-medium text-warning\">Cannot reach the repository since {{ since(sync.error.since) }}</p>\n <p class=\"mt-1 text-xs text-muted\">\n You can go on saving. Mica tries again every minute. If this stays, tell the person who runs Mica:\n {{ sync.error.message }}\n </p>\n <button type=\"button\" class=\"btn-secondary mt-2.5\" :disabled=\"syncing\" @click=\"syncNow\">\n {{ syncing ? \"Trying…\" : \"Try again\" }}\n </button>\n </div>\n\n <div v-if=\"sync.pendingPush\" role=\"status\" class=\"rounded-md border border-line bg-subtle p-4\">\n <p class=\"text-sm font-medium\">Published in Mica, not yet sent to the website</p>\n <p class=\"mt-1 text-xs text-muted\">\n Mica tries again every minute. If this stays, tell the person who runs Mica:\n {{ sync.pendingPush.reason }}\n </p>\n </div>\n\n <div v-if=\"sync.refused\" role=\"alert\" class=\"rounded-md border border-danger/15 bg-danger-soft p-4\">\n <p class=\"text-sm font-medium text-danger\">The repository refused the last publication</p>\n <p class=\"mt-1 text-xs text-muted\">\n Its changes are drafts again, listed below. Tell the person who runs Mica: {{ sync.refused.reason }}\n </p>\n </div>\n\n <section v-if=\"sync.conflicts.length > 0\" aria-labelledby=\"conflicts-heading\" class=\"space-y-3\">\n <h2 id=\"conflicts-heading\" class=\"text-sm font-semibold\">\n Changed on the website while you edited\n </h2>\n <p class=\"text-xs text-muted\">\n Choose a version for each. Until you do, nothing can be published and the website's newer\n changes stay out of Mica.\n </p>\n <ul class=\"card divide-y divide-line\">\n <li v-for=\"conflict in sync.conflicts\" :key=\"conflict.group\" class=\"p-4\">\n <p class=\"text-sm font-medium\">{{ conflict.label }}</p>\n <p class=\"mt-0.5 text-xs text-muted\">It {{ upstreamWords[conflict.upstream] }}.</p>\n\n <template v-if=\"conflict.target\">\n <p v-if=\"conflictComparisons[conflict.group]?.loading\" class=\"mt-2 text-xs text-muted\">Comparing…</p>\n <p v-else-if=\"conflictComparisons[conflict.group]?.error\" class=\"mt-2 text-xs text-danger\">\n {{ conflictComparisons[conflict.group]?.error }}\n </p>\n <template v-else-if=\"conflictComparisons[conflict.group]?.comparison\">\n <p class=\"mt-2 text-xs text-muted\">The website's version, then yours:</p>\n <ChangeComparison\n :comparison=\"conflictComparisons[conflict.group]!.comparison!\"\n :fields=\"fieldsFor(conflict.target)\"\n />\n </template>\n </template>\n\n <p v-if=\"confirmWebsite === conflict\" role=\"alert\" class=\"mt-2 text-xs text-danger\">\n This deletes the entry you created. Press the button again to confirm.\n </p>\n <div class=\"mt-2.5 flex flex-wrap gap-2\">\n <button\n type=\"button\"\n class=\"btn-secondary\"\n :disabled=\"resolving !== null\"\n @click=\"resolve(conflict, 'mine')\"\n >\n {{ conflict.upstream === \"deleted\" ? \"Restore it\" : \"Keep my version\" }}\n </button>\n <button\n type=\"button\"\n class=\"btn-ghost\"\n :disabled=\"resolving !== null\"\n @click=\"resolve(conflict, 'website')\"\n >\n {{ conflict.upstream === \"deleted\" ? \"Accept the deletion\" : \"Use the website's version\" }}\n </button>\n </div>\n </li>\n </ul>\n </section>\n </template>\n\n <!-- A commit exists that the repository's remote has not seen. -->\n <div\n v-if=\"waitingToPush\"\n role=\"alert\"\n class=\"rounded-md border border-warning/15 bg-warning-soft p-4\"\n >\n <p class=\"text-sm font-medium text-warning\">Committed, waiting to be pushed</p>\n <p class=\"mt-1 text-xs text-muted\">\n {{\n pendingPush?.pushError ??\n \"This branch has commits the remote has not seen. Retrying pushes the existing \" +\n \"commits — it never creates new ones.\"\n }}\n </p>\n <button\n type=\"button\"\n class=\"btn-secondary mt-2.5\"\n :disabled=\"pushing\"\n @click=\"retryPush\"\n >\n {{ pushing ? \"Pushing…\" : \"Retry push\" }}\n </button>\n </div>\n\n <div\n v-if=\"!status.validation.ok\"\n role=\"alert\"\n class=\"rounded-md border border-danger/15 bg-danger-soft p-4\"\n >\n <p class=\"text-sm font-medium text-danger\">\n These changes cannot be published until they are fixed\n </p>\n <ul class=\"mt-1.5 space-y-1 text-xs text-danger\">\n <li v-for=\"problem in status.validation.problems\" :key=\"problem.path\">\n <span class=\"font-medium\">{{ problem.label }}</span>\n <template v-if=\"problem.issues.length\">\n — {{ problem.issues.map((issue) => issue.message).join(\"; \") }}\n </template>\n </li>\n </ul>\n </div>\n\n <div v-if=\"edits.length === 0\" class=\"card p-8 text-center\">\n <p class=\"text-sm font-medium\">Nothing waiting to be published</p>\n <p class=\"mt-1 text-sm text-muted\">\n Saved changes collect here until somebody publishes them. Right now the\n {{ isTree ? \"working tree\" : \"draft workspace\" }} matches the repository.\n </p>\n </div>\n\n <section v-else aria-labelledby=\"changes-heading\">\n <h2 id=\"changes-heading\" class=\"sr-only\">Pending changes</h2>\n\n <ul class=\"card divide-y divide-line\">\n <li v-for=\"edit in edits\" :key=\"edit.key\" class=\"flex items-start gap-3 p-4\">\n <div class=\"min-w-0 flex-1\">\n <component\n :is=\"edit.link ? 'RouterLink' : 'p'\"\n v-bind=\"edit.link ? { to: edit.link } : {}\"\n :class=\"['text-sm font-medium', edit.link && 'hover:underline']\"\n >\n {{ edit.label }}\n </component>\n\n <!-- What the change does. Edits with a document get the\n before/after view and no file paths; media and everything\n else have only their files to show. -->\n <template v-if=\"edit.target\">\n <p v-if=\"comparisons[edit.key]?.loading\" class=\"mt-2 text-xs text-muted\">\n Comparing…\n </p>\n <p v-else-if=\"comparisons[edit.key]?.error\" class=\"mt-2 text-xs text-danger\">\n {{ comparisons[edit.key]?.error }}\n </p>\n <ChangeComparison\n v-else-if=\"comparisons[edit.key]?.comparison\"\n :comparison=\"comparisons[edit.key]!.comparison!\"\n :fields=\"fieldsFor(edit.target)\"\n />\n </template>\n\n <ul v-else class=\"mt-1 space-y-0.5\">\n <li\n v-for=\"change in edit.fileChanges\"\n :key=\"change.path\"\n class=\"flex items-baseline gap-2 text-xs text-muted\"\n >\n <span\n :class=\"[\n 'w-16 shrink-0 font-mono text-[11px]',\n change.kind === 'delete' ? 'text-danger' : change.kind === 'create' ? 'text-success' : '',\n ]\"\n >\n {{ kindWord[change.kind] }}\n </span>\n <span class=\"break-all font-mono\">{{ change.path }}</span>\n </li>\n </ul>\n </div>\n\n <button type=\"button\" class=\"btn-ghost shrink-0 text-xs\" @click=\"discardTarget = edit\">\n Discard\n </button>\n </li>\n </ul>\n </section>\n\n <section v-if=\"edits.length > 0 && status.publish.enabled\" class=\"card p-4\" aria-labelledby=\"publish-heading\">\n <h2 id=\"publish-heading\" class=\"text-sm font-semibold\">\n Publish {{ edits.length === 1 ? \"this edit\" : `these ${edits.length} edits` }}\n </h2>\n\n <p class=\"mt-1 text-xs text-muted\">\n {{\n isTree\n ? \"One commit of exactly the files listed above, pushed with your own Git configuration.\"\n : \"One commit to the content repository, which is what triggers your site's build.\"\n }}\n <template v-if=\"isTree && status.head\"> Based on {{ short(status.head) }}.</template>\n </p>\n\n <form class=\"mt-3 flex flex-wrap items-center gap-2\" @submit.prevent=\"canPublish && message.trim() && publish()\">\n <input\n v-model=\"message\"\n type=\"text\"\n class=\"input min-w-0 flex-1\"\n maxlength=\"500\"\n placeholder=\"Describe this publication — it becomes the commit message\"\n aria-label=\"Commit message\"\n :disabled=\"publishing\"\n />\n <button type=\"submit\" class=\"btn-primary\" :disabled=\"!canPublish || !message.trim()\">\n {{ publishing ? \"Publishing…\" : \"Publish\" }}\n </button>\n </form>\n </section>\n </template>\n\n <p v-else class=\"text-sm text-muted\">The change list could not be loaded. Reload to retry.</p>\n </div>\n\n <ConfirmDialog\n :open=\"discardTarget !== null\"\n :title=\"`Discard the changes to “${discardTarget?.label}”?`\"\n confirm-label=\"Discard\"\n destructive\n :busy=\"discarding\"\n @cancel=\"discardTarget = null\"\n @confirm=\"discard\"\n >\n <p v-if=\"isTree\">\n This restores the files in your working tree to the last commit — new files are deleted,\n edited ones go back. It cannot be undone from here.\n </p>\n <p v-else>\n This forgets the saved changes; the repository stays exactly as it is. It cannot be undone\n from here.\n </p>\n </ConfirmDialog>\n</template>\n"],"names":["__iconNode","RefreshCw","createLucideIcon","compareRows","fields","before","after","walk","path","label","rows","name","field","a","b","at","named","humanise","asRecord","repeaterRows","asList","canonical","displayValue","itemLabel","present","count","index","caption","item","value","entry","_a","option","values","point","_key","record","sorted","key","props","__props","mode","computed","order","side","data","moved","positions","_openBlock","_createElementBlock","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_createElementVNode","_cache","_hoisted_5","_Fragment","_renderList","_toDisplayString","_hoisted_6","_hoisted_7","_hoisted_8","row","_hoisted_9","part","_hoisted_10","_hoisted_11","_createTextVNode","_hoisted_12","schema","useSchema","staging","useStaging","toast","useToast","loading","ref","publishing","pushing","message","discardTarget","discarding","pendingPush","status","edits","comparisons","fieldsFor","target","_b","loadComparison","edit","comparison","api","error","ApiError","watch","next","isTree","sync","subtitle","waitingToPush","canPublish","_c","kindWord","refresh","onMounted","publish","reviewed","change","outcome","short","retryPush","discard","syncing","resolving","confirmWebsite","syncNow","discardsNewWork","conflict","created","resolve","choice","upstreamWords","conflictComparisons","loadConflictComparison","conflicts","since","iso","revision","_createVNode","PageHeader","_unref","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","_hoisted_18","_hoisted_19","_hoisted_20","_d","ChangeComparison","_hoisted_21","_hoisted_22","$event","_hoisted_23","_hoisted_24","_hoisted_25","_hoisted_26","_hoisted_27","_hoisted_28","_hoisted_29","problem","_hoisted_30","issue","_hoisted_31","_hoisted_32","_hoisted_33","_hoisted_34","_hoisted_35","_createBlock","_resolveDynamicComponent","_mergeProps","_hoisted_36","_hoisted_37","_hoisted_38","_normalizeClass","_hoisted_39","_hoisted_40","_hoisted_41","_hoisted_42","_hoisted_43","_hoisted_45","_hoisted_46","ConfirmDialog"],"mappings":"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","x_google_ignoreList":[0]}
@@ -1,2 +1,2 @@
1
- import{d as R,o as z,q as D,c as o,j as p,w as _,a as s,s as j,v as E,t as r,x as P,b as L,F as y,k as $,r as x,g as b,i as B,A as F,p as U,n as q,h as a,l as g,e as N,y as J}from"./index-Colfqb-9.js";import{_ as O}from"./PageHeader.vue_vue_type_script_setup_true_lang-B-fBJAjn.js";import{p as G}from"./format-Cpnr2As6.js";import{P as H}from"./plus-D_aoCFbi.js";import{A as I,a as K}from"./arrow-up-CibOjHj-.js";const Q={class:"mx-auto max-w-5xl px-4 py-5 sm:px-6"},W={class:"mb-3 flex flex-wrap items-center gap-2"},X=["placeholder","aria-label"],Y={class:"card overflow-hidden"},Z={key:0,class:"px-4 py-8 text-center text-sm text-muted"},ee={key:1,class:"px-4 py-12 text-center"},te={class:"text-sm font-medium"},se={key:2,class:"w-full text-sm"},le={class:"sr-only"},ae={class:"border-b border-line text-left text-xs text-muted"},oe={key:0,scope:"col",class:"px-2 py-2"},ne={class:"divide-y divide-line"},ie={class:"px-4 py-2.5"},re={class:"block font-mono text-xs text-muted"},ce={key:0,class:"px-2 py-2.5"},ue={class:"flex justify-end gap-0.5"},de=["disabled","onClick"],pe={class:"sr-only"},me=["disabled","onClick"],ve={class:"sr-only"},he={key:1,class:"p-8 text-center text-sm text-muted"},ge=R({__name:"CollectionView",props:{collection:{}},setup(m){const v=m,M=J(),S=q(),c=x([]),w=x(!0),d=x(""),h=x(!1),n=b(()=>M.collection(v.collection)),V=b(()=>{const t=n.value;return t?t.listedFields.filter(l=>l!==t.titleField):[]}),k=b(()=>{const t=d.value.trim().toLowerCase();return t?c.value.filter(l=>l.title.toLowerCase().includes(t)||l.slug.includes(t)||JSON.stringify(l.data).toLowerCase().includes(t)):c.value});async function C(){w.value=!0;try{c.value=(await B.entries.list(v.collection)).items}catch(t){S.error(t instanceof F?t.message:"Could not load the entries.")}finally{w.value=!1}}z(C),D(()=>v.collection,C);async function A(t,l){const u=t+l;if(u<0||u>=c.value.length)return;const e=[...c.value];[e[t],e[u]]=[e[u],e[t]],c.value=e,h.value=!0;try{await B.entries.reorder(v.collection,e.map(i=>i.slug))}catch(i){S.error(i instanceof F?i.message:"Could not save the new order."),await C()}finally{h.value=!1}}const T=b(()=>{var t;return(((t=n.value)==null?void 0:t.sortable)??!1)&&!d.value});return(t,l)=>{const u=U("RouterLink");return n.value?(a(),o(y,{key:0},[p(O,{title:n.value.label,subtitle:n.value.description??`${c.value.length} ${c.value.length===1?"entry":"entries"}`},{default:_(()=>[p(u,{to:{name:"entry-new",params:{collection:m.collection}},class:"btn-primary"},{default:_(()=>[p(g(H),{size:16,"aria-hidden":"true"}),N(" New "+r(n.value.labelSingular.toLowerCase()),1)]),_:1},8,["to"])]),_:1},8,["title","subtitle"]),s("div",Q,[s("div",W,[j(s("input",{"onUpdate:modelValue":l[0]||(l[0]=e=>d.value=e),type:"search",placeholder:`Search ${n.value.label.toLowerCase()}…`,class:"input max-w-64","aria-label":`Search ${n.value.label}`},null,8,X),[[E,d.value]])]),s("div",Y,[w.value?(a(),o("p",Z,"Loading…")):k.value.length===0?(a(),o("div",ee,[s("p",te,r(d.value?`Nothing matches "${d.value}".`:`No ${n.value.label.toLowerCase()} yet.`),1),d.value?L("",!0):(a(),P(u,{key:0,to:{name:"entry-new",params:{collection:m.collection}},class:"btn-primary mt-4"},{default:_(()=>[N(" Create the first "+r(n.value.labelSingular.toLowerCase()),1)]),_:1},8,["to"]))])):(a(),o("table",se,[s("caption",le,r(n.value.label),1),s("thead",null,[s("tr",ae,[l[2]||(l[2]=s("th",{scope:"col",class:"px-4 py-2 font-medium"},"Title",-1)),(a(!0),o(y,null,$(V.value,e=>{var i;return a(),o("th",{key:e,scope:"col",class:"hidden px-4 py-2 font-medium md:table-cell"},r(((i=n.value.fields[e])==null?void 0:i.label)??e),1)}),128)),T.value?(a(),o("th",oe,[...l[1]||(l[1]=[s("span",{class:"sr-only"},"Reorder",-1)])])):L("",!0)])]),s("tbody",ne,[(a(!0),o(y,null,$(k.value,(e,i)=>(a(),o("tr",{key:e.slug,class:"transition hover:bg-canvas"},[s("td",ie,[p(u,{to:{name:"entry",params:{collection:m.collection,slug:e.slug}},class:"font-medium hover:underline"},{default:_(()=>[N(r(e.title),1)]),_:2},1032,["to"]),s("span",re,"/"+r(e.slug),1)]),(a(!0),o(y,null,$(V.value,f=>(a(),o("td",{key:f,class:"hidden px-4 py-2.5 text-muted md:table-cell"},r(g(G)(e.data[f])),1))),128)),T.value?(a(),o("td",ce,[s("div",ue,[s("button",{type:"button",class:"btn-ghost px-1 py-0.5",disabled:i===0||h.value,onClick:f=>A(i,-1)},[p(g(I),{size:16,"aria-hidden":"true"}),s("span",pe,'Move "'+r(e.title)+'" up',1)],8,de),s("button",{type:"button",class:"btn-ghost px-1 py-0.5",disabled:i===k.value.length-1||h.value,onClick:f=>A(i,1)},[p(g(K),{size:16,"aria-hidden":"true"}),s("span",ve,'Move "'+r(e.title)+'" down',1)],8,me)])])):L("",!0)]))),128))])]))])])],64)):(a(),o("div",he,' There is no collection called "'+r(m.collection)+'". ',1))}}});export{ge as default};
2
- //# sourceMappingURL=CollectionView-D3W4tRXa.js.map
1
+ import{d as R,o as z,q as D,c as o,j as p,w as _,a as s,s as j,v as E,t as r,x as P,b as L,F as y,k as $,r as x,g as b,i as B,A as F,p as U,n as q,h as a,l as g,e as N,y as J}from"./index-BsOOwFCf.js";import{_ as O}from"./PageHeader.vue_vue_type_script_setup_true_lang-DJWhe1Ye.js";import{p as G}from"./format-Cpnr2As6.js";import{P as H}from"./plus-CgaTSYYb.js";import{A as I,a as K}from"./arrow-up-Bdxg9_RJ.js";const Q={class:"mx-auto max-w-5xl px-4 py-5 sm:px-6"},W={class:"mb-3 flex flex-wrap items-center gap-2"},X=["placeholder","aria-label"],Y={class:"card overflow-hidden"},Z={key:0,class:"px-4 py-8 text-center text-sm text-muted"},ee={key:1,class:"px-4 py-12 text-center"},te={class:"text-sm font-medium"},se={key:2,class:"w-full text-sm"},le={class:"sr-only"},ae={class:"border-b border-line text-left text-xs text-muted"},oe={key:0,scope:"col",class:"px-2 py-2"},ne={class:"divide-y divide-line"},ie={class:"px-4 py-2.5"},re={class:"block font-mono text-xs text-muted"},ce={key:0,class:"px-2 py-2.5"},ue={class:"flex justify-end gap-0.5"},de=["disabled","onClick"],pe={class:"sr-only"},me=["disabled","onClick"],ve={class:"sr-only"},he={key:1,class:"p-8 text-center text-sm text-muted"},ge=R({__name:"CollectionView",props:{collection:{}},setup(m){const v=m,M=J(),S=q(),c=x([]),w=x(!0),d=x(""),h=x(!1),n=b(()=>M.collection(v.collection)),V=b(()=>{const t=n.value;return t?t.listedFields.filter(l=>l!==t.titleField):[]}),k=b(()=>{const t=d.value.trim().toLowerCase();return t?c.value.filter(l=>l.title.toLowerCase().includes(t)||l.slug.includes(t)||JSON.stringify(l.data).toLowerCase().includes(t)):c.value});async function C(){w.value=!0;try{c.value=(await B.entries.list(v.collection)).items}catch(t){S.error(t instanceof F?t.message:"Could not load the entries.")}finally{w.value=!1}}z(C),D(()=>v.collection,C);async function A(t,l){const u=t+l;if(u<0||u>=c.value.length)return;const e=[...c.value];[e[t],e[u]]=[e[u],e[t]],c.value=e,h.value=!0;try{await B.entries.reorder(v.collection,e.map(i=>i.slug))}catch(i){S.error(i instanceof F?i.message:"Could not save the new order."),await C()}finally{h.value=!1}}const T=b(()=>{var t;return(((t=n.value)==null?void 0:t.sortable)??!1)&&!d.value});return(t,l)=>{const u=U("RouterLink");return n.value?(a(),o(y,{key:0},[p(O,{title:n.value.label,subtitle:n.value.description??`${c.value.length} ${c.value.length===1?"entry":"entries"}`},{default:_(()=>[p(u,{to:{name:"entry-new",params:{collection:m.collection}},class:"btn-primary"},{default:_(()=>[p(g(H),{size:16,"aria-hidden":"true"}),N(" New "+r(n.value.labelSingular.toLowerCase()),1)]),_:1},8,["to"])]),_:1},8,["title","subtitle"]),s("div",Q,[s("div",W,[j(s("input",{"onUpdate:modelValue":l[0]||(l[0]=e=>d.value=e),type:"search",placeholder:`Search ${n.value.label.toLowerCase()}…`,class:"input max-w-64","aria-label":`Search ${n.value.label}`},null,8,X),[[E,d.value]])]),s("div",Y,[w.value?(a(),o("p",Z,"Loading…")):k.value.length===0?(a(),o("div",ee,[s("p",te,r(d.value?`Nothing matches "${d.value}".`:`No ${n.value.label.toLowerCase()} yet.`),1),d.value?L("",!0):(a(),P(u,{key:0,to:{name:"entry-new",params:{collection:m.collection}},class:"btn-primary mt-4"},{default:_(()=>[N(" Create the first "+r(n.value.labelSingular.toLowerCase()),1)]),_:1},8,["to"]))])):(a(),o("table",se,[s("caption",le,r(n.value.label),1),s("thead",null,[s("tr",ae,[l[2]||(l[2]=s("th",{scope:"col",class:"px-4 py-2 font-medium"},"Title",-1)),(a(!0),o(y,null,$(V.value,e=>{var i;return a(),o("th",{key:e,scope:"col",class:"hidden px-4 py-2 font-medium md:table-cell"},r(((i=n.value.fields[e])==null?void 0:i.label)??e),1)}),128)),T.value?(a(),o("th",oe,[...l[1]||(l[1]=[s("span",{class:"sr-only"},"Reorder",-1)])])):L("",!0)])]),s("tbody",ne,[(a(!0),o(y,null,$(k.value,(e,i)=>(a(),o("tr",{key:e.slug,class:"transition hover:bg-canvas"},[s("td",ie,[p(u,{to:{name:"entry",params:{collection:m.collection,slug:e.slug}},class:"font-medium hover:underline"},{default:_(()=>[N(r(e.title),1)]),_:2},1032,["to"]),s("span",re,"/"+r(e.slug),1)]),(a(!0),o(y,null,$(V.value,f=>(a(),o("td",{key:f,class:"hidden px-4 py-2.5 text-muted md:table-cell"},r(g(G)(e.data[f])),1))),128)),T.value?(a(),o("td",ce,[s("div",ue,[s("button",{type:"button",class:"btn-ghost px-1 py-0.5",disabled:i===0||h.value,onClick:f=>A(i,-1)},[p(g(I),{size:16,"aria-hidden":"true"}),s("span",pe,'Move "'+r(e.title)+'" up',1)],8,de),s("button",{type:"button",class:"btn-ghost px-1 py-0.5",disabled:i===k.value.length-1||h.value,onClick:f=>A(i,1)},[p(g(K),{size:16,"aria-hidden":"true"}),s("span",ve,'Move "'+r(e.title)+'" down',1)],8,me)])])):L("",!0)]))),128))])]))])])],64)):(a(),o("div",he,' There is no collection called "'+r(m.collection)+'". ',1))}}});export{ge as default};
2
+ //# sourceMappingURL=CollectionView-BlLAhVVi.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CollectionView-D3W4tRXa.js","sources":["../../src/views/CollectionView.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Collection overview: a filterable list of entries.\n * Columns come from fields marked `listed` in the model.\n *\n * The status column and its filter are gone: an entry in the repository is\n * saved, and saved is published. The \"Updated\" column went with them — the\n * honest answer is the last commit that touched the file, which is the\n * repository's to give, not a timestamp Mica writes into the entry.\n */\n\nimport { computed, onMounted, ref, watch } from \"vue\";\nimport { ArrowDown, ArrowUp, Plus } from \"@lucide/vue\";\n\nimport { api, ApiError, type EntrySummary } from \"@/api\";\nimport PageHeader from \"@/components/PageHeader.vue\";\nimport { previewValue } from \"@/lib/format\";\nimport { useSchema } from \"@/stores/schema\";\nimport { useToast } from \"@/stores/toast\";\n\nconst props = defineProps<{ collection: string }>();\n\nconst schema = useSchema();\nconst toast = useToast();\n\nconst entries = ref<EntrySummary[]>([]);\nconst loading = ref(true);\nconst search = ref(\"\");\nconst reordering = ref(false);\n\nconst definition = computed(() => schema.collection(props.collection));\nconst columns = computed(() => {\n const def = definition.value;\n if (!def) return [];\n // The title already has its own column, so do not repeat it.\n return def.listedFields.filter((key) => key !== def.titleField);\n});\n\nconst matching = computed(() => {\n const term = search.value.trim().toLowerCase();\n if (!term) return entries.value;\n\n // In the browser: the whole collection is already here, and a round trip per\n // keystroke would only add latency.\n return entries.value.filter(\n (entry) =>\n entry.title.toLowerCase().includes(term) ||\n entry.slug.includes(term) ||\n JSON.stringify(entry.data).toLowerCase().includes(term),\n );\n});\n\nasync function load(): Promise<void> {\n loading.value = true;\n try {\n entries.value = (await api.entries.list(props.collection)).items;\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : \"Could not load the entries.\");\n } finally {\n loading.value = false;\n }\n}\n\nonMounted(load);\nwatch(() => props.collection, load);\n\n/**\n * Manual ordering.\n *\n * The order is a `_order.json` file listing slugs, so moving one entry is a\n * one-line diff rather than a rewrite of every entry in the collection.\n */\nasync function move(index: number, direction: -1 | 1): Promise<void> {\n const target = index + direction;\n if (target < 0 || target >= entries.value.length) return;\n\n const next = [...entries.value];\n [next[index], next[target]] = [next[target]!, next[index]!];\n entries.value = next;\n\n reordering.value = true;\n try {\n await api.entries.reorder(\n props.collection,\n next.map((entry) => entry.slug),\n );\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : \"Could not save the new order.\");\n await load();\n } finally {\n reordering.value = false;\n }\n}\n\n// Reordering a filtered list would move the wrong neighbours.\nconst canReorder = computed(() => (definition.value?.sortable ?? false) && !search.value);\n</script>\n\n<template>\n <template v-if=\"definition\">\n <PageHeader\n :title=\"definition.label\"\n :subtitle=\"\n definition.description ??\n `${entries.length} ${entries.length === 1 ? 'entry' : 'entries'}`\n \"\n >\n <RouterLink :to=\"{ name: 'entry-new', params: { collection } }\" class=\"btn-primary\">\n <Plus :size=\"16\" aria-hidden=\"true\" />\n New {{ definition.labelSingular.toLowerCase() }}\n </RouterLink>\n </PageHeader>\n\n <div class=\"mx-auto max-w-5xl px-4 py-5 sm:px-6\">\n <div class=\"mb-3 flex flex-wrap items-center gap-2\">\n <input\n v-model=\"search\"\n type=\"search\"\n :placeholder=\"`Search ${definition.label.toLowerCase()}…`\"\n class=\"input max-w-64\"\n :aria-label=\"`Search ${definition.label}`\"\n />\n </div>\n\n <div class=\"card overflow-hidden\">\n <p v-if=\"loading\" class=\"px-4 py-8 text-center text-sm text-muted\">Loading…</p>\n\n <div v-else-if=\"matching.length === 0\" class=\"px-4 py-12 text-center\">\n <p class=\"text-sm font-medium\">\n {{ search ? `Nothing matches \"${search}\".` : `No ${definition.label.toLowerCase()} yet.` }}\n </p>\n <RouterLink\n v-if=\"!search\"\n :to=\"{ name: 'entry-new', params: { collection } }\"\n class=\"btn-primary mt-4\"\n >\n Create the first {{ definition.labelSingular.toLowerCase() }}\n </RouterLink>\n </div>\n\n <table v-else class=\"w-full text-sm\">\n <caption class=\"sr-only\">{{ definition.label }}</caption>\n <thead>\n <tr class=\"border-b border-line text-left text-xs text-muted\">\n <th scope=\"col\" class=\"px-4 py-2 font-medium\">Title</th>\n <th\n v-for=\"column in columns\"\n :key=\"column\"\n scope=\"col\"\n class=\"hidden px-4 py-2 font-medium md:table-cell\"\n >\n {{ definition.fields[column]?.label ?? column }}\n </th>\n <th v-if=\"canReorder\" scope=\"col\" class=\"px-2 py-2\">\n <span class=\"sr-only\">Reorder</span>\n </th>\n </tr>\n </thead>\n\n <tbody class=\"divide-y divide-line\">\n <tr v-for=\"(entry, index) in matching\" :key=\"entry.slug\" class=\"transition hover:bg-canvas\">\n <td class=\"px-4 py-2.5\">\n <RouterLink\n :to=\"{ name: 'entry', params: { collection, slug: entry.slug } }\"\n class=\"font-medium hover:underline\"\n >\n {{ entry.title }}\n </RouterLink>\n <span class=\"block font-mono text-xs text-muted\">/{{ entry.slug }}</span>\n </td>\n\n <td\n v-for=\"column in columns\"\n :key=\"column\"\n class=\"hidden px-4 py-2.5 text-muted md:table-cell\"\n >\n {{ previewValue(entry.data[column]) }}\n </td>\n\n <td v-if=\"canReorder\" class=\"px-2 py-2.5\">\n <div class=\"flex justify-end gap-0.5\">\n <button\n type=\"button\"\n class=\"btn-ghost px-1 py-0.5\"\n :disabled=\"index === 0 || reordering\"\n @click=\"move(index, -1)\"\n >\n <ArrowUp :size=\"16\" aria-hidden=\"true\" />\n <span class=\"sr-only\">Move \"{{ entry.title }}\" up</span>\n </button>\n <button\n type=\"button\"\n class=\"btn-ghost px-1 py-0.5\"\n :disabled=\"index === matching.length - 1 || reordering\"\n @click=\"move(index, 1)\"\n >\n <ArrowDown :size=\"16\" aria-hidden=\"true\" />\n <span class=\"sr-only\">Move \"{{ entry.title }}\" down</span>\n </button>\n </div>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n </template>\n\n <div v-else class=\"p-8 text-center text-sm text-muted\">\n There is no collection called \"{{ collection }}\".\n </div>\n</template>\n"],"names":["props","__props","schema","useSchema","toast","useToast","entries","ref","loading","search","reordering","definition","computed","columns","def","key","matching","term","entry","load","api","error","ApiError","onMounted","watch","move","index","direction","target","next","canReorder","_a","_createElementBlock","_Fragment","_createVNode","PageHeader","_component_RouterLink","_unref","Plus","_createTextVNode","_toDisplayString","_createElementVNode","_hoisted_1","_hoisted_2","$event","_hoisted_4","_hoisted_5","_openBlock","_hoisted_6","_hoisted_7","_createBlock","_hoisted_8","_hoisted_9","_hoisted_10","_renderList","column","_hoisted_11","_cache","_hoisted_12","_hoisted_13","_hoisted_14","previewValue","_hoisted_15","_hoisted_16","ArrowUp","_hoisted_18","ArrowDown","_hoisted_20","_hoisted_21"],"mappings":"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"}
1
+ {"version":3,"file":"CollectionView-BlLAhVVi.js","sources":["../../src/views/CollectionView.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Collection overview: a filterable list of entries.\n * Columns come from fields marked `listed` in the model.\n *\n * The status column and its filter are gone: an entry in the repository is\n * saved, and saved is published. The \"Updated\" column went with them — the\n * honest answer is the last commit that touched the file, which is the\n * repository's to give, not a timestamp Mica writes into the entry.\n */\n\nimport { computed, onMounted, ref, watch } from \"vue\";\nimport { ArrowDown, ArrowUp, Plus } from \"@lucide/vue\";\n\nimport { api, ApiError, type EntrySummary } from \"@/api\";\nimport PageHeader from \"@/components/PageHeader.vue\";\nimport { previewValue } from \"@/lib/format\";\nimport { useSchema } from \"@/stores/schema\";\nimport { useToast } from \"@/stores/toast\";\n\nconst props = defineProps<{ collection: string }>();\n\nconst schema = useSchema();\nconst toast = useToast();\n\nconst entries = ref<EntrySummary[]>([]);\nconst loading = ref(true);\nconst search = ref(\"\");\nconst reordering = ref(false);\n\nconst definition = computed(() => schema.collection(props.collection));\nconst columns = computed(() => {\n const def = definition.value;\n if (!def) return [];\n // The title already has its own column, so do not repeat it.\n return def.listedFields.filter((key) => key !== def.titleField);\n});\n\nconst matching = computed(() => {\n const term = search.value.trim().toLowerCase();\n if (!term) return entries.value;\n\n // In the browser: the whole collection is already here, and a round trip per\n // keystroke would only add latency.\n return entries.value.filter(\n (entry) =>\n entry.title.toLowerCase().includes(term) ||\n entry.slug.includes(term) ||\n JSON.stringify(entry.data).toLowerCase().includes(term),\n );\n});\n\nasync function load(): Promise<void> {\n loading.value = true;\n try {\n entries.value = (await api.entries.list(props.collection)).items;\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : \"Could not load the entries.\");\n } finally {\n loading.value = false;\n }\n}\n\nonMounted(load);\nwatch(() => props.collection, load);\n\n/**\n * Manual ordering.\n *\n * The order is a `_order.json` file listing slugs, so moving one entry is a\n * one-line diff rather than a rewrite of every entry in the collection.\n */\nasync function move(index: number, direction: -1 | 1): Promise<void> {\n const target = index + direction;\n if (target < 0 || target >= entries.value.length) return;\n\n const next = [...entries.value];\n [next[index], next[target]] = [next[target]!, next[index]!];\n entries.value = next;\n\n reordering.value = true;\n try {\n await api.entries.reorder(\n props.collection,\n next.map((entry) => entry.slug),\n );\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : \"Could not save the new order.\");\n await load();\n } finally {\n reordering.value = false;\n }\n}\n\n// Reordering a filtered list would move the wrong neighbours.\nconst canReorder = computed(() => (definition.value?.sortable ?? false) && !search.value);\n</script>\n\n<template>\n <template v-if=\"definition\">\n <PageHeader\n :title=\"definition.label\"\n :subtitle=\"\n definition.description ??\n `${entries.length} ${entries.length === 1 ? 'entry' : 'entries'}`\n \"\n >\n <RouterLink :to=\"{ name: 'entry-new', params: { collection } }\" class=\"btn-primary\">\n <Plus :size=\"16\" aria-hidden=\"true\" />\n New {{ definition.labelSingular.toLowerCase() }}\n </RouterLink>\n </PageHeader>\n\n <div class=\"mx-auto max-w-5xl px-4 py-5 sm:px-6\">\n <div class=\"mb-3 flex flex-wrap items-center gap-2\">\n <input\n v-model=\"search\"\n type=\"search\"\n :placeholder=\"`Search ${definition.label.toLowerCase()}…`\"\n class=\"input max-w-64\"\n :aria-label=\"`Search ${definition.label}`\"\n />\n </div>\n\n <div class=\"card overflow-hidden\">\n <p v-if=\"loading\" class=\"px-4 py-8 text-center text-sm text-muted\">Loading…</p>\n\n <div v-else-if=\"matching.length === 0\" class=\"px-4 py-12 text-center\">\n <p class=\"text-sm font-medium\">\n {{ search ? `Nothing matches \"${search}\".` : `No ${definition.label.toLowerCase()} yet.` }}\n </p>\n <RouterLink\n v-if=\"!search\"\n :to=\"{ name: 'entry-new', params: { collection } }\"\n class=\"btn-primary mt-4\"\n >\n Create the first {{ definition.labelSingular.toLowerCase() }}\n </RouterLink>\n </div>\n\n <table v-else class=\"w-full text-sm\">\n <caption class=\"sr-only\">{{ definition.label }}</caption>\n <thead>\n <tr class=\"border-b border-line text-left text-xs text-muted\">\n <th scope=\"col\" class=\"px-4 py-2 font-medium\">Title</th>\n <th\n v-for=\"column in columns\"\n :key=\"column\"\n scope=\"col\"\n class=\"hidden px-4 py-2 font-medium md:table-cell\"\n >\n {{ definition.fields[column]?.label ?? column }}\n </th>\n <th v-if=\"canReorder\" scope=\"col\" class=\"px-2 py-2\">\n <span class=\"sr-only\">Reorder</span>\n </th>\n </tr>\n </thead>\n\n <tbody class=\"divide-y divide-line\">\n <tr v-for=\"(entry, index) in matching\" :key=\"entry.slug\" class=\"transition hover:bg-canvas\">\n <td class=\"px-4 py-2.5\">\n <RouterLink\n :to=\"{ name: 'entry', params: { collection, slug: entry.slug } }\"\n class=\"font-medium hover:underline\"\n >\n {{ entry.title }}\n </RouterLink>\n <span class=\"block font-mono text-xs text-muted\">/{{ entry.slug }}</span>\n </td>\n\n <td\n v-for=\"column in columns\"\n :key=\"column\"\n class=\"hidden px-4 py-2.5 text-muted md:table-cell\"\n >\n {{ previewValue(entry.data[column]) }}\n </td>\n\n <td v-if=\"canReorder\" class=\"px-2 py-2.5\">\n <div class=\"flex justify-end gap-0.5\">\n <button\n type=\"button\"\n class=\"btn-ghost px-1 py-0.5\"\n :disabled=\"index === 0 || reordering\"\n @click=\"move(index, -1)\"\n >\n <ArrowUp :size=\"16\" aria-hidden=\"true\" />\n <span class=\"sr-only\">Move \"{{ entry.title }}\" up</span>\n </button>\n <button\n type=\"button\"\n class=\"btn-ghost px-1 py-0.5\"\n :disabled=\"index === matching.length - 1 || reordering\"\n @click=\"move(index, 1)\"\n >\n <ArrowDown :size=\"16\" aria-hidden=\"true\" />\n <span class=\"sr-only\">Move \"{{ entry.title }}\" down</span>\n </button>\n </div>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n </template>\n\n <div v-else class=\"p-8 text-center text-sm text-muted\">\n There is no collection called \"{{ collection }}\".\n </div>\n</template>\n"],"names":["props","__props","schema","useSchema","toast","useToast","entries","ref","loading","search","reordering","definition","computed","columns","def","key","matching","term","entry","load","api","error","ApiError","onMounted","watch","move","index","direction","target","next","canReorder","_a","_createElementBlock","_Fragment","_createVNode","PageHeader","_component_RouterLink","_unref","Plus","_createTextVNode","_toDisplayString","_createElementVNode","_hoisted_1","_hoisted_2","$event","_hoisted_4","_hoisted_5","_openBlock","_hoisted_6","_hoisted_7","_createBlock","_hoisted_8","_hoisted_9","_hoisted_10","_renderList","column","_hoisted_11","_cache","_hoisted_12","_hoisted_13","_hoisted_14","previewValue","_hoisted_15","_hoisted_16","ArrowUp","_hoisted_18","ArrowDown","_hoisted_20","_hoisted_21"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{d as k,q as v,x,c as g,z as p,a as l,t as d,E as C,C as B,b as h,T as w,r as u,h as b,H as E}from"./index-Colfqb-9.js";const D={id:"confirm-title",class:"text-base font-semibold"},L={id:"confirm-body",class:"mt-2 text-sm text-muted"},T={class:"mt-5 flex justify-end gap-2"},$=["disabled"],z=["disabled"],S=k({__name:"ConfirmDialog",props:{open:{type:Boolean},title:{},confirmLabel:{default:"Confirm"},cancelLabel:{default:"Cancel"},destructive:{type:Boolean,default:!1},busy:{type:Boolean,default:!1}},emits:["confirm","cancel"],setup(t,{emit:m}){const r=t,s=m,i=u(null),f=u(null);let o=null;v(()=>r.open,async n=>{var e;if(!n){o==null||o.focus();return}o=document.activeElement,await E(),(e=f.value)==null||e.focus()});function y(n){if(n.key==="Escape"&&!r.busy&&s("cancel"),n.key!=="Tab"||!i.value)return;const e=i.value.querySelectorAll("button:not([disabled])"),a=e[0],c=e[e.length-1];!a||!c||(n.shiftKey&&document.activeElement===a?(n.preventDefault(),c.focus()):!n.shiftKey&&document.activeElement===c&&(n.preventDefault(),a.focus()))}return(n,e)=>(b(),x(w,{to:"body"},[t.open?(b(),g("div",{key:0,class:"fixed inset-0 z-50 grid place-items-center bg-ink/40 p-4",onClick:e[2]||(e[2]=p(a=>!t.busy&&s("cancel"),["self"])),onKeydown:y},[l("div",{ref_key:"panel",ref:i,role:"alertdialog","aria-modal":"true","aria-labelledby":"confirm-title","aria-describedby":"confirm-body",class:"w-full max-w-md rounded-xl border border-line bg-raised p-5 shadow-2xl"},[l("h2",D,d(t.title),1),l("div",L,[C(n.$slots,"default")]),l("div",T,[l("button",{ref_key:"cancelButton",ref:f,type:"button",class:"btn-secondary",disabled:t.busy,onClick:e[0]||(e[0]=a=>s("cancel"))},d(t.cancelLabel),9,$),l("button",{type:"button",class:B(t.destructive?"btn-danger":"btn-primary"),disabled:t.busy,onClick:e[1]||(e[1]=a=>s("confirm"))},d(t.busy?"Working…":t.confirmLabel),11,z)])],512)],32)):h("",!0)]))}});export{S as _};
2
- //# sourceMappingURL=ConfirmDialog.vue_vue_type_script_setup_true_lang-BhOQwoeA.js.map
1
+ import{d as k,q as v,x,c as g,z as p,a as l,t as d,E as C,C as B,b as h,T as w,r as u,h as b,H as E}from"./index-BsOOwFCf.js";const D={id:"confirm-title",class:"text-base font-semibold"},L={id:"confirm-body",class:"mt-2 text-sm text-muted"},T={class:"mt-5 flex justify-end gap-2"},$=["disabled"],z=["disabled"],S=k({__name:"ConfirmDialog",props:{open:{type:Boolean},title:{},confirmLabel:{default:"Confirm"},cancelLabel:{default:"Cancel"},destructive:{type:Boolean,default:!1},busy:{type:Boolean,default:!1}},emits:["confirm","cancel"],setup(t,{emit:m}){const r=t,s=m,i=u(null),f=u(null);let o=null;v(()=>r.open,async n=>{var e;if(!n){o==null||o.focus();return}o=document.activeElement,await E(),(e=f.value)==null||e.focus()});function y(n){if(n.key==="Escape"&&!r.busy&&s("cancel"),n.key!=="Tab"||!i.value)return;const e=i.value.querySelectorAll("button:not([disabled])"),a=e[0],c=e[e.length-1];!a||!c||(n.shiftKey&&document.activeElement===a?(n.preventDefault(),c.focus()):!n.shiftKey&&document.activeElement===c&&(n.preventDefault(),a.focus()))}return(n,e)=>(b(),x(w,{to:"body"},[t.open?(b(),g("div",{key:0,class:"fixed inset-0 z-50 grid place-items-center bg-ink/40 p-4",onClick:e[2]||(e[2]=p(a=>!t.busy&&s("cancel"),["self"])),onKeydown:y},[l("div",{ref_key:"panel",ref:i,role:"alertdialog","aria-modal":"true","aria-labelledby":"confirm-title","aria-describedby":"confirm-body",class:"w-full max-w-md rounded-xl border border-line bg-raised p-5 shadow-2xl"},[l("h2",D,d(t.title),1),l("div",L,[C(n.$slots,"default")]),l("div",T,[l("button",{ref_key:"cancelButton",ref:f,type:"button",class:"btn-secondary",disabled:t.busy,onClick:e[0]||(e[0]=a=>s("cancel"))},d(t.cancelLabel),9,$),l("button",{type:"button",class:B(t.destructive?"btn-danger":"btn-primary"),disabled:t.busy,onClick:e[1]||(e[1]=a=>s("confirm"))},d(t.busy?"Working…":t.confirmLabel),11,z)])],512)],32)):h("",!0)]))}});export{S as _};
2
+ //# sourceMappingURL=ConfirmDialog.vue_vue_type_script_setup_true_lang-C073kW3p.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ConfirmDialog.vue_vue_type_script_setup_true_lang-BhOQwoeA.js","sources":["../../src/components/ConfirmDialog.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/** Confirmation dialog with focus handling and Escape-to-cancel. */\n\nimport { nextTick, ref, watch } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n open: boolean;\n title: string;\n confirmLabel?: string;\n cancelLabel?: string;\n destructive?: boolean;\n busy?: boolean;\n }>(),\n { confirmLabel: \"Confirm\", cancelLabel: \"Cancel\", destructive: false, busy: false },\n);\n\nconst emit = defineEmits<{ confirm: []; cancel: [] }>();\n\nconst panel = ref<HTMLElement | null>(null);\nconst cancelButton = ref<HTMLButtonElement | null>(null);\nlet previouslyFocused: HTMLElement | null = null;\n\nwatch(\n () => props.open,\n async (open) => {\n if (!open) {\n previouslyFocused?.focus();\n return;\n }\n previouslyFocused = document.activeElement as HTMLElement | null;\n await nextTick();\n // Focus the safe option, not the destructive one.\n cancelButton.value?.focus();\n },\n);\n\nfunction onKeydown(event: KeyboardEvent): void {\n if (event.key === \"Escape\" && !props.busy) emit(\"cancel\");\n if (event.key !== \"Tab\" || !panel.value) return;\n\n const focusable = panel.value.querySelectorAll<HTMLElement>(\"button:not([disabled])\");\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n if (!first || !last) return;\n\n if (event.shiftKey && document.activeElement === first) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && document.activeElement === last) {\n event.preventDefault();\n first.focus();\n }\n}\n</script>\n\n<template>\n <Teleport to=\"body\">\n <div\n v-if=\"open\"\n class=\"fixed inset-0 z-50 grid place-items-center bg-ink/40 p-4\"\n @click.self=\"!busy && emit('cancel')\"\n @keydown=\"onKeydown\"\n >\n <div\n ref=\"panel\"\n role=\"alertdialog\"\n aria-modal=\"true\"\n aria-labelledby=\"confirm-title\"\n aria-describedby=\"confirm-body\"\n class=\"w-full max-w-md rounded-xl border border-line bg-raised p-5 shadow-2xl\"\n >\n <h2 id=\"confirm-title\" class=\"text-base font-semibold\">{{ title }}</h2>\n\n <div id=\"confirm-body\" class=\"mt-2 text-sm text-muted\">\n <slot />\n </div>\n\n <div class=\"mt-5 flex justify-end gap-2\">\n <button\n ref=\"cancelButton\"\n type=\"button\"\n class=\"btn-secondary\"\n :disabled=\"busy\"\n @click=\"emit('cancel')\"\n >\n {{ cancelLabel }}\n </button>\n <button\n type=\"button\"\n :class=\"destructive ? 'btn-danger' : 'btn-primary'\"\n :disabled=\"busy\"\n @click=\"emit('confirm')\"\n >\n {{ busy ? \"Working…\" : confirmLabel }}\n </button>\n </div>\n </div>\n </div>\n </Teleport>\n</template>\n"],"names":["props","__props","emit","__emit","panel","ref","cancelButton","previouslyFocused","watch","open","nextTick","_a","onKeydown","event","focusable","first","last","_createBlock","_Teleport","_createElementBlock","_cache","_withModifiers","$event","_createElementVNode","_hoisted_1","_toDisplayString","_hoisted_2","_renderSlot","_ctx","_hoisted_3","_hoisted_4","_hoisted_5"],"mappings":"0iBAKA,MAAMA,EAAQC,EAYRC,EAAOC,EAEPC,EAAQC,EAAwB,IAAI,EACpCC,EAAeD,EAA8B,IAAI,EACvD,IAAIE,EAAwC,KAE5CC,EACE,IAAMR,EAAM,KACZ,MAAOS,GAAS,OACd,GAAI,CAACA,EAAM,CACTF,GAAA,MAAAA,EAAmB,QACnB,MACF,CACAA,EAAoB,SAAS,cAC7B,MAAMG,EAAA,GAENC,EAAAL,EAAa,QAAb,MAAAK,EAAoB,OACtB,CAAA,EAGF,SAASC,EAAUC,EAA4B,CAE7C,GADIA,EAAM,MAAQ,UAAY,CAACb,EAAM,QAAW,QAAQ,EACpDa,EAAM,MAAQ,OAAS,CAACT,EAAM,MAAO,OAEzC,MAAMU,EAAYV,EAAM,MAAM,iBAA8B,wBAAwB,EAC9EW,EAAQD,EAAU,CAAC,EACnBE,EAAOF,EAAUA,EAAU,OAAS,CAAC,EACvC,CAACC,GAAS,CAACC,IAEXH,EAAM,UAAY,SAAS,gBAAkBE,GAC/CF,EAAM,eAAA,EACNG,EAAK,MAAA,GACI,CAACH,EAAM,UAAY,SAAS,gBAAkBG,IACvDH,EAAM,eAAA,EACNE,EAAM,MAAA,GAEV,mBAIEE,EA0CWC,EAAA,CA1CD,GAAG,QAAM,CAETjB,EAAA,UADRkB,EAwCM,MAAA,OAtCJ,MAAM,2DACL,QAAKC,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAAC,EAAAC,GAAA,CAAQrB,EAAA,MAAQC,EAAI,QAAA,EAAA,CAAA,MAAA,CAAA,GACzB,UAAAU,CAAA,GAEDW,EAiCM,MAAA,SAhCA,QAAJ,IAAInB,EACJ,KAAK,cACL,aAAW,OACX,kBAAgB,gBAChB,mBAAiB,eACjB,MAAM,wEAAA,GAENmB,EAAuE,KAAvEC,EAAuEC,EAAbxB,EAAA,KAAK,EAAA,CAAA,EAE/DsB,EAEM,MAFNG,EAEM,CADJC,EAAQC,EAAA,OAAA,SAAA,CAAA,GAGVL,EAkBM,MAlBNM,EAkBM,CAjBJN,EAQS,SAAA,SAPH,eAAJ,IAAIjB,EACJ,KAAK,SACL,MAAM,gBACL,SAAUL,EAAA,KACV,uBAAOC,EAAI,QAAA,EAAA,IAETD,EAAA,WAAW,EAAA,EAAA6B,CAAA,EAEhBP,EAOS,SAAA,CANP,KAAK,SACJ,QAAOtB,EAAA,YAAW,aAAA,aAAA,EAClB,SAAUA,EAAA,KACV,uBAAOC,EAAI,SAAA,EAAA,EAETuB,EAAAxB,EAAA,gBAAoBA,EAAA,YAAY,EAAA,GAAA8B,CAAA,CAAA"}
1
+ {"version":3,"file":"ConfirmDialog.vue_vue_type_script_setup_true_lang-C073kW3p.js","sources":["../../src/components/ConfirmDialog.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/** Confirmation dialog with focus handling and Escape-to-cancel. */\n\nimport { nextTick, ref, watch } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n open: boolean;\n title: string;\n confirmLabel?: string;\n cancelLabel?: string;\n destructive?: boolean;\n busy?: boolean;\n }>(),\n { confirmLabel: \"Confirm\", cancelLabel: \"Cancel\", destructive: false, busy: false },\n);\n\nconst emit = defineEmits<{ confirm: []; cancel: [] }>();\n\nconst panel = ref<HTMLElement | null>(null);\nconst cancelButton = ref<HTMLButtonElement | null>(null);\nlet previouslyFocused: HTMLElement | null = null;\n\nwatch(\n () => props.open,\n async (open) => {\n if (!open) {\n previouslyFocused?.focus();\n return;\n }\n previouslyFocused = document.activeElement as HTMLElement | null;\n await nextTick();\n // Focus the safe option, not the destructive one.\n cancelButton.value?.focus();\n },\n);\n\nfunction onKeydown(event: KeyboardEvent): void {\n if (event.key === \"Escape\" && !props.busy) emit(\"cancel\");\n if (event.key !== \"Tab\" || !panel.value) return;\n\n const focusable = panel.value.querySelectorAll<HTMLElement>(\"button:not([disabled])\");\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n if (!first || !last) return;\n\n if (event.shiftKey && document.activeElement === first) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && document.activeElement === last) {\n event.preventDefault();\n first.focus();\n }\n}\n</script>\n\n<template>\n <Teleport to=\"body\">\n <div\n v-if=\"open\"\n class=\"fixed inset-0 z-50 grid place-items-center bg-ink/40 p-4\"\n @click.self=\"!busy && emit('cancel')\"\n @keydown=\"onKeydown\"\n >\n <div\n ref=\"panel\"\n role=\"alertdialog\"\n aria-modal=\"true\"\n aria-labelledby=\"confirm-title\"\n aria-describedby=\"confirm-body\"\n class=\"w-full max-w-md rounded-xl border border-line bg-raised p-5 shadow-2xl\"\n >\n <h2 id=\"confirm-title\" class=\"text-base font-semibold\">{{ title }}</h2>\n\n <div id=\"confirm-body\" class=\"mt-2 text-sm text-muted\">\n <slot />\n </div>\n\n <div class=\"mt-5 flex justify-end gap-2\">\n <button\n ref=\"cancelButton\"\n type=\"button\"\n class=\"btn-secondary\"\n :disabled=\"busy\"\n @click=\"emit('cancel')\"\n >\n {{ cancelLabel }}\n </button>\n <button\n type=\"button\"\n :class=\"destructive ? 'btn-danger' : 'btn-primary'\"\n :disabled=\"busy\"\n @click=\"emit('confirm')\"\n >\n {{ busy ? \"Working…\" : confirmLabel }}\n </button>\n </div>\n </div>\n </div>\n </Teleport>\n</template>\n"],"names":["props","__props","emit","__emit","panel","ref","cancelButton","previouslyFocused","watch","open","nextTick","_a","onKeydown","event","focusable","first","last","_createBlock","_Teleport","_createElementBlock","_cache","_withModifiers","$event","_createElementVNode","_hoisted_1","_toDisplayString","_hoisted_2","_renderSlot","_ctx","_hoisted_3","_hoisted_4","_hoisted_5"],"mappings":"0iBAKA,MAAMA,EAAQC,EAYRC,EAAOC,EAEPC,EAAQC,EAAwB,IAAI,EACpCC,EAAeD,EAA8B,IAAI,EACvD,IAAIE,EAAwC,KAE5CC,EACE,IAAMR,EAAM,KACZ,MAAOS,GAAS,OACd,GAAI,CAACA,EAAM,CACTF,GAAA,MAAAA,EAAmB,QACnB,MACF,CACAA,EAAoB,SAAS,cAC7B,MAAMG,EAAA,GAENC,EAAAL,EAAa,QAAb,MAAAK,EAAoB,OACtB,CAAA,EAGF,SAASC,EAAUC,EAA4B,CAE7C,GADIA,EAAM,MAAQ,UAAY,CAACb,EAAM,QAAW,QAAQ,EACpDa,EAAM,MAAQ,OAAS,CAACT,EAAM,MAAO,OAEzC,MAAMU,EAAYV,EAAM,MAAM,iBAA8B,wBAAwB,EAC9EW,EAAQD,EAAU,CAAC,EACnBE,EAAOF,EAAUA,EAAU,OAAS,CAAC,EACvC,CAACC,GAAS,CAACC,IAEXH,EAAM,UAAY,SAAS,gBAAkBE,GAC/CF,EAAM,eAAA,EACNG,EAAK,MAAA,GACI,CAACH,EAAM,UAAY,SAAS,gBAAkBG,IACvDH,EAAM,eAAA,EACNE,EAAM,MAAA,GAEV,mBAIEE,EA0CWC,EAAA,CA1CD,GAAG,QAAM,CAETjB,EAAA,UADRkB,EAwCM,MAAA,OAtCJ,MAAM,2DACL,QAAKC,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAAC,EAAAC,GAAA,CAAQrB,EAAA,MAAQC,EAAI,QAAA,EAAA,CAAA,MAAA,CAAA,GACzB,UAAAU,CAAA,GAEDW,EAiCM,MAAA,SAhCA,QAAJ,IAAInB,EACJ,KAAK,cACL,aAAW,OACX,kBAAgB,gBAChB,mBAAiB,eACjB,MAAM,wEAAA,GAENmB,EAAuE,KAAvEC,EAAuEC,EAAbxB,EAAA,KAAK,EAAA,CAAA,EAE/DsB,EAEM,MAFNG,EAEM,CADJC,EAAQC,EAAA,OAAA,SAAA,CAAA,GAGVL,EAkBM,MAlBNM,EAkBM,CAjBJN,EAQS,SAAA,SAPH,eAAJ,IAAIjB,EACJ,KAAK,SACL,MAAM,gBACL,SAAUL,EAAA,KACV,uBAAOC,EAAI,QAAA,EAAA,IAETD,EAAA,WAAW,EAAA,EAAA6B,CAAA,EAEhBP,EAOS,SAAA,CANP,KAAK,SACJ,QAAOtB,EAAA,YAAW,aAAA,aAAA,EAClB,SAAUA,EAAA,KACV,uBAAOC,EAAI,SAAA,EAAA,EAETuB,EAAAxB,EAAA,gBAAoBA,EAAA,YAAY,EAAA,GAAA8B,CAAA,CAAA"}
@@ -0,0 +1,2 @@
1
+ import{d as V,o as $,i as L,A as N,c as i,j as c,a as e,F as g,k as S,w as p,l as h,b,t as o,m as j,n as B,r as f,g as y,p as D,h as l,e as _,u as E}from"./index-BsOOwFCf.js";import{_ as G}from"./PageHeader.vue_vue_type_script_setup_true_lang-DJWhe1Ye.js";const R={class:"mx-auto max-w-5xl space-y-6 px-4 py-6 sm:px-6"},T={key:0,class:"text-sm text-muted"},A={"aria-labelledby":"content-heading"},F={class:"grid grid-cols-2 gap-3 lg:grid-cols-4"},M={class:"text-sm text-muted"},H={class:"mt-1 text-[30px] font-light leading-9 tabular-nums"},O={class:"mt-1 text-xs text-muted"},U={class:"mt-1 text-[30px] font-light leading-9 tabular-nums"},q={class:"mt-1 text-xs text-muted"},z={key:0,role:"alert",class:"rounded-md border border-warning/15 bg-warning-soft p-4","aria-labelledby":"conflicts-heading"},I={class:"flex flex-wrap items-center justify-between gap-3"},J={key:1,class:"card p-4","aria-labelledby":"pending-heading"},K={class:"flex flex-wrap items-center justify-between gap-3"},P={id:"pending-heading",class:"text-sm font-semibold"},Q={class:"card p-4","aria-labelledby":"repository-heading"},W={class:"mt-2 break-all font-mono text-xs text-muted"},ee=V({__name:"DashboardView",setup(X){const w=E(),m=j(),k=B(),n=f(null),v=f(!0),C=y(()=>{var r,d;const s=(d=(r=w.identity.value)==null?void 0:r.name)==null?void 0:d.split(" ")[0];if(!s)return"Overview";const t=new Date().getHours();return`${t<12?"Good morning":t<18?"Good afternoon":"Good evening"}, ${s}`}),x=y(()=>{var s;return(((s=n.value)==null?void 0:s.collections)??[]).reduce((t,a)=>t+a.total,0)});return $(async()=>{try{n.value=await L.overview()}catch(s){k.error(s instanceof N?s.message:"Could not load the overview.")}finally{v.value=!1}}),(s,t)=>{var r,d;const a=D("RouterLink");return l(),i(g,null,[c(G,{title:C.value,subtitle:`${x.value} entr${x.value===1?"y":"ies"} in the content repository`},null,8,["title","subtitle"]),e("div",R,[v.value?(l(),i("p",T,"Loading…")):n.value?(l(),i(g,{key:1},[e("section",A,[t[1]||(t[1]=e("h2",{id:"content-heading",class:"sr-only"},"Content",-1)),e("ul",F,[(l(!0),i(g,null,S(n.value.collections,u=>(l(),i("li",{key:u.name},[c(a,{to:{name:"collection",params:{collection:u.name}},class:"card block p-4 transition hover:border-line-strong hover:bg-raised"},{default:p(()=>[e("p",M,o(u.label),1),e("p",H,o(u.total),1),e("p",O,o(u.total===1?"entry":"entries"),1)]),_:2},1032,["to"])]))),128)),e("li",null,[c(a,{to:{name:"media"},class:"card block p-4 transition hover:border-line-strong hover:bg-raised"},{default:p(()=>[t[0]||(t[0]=e("p",{class:"text-sm text-muted"},"Media",-1)),e("p",U,o(n.value.media.files),1),e("p",q,o(n.value.media.readable),1)]),_:1})])])]),(((d=(r=h(m).status.value)==null?void 0:r.sync)==null?void 0:d.conflicts.length)??0)>0?(l(),i("section",z,[e("div",I,[t[3]||(t[3]=e("div",null,[e("h2",{id:"conflicts-heading",class:"text-sm font-semibold text-warning"}," Changed on the website while you edited "),e("p",{class:"mt-1 text-sm text-muted"}," Choose a version for each. Until then, nothing can be published. ")],-1)),c(a,{to:{name:"changes"},class:"btn-secondary"},{default:p(()=>[...t[2]||(t[2]=[_("Choose versions",-1)])]),_:1})])])):b("",!0),h(m).editCount.value>0?(l(),i("section",J,[e("div",K,[e("div",null,[e("h2",P,o(h(m).editCount.value)+" edit"+o(h(m).editCount.value===1?"":"s")+" waiting to be published ",1),t[4]||(t[4]=e("p",{class:"mt-1 text-sm text-muted"}," Saved, and not yet in the repository. The whole set is published together. ",-1))]),c(a,{to:{name:"changes"},class:"btn-secondary"},{default:p(()=>[...t[5]||(t[5]=[_("Review and publish",-1)])]),_:1})])])):b("",!0),e("section",Q,[t[6]||(t[6]=e("h2",{id:"repository-heading",class:"text-sm font-semibold"},"Content repository",-1)),e("p",W,o(n.value.repository),1),t[7]||(t[7]=e("p",{class:"mt-3 text-sm text-muted"}," Every entry and every uploaded file becomes a file in this repository when it is published, so the website can be rebuilt from the repository alone — with or without this server. ",-1))])],64)):b("",!0)])],64)}}});export{ee as default};
2
+ //# sourceMappingURL=DashboardView-BpelkVGe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DashboardView-BpelkVGe.js","sources":["../../src/views/DashboardView.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Dashboard: counts, where the content lives, and the pending changes.\n * No \"Rebuild website\" button: the published commit triggers the repository's own pipeline.\n * No \"recently edited\" list: only the repository's history can say that truthfully.\n */\n\nimport { computed, onMounted, ref } from \"vue\";\n\nimport { api, ApiError, type Overview } from \"@/api\";\nimport PageHeader from \"@/components/PageHeader.vue\";\nimport { useSession } from \"@/stores/session\";\nimport { useStaging } from \"@/stores/staging\";\nimport { useToast } from \"@/stores/toast\";\n\nconst session = useSession();\nconst staging = useStaging();\nconst toast = useToast();\n\nconst overview = ref<Overview | null>(null);\nconst loading = ref(true);\n\n/** Greets by name only when the provider gave one; local mode has no user. */\nconst greeting = computed(() => {\n const name = session.identity.value?.name?.split(\" \")[0];\n if (!name) return \"Overview\";\n\n const hour = new Date().getHours();\n const part = hour < 12 ? \"Good morning\" : hour < 18 ? \"Good afternoon\" : \"Good evening\";\n return `${part}, ${name}`;\n});\n\nconst entries = computed(() =>\n (overview.value?.collections ?? []).reduce((total, item) => total + item.total, 0),\n);\n\nonMounted(async () => {\n try {\n overview.value = await api.overview();\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : \"Could not load the overview.\");\n } finally {\n loading.value = false;\n }\n});\n</script>\n\n<template>\n <PageHeader\n :title=\"greeting\"\n :subtitle=\"`${entries} entr${entries === 1 ? 'y' : 'ies'} in the content repository`\"\n />\n\n <div class=\"mx-auto max-w-5xl space-y-6 px-4 py-6 sm:px-6\">\n <p v-if=\"loading\" class=\"text-sm text-muted\">Loading…</p>\n\n <template v-else-if=\"overview\">\n <section aria-labelledby=\"content-heading\">\n <h2 id=\"content-heading\" class=\"sr-only\">Content</h2>\n\n <ul class=\"grid grid-cols-2 gap-3 lg:grid-cols-4\">\n <li v-for=\"collection in overview.collections\" :key=\"collection.name\">\n <RouterLink\n :to=\"{ name: 'collection', params: { collection: collection.name } }\"\n class=\"card block p-4 transition hover:border-line-strong hover:bg-raised\"\n >\n <p class=\"text-sm text-muted\">{{ collection.label }}</p>\n <p class=\"mt-1 text-[30px] font-light leading-9 tabular-nums\">{{ collection.total }}</p>\n <p class=\"mt-1 text-xs text-muted\">\n {{ collection.total === 1 ? \"entry\" : \"entries\" }}\n </p>\n </RouterLink>\n </li>\n\n <li>\n <RouterLink :to=\"{ name: 'media' }\" class=\"card block p-4 transition hover:border-line-strong hover:bg-raised\">\n <p class=\"text-sm text-muted\">Media</p>\n <p class=\"mt-1 text-[30px] font-light leading-9 tabular-nums\">{{ overview.media.files }}</p>\n <p class=\"mt-1 text-xs text-muted\">{{ overview.media.readable }}</p>\n </RouterLink>\n </li>\n </ul>\n </section>\n\n <section\n v-if=\"(staging.status.value?.sync?.conflicts.length ?? 0) > 0\"\n role=\"alert\"\n class=\"rounded-md border border-warning/15 bg-warning-soft p-4\"\n aria-labelledby=\"conflicts-heading\"\n >\n <div class=\"flex flex-wrap items-center justify-between gap-3\">\n <div>\n <h2 id=\"conflicts-heading\" class=\"text-sm font-semibold text-warning\">\n Changed on the website while you edited\n </h2>\n <p class=\"mt-1 text-sm text-muted\">\n Choose a version for each. Until then, nothing can be published.\n </p>\n </div>\n <RouterLink :to=\"{ name: 'changes' }\" class=\"btn-secondary\">Choose versions</RouterLink>\n </div>\n </section>\n\n <section\n v-if=\"staging.editCount.value > 0\"\n class=\"card p-4\"\n aria-labelledby=\"pending-heading\"\n >\n <div class=\"flex flex-wrap items-center justify-between gap-3\">\n <div>\n <h2 id=\"pending-heading\" class=\"text-sm font-semibold\">\n {{ staging.editCount.value }} edit{{ staging.editCount.value === 1 ? \"\" : \"s\" }}\n waiting to be published\n </h2>\n <p class=\"mt-1 text-sm text-muted\">\n Saved, and not yet in the repository. The whole set is published together.\n </p>\n </div>\n <RouterLink :to=\"{ name: 'changes' }\" class=\"btn-secondary\">Review and publish</RouterLink>\n </div>\n </section>\n\n <section class=\"card p-4\" aria-labelledby=\"repository-heading\">\n <h2 id=\"repository-heading\" class=\"text-sm font-semibold\">Content repository</h2>\n\n <p class=\"mt-2 break-all font-mono text-xs text-muted\">{{ overview.repository }}</p>\n\n <p class=\"mt-3 text-sm text-muted\">\n Every entry and every uploaded file becomes a file in this repository when it is\n published, so the website can be rebuilt from the repository alone — with or without\n this server.\n </p>\n </section>\n </template>\n </div>\n</template>\n"],"names":["session","useSession","staging","useStaging","toast","useToast","overview","ref","loading","greeting","computed","name","_b","_a","hour","entries","total","item","onMounted","api","error","ApiError","_createVNode","PageHeader","_createElementVNode","_hoisted_1","_createElementBlock","_hoisted_2","_Fragment","_hoisted_3","_hoisted_4","_openBlock","_renderList","collection","_component_RouterLink","_hoisted_5","_toDisplayString","_hoisted_6","_hoisted_7","_cache","_hoisted_8","_hoisted_9","_unref","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{d as K,o as O,A as f,c as l,j as C,w as E,a as n,F as _,b,t as o,k as M,z as Q,B as W,n as X,g as w,r as c,i as x,p as Z,y as ee,h as a,e as S,l as F,C as te,m as se}from"./index-BsOOwFCf.js";import{_ as ne}from"./ConfirmDialog.vue_vue_type_script_setup_true_lang-C073kW3p.js";import{u as le,d as ae,_ as oe}from"./useUnsavedChanges-FB5XcFfI.js";import{_ as re}from"./PageHeader.vue_vue_type_script_setup_true_lang-DJWhe1Ye.js";import"./useField-BxptRsz_.js";import"./arrow-right-DD5BWXHV.js";import"./plus-CgaTSYYb.js";const ie=["disabled"],ue={class:"mx-auto max-w-3xl px-4 py-6 sm:px-6"},ce={key:0,class:"text-sm text-muted"},de={key:0,role:"alert",class:"mb-5 rounded-md border border-warning/15 bg-warning-soft p-4"},me={key:1,role:"alert",class:"mb-5 rounded-md border border-warning/15 bg-warning-soft p-4"},ve={key:2,role:"alert",class:"mb-5 rounded-md border border-danger/15 bg-danger-soft p-4"},ge={class:"text-sm font-medium text-danger"},fe={class:"mt-1.5 space-y-0.5 text-xs text-danger"},ye={key:3,class:"mt-10 border-t border-line pt-5"},pe={key:0,class:"mt-3 rounded-md border border-warning/15 bg-warning-soft p-4"},he={class:"text-sm font-medium text-warning"},be={class:"mt-1 list-inside list-disc text-xs"},we={key:1,class:"p-8 text-center text-sm text-muted"},$e=K({__name:"EntryView",props:{collection:{},slug:{}},setup(y){const s=y,V=W(),R=ee(),D=se(),u=X(),v=w(()=>R.collection(s.collection)),U=c(null),m=c({}),d=c([]),$=c(!0),p=c(!1),L=c(!1),k=c(!1),Y=w(()=>{var t,e;return(((e=(t=D.status.value)==null?void 0:t.sync)==null?void 0:e.conflicts)??[]).some(r=>{var i;return((i=r.target)==null?void 0:i.kind)==="entry"&&r.target.collection===s.collection&&r.target.slug===s.slug})}),g=c([]),N=c(null),h=le(m),T=h.dirty,A=w(()=>!s.slug),j=w(()=>{const t=v.value;if(!t)return"Entry";const e=m.value[t.titleField];return typeof e=="string"&&e.trim()?e.trim():A.value?`New ${t.labelSingular.toLowerCase()}`:"Untitled"}),q=w(()=>{var t;return((t=U.value)==null?void 0:t.path)??"Not saved yet"});function I(t){U.value=t,m.value=structuredClone(t.data),N.value=t.revision,d.value=[],L.value=!1,h.markClean()}async function B(t){I(await x.entries.get(s.collection,t))}O(async()=>{const t=v.value;if(!t){$.value=!1;return}try{s.slug?await B(s.slug):(m.value=ae(t.fields),N.value=null,h.markClean())}catch(e){u.error(e instanceof f?e.message:"Could not open this entry."),await V.replace({name:"collection",params:{collection:s.collection}})}finally{$.value=!1}});async function z(){p.value=!0,d.value=[];const t={data:m.value,revision:N.value};try{const e=s.slug?await x.entries.update(s.collection,s.slug,t):await x.entries.create(s.collection,t);if(h.markClean(),u.success(e.renamed?`Saved as “${e.slug}”.`:"Saved."),D.refresh(),e.slug!==s.slug){await V.replace({name:"entry",params:{collection:s.collection,slug:e.slug}});return}await B(e.slug)}catch(e){e instanceof f&&e.status===409?(L.value=!0,u.error("This entry was changed while you were editing it — by a colleague or on the website.")):e instanceof f&&e.issues.length>0?(d.value=e.issues,u.error(e.message,`${e.issues.length} field(s) need attention.`),requestAnimationFrame(()=>{var r;(r=document.querySelector('[aria-invalid="true"], .field-error'))==null||r.scrollIntoView({behavior:"smooth",block:"center"})})):u.error(e instanceof f?e.message:"Could not save this entry.")}finally{p.value=!1}}async function P(){if(s.slug)try{await B(s.slug),u.success("Reloaded from the repository.")}catch(t){u.error(t instanceof f?t.message:"Could not reload this entry.")}}async function G(){if(g.value=[],s.slug)try{const{references:t}=await x.entries.references(s.collection,s.slug);g.value=t.map(e=>{var r;return{collection:((r=R.collection(e.collection))==null?void 0:r.label)??e.collection,title:e.title}})}catch{}k.value=!0}async function H(){if(s.slug)try{await x.entries.remove(s.collection,s.slug),h.release(),u.success("Deleted."),D.refresh(),await V.replace({name:"collection",params:{collection:s.collection}})}catch(t){u.error(t instanceof f?t.message:"Could not delete this entry.")}finally{k.value=!1}}return(t,e)=>{const r=Z("RouterLink");return v.value?(a(),l(_,{key:0},[C(re,{title:j.value,subtitle:q.value,back:{name:"collection",params:{collection:y.collection}},"back-label":v.value.label},{default:E(()=>[n("span",{class:te(["badge",F(T)?"border-accent bg-accent-soft text-accent":"text-success"])},[e[2]||(e[2]=n("span",{class:"h-1.5 w-1.5 rounded-full bg-current","aria-hidden":"true"},null,-1)),S(" "+o(F(T)?"Unsaved":"Saved"),1)],2),n("button",{type:"button",class:"btn-primary",disabled:p.value||!F(T),onClick:z},o(p.value?"Saving…":"Save changes"),9,ie)]),_:1},8,["title","subtitle","back","back-label"]),n("div",ue,[$.value?(a(),l("p",ce,"Loading…")):(a(),l(_,{key:1},[Y.value?(a(),l("div",de,[e[4]||(e[4]=n("p",{class:"text-sm font-medium text-warning"},"This entry was changed on the website while you edited",-1)),e[5]||(e[5]=n("p",{class:"mt-1 text-xs text-muted"}," Your saved version is kept. Choose which version to publish on the Publish page. ",-1)),C(r,{to:{name:"changes"},class:"btn-secondary mt-2.5 inline-flex"},{default:E(()=>[...e[3]||(e[3]=[S("Choose a version",-1)])]),_:1})])):b("",!0),L.value?(a(),l("div",me,[e[6]||(e[6]=n("p",{class:"text-sm font-medium text-warning"},"This entry changed while you were editing",-1)),e[7]||(e[7]=n("p",{class:"mt-1 text-xs text-muted"}," Your changes are still here and have not been saved. Copy anything you need, then load the current version — or open the repository to see what changed. ",-1)),n("button",{type:"button",class:"btn-secondary mt-2.5",onClick:P}," Discard mine and reload ")])):b("",!0),d.value.length?(a(),l("div",ve,[n("p",ge,o(d.value.length)+" field"+o(d.value.length===1?"":"s")+" need attention ",1),n("ul",fe,[(a(!0),l(_,null,M(d.value,i=>(a(),l("li",{key:i.path},o(i.message),1))),128))])])):b("",!0),n("form",{novalidate:"",onSubmit:Q(z,["prevent"])},[C(oe,{modelValue:m.value,"onUpdate:modelValue":e[0]||(e[0]=i=>m.value=i),fields:v.value.fields,issues:d.value,disabled:p.value,collection:y.collection,"entry-slug":y.slug},null,8,["modelValue","fields","issues","disabled","collection","entry-slug"]),e[8]||(e[8]=n("button",{type:"submit",class:"sr-only"},"Save",-1))],32),A.value?b("",!0):(a(),l("div",ye,[n("button",{type:"button",class:"btn-danger",onClick:G}," Delete this "+o(v.value.labelSingular.toLowerCase()),1)]))],64))]),C(ne,{open:k.value,title:`Delete “${j.value}”?`,"confirm-label":"Delete",destructive:"",onCancel:e[1]||(e[1]=i=>k.value=!1),onConfirm:H},{default:E(()=>[n("p",null,[e[9]||(e[9]=S(" This removes ",-1)),n("code",null,o(q.value),1),e[10]||(e[10]=S(". Like any edit, the removal joins the pending changes and reaches the repository when they are published. ",-1))]),g.value.length?(a(),l("div",pe,[n("p",he,o(g.value.length)+" other entr"+o(g.value.length===1?"y":"ies")+" link to this one: ",1),n("ul",be,[(a(!0),l(_,null,M(g.value,(i,J)=>(a(),l("li",{key:J},o(i.title)+" ("+o(i.collection)+") ",1))),128))])])):b("",!0)]),_:1},8,["open","title"])],64)):(a(),l("div",we,' There is no collection called "'+o(y.collection)+'". ',1))}}});export{$e as default};
2
+ //# sourceMappingURL=EntryView-DZuhqJnd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EntryView-DZuhqJnd.js","sources":["../../src/views/EntryView.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Entry editor. Saved is not Published: a save joins the pending changes, and\n * unsaved work stays in the form. `revision` goes back with the save, so a\n * concurrent save gets a 409 instead of an overwrite. The slug is the address:\n * changing it moves the entry, and the route follows.\n */\n\nimport { computed, onMounted, ref } from \"vue\";\nimport { useRouter } from \"vue-router\";\n\nimport { defaultValues, type FieldIssue } from \"@micacms/core\";\n\nimport { api, ApiError, type EntryDetail } from \"@/api\";\nimport ConfirmDialog from \"@/components/ConfirmDialog.vue\";\nimport FieldList from \"@/components/fields/FieldList.vue\";\nimport PageHeader from \"@/components/PageHeader.vue\";\nimport { useUnsavedChanges } from \"@/composables/useUnsavedChanges\";\nimport { useSchema } from \"@/stores/schema\";\nimport { useStaging } from \"@/stores/staging\";\nimport { useToast } from \"@/stores/toast\";\n\nconst props = defineProps<{ collection: string; slug?: string }>();\n\nconst router = useRouter();\nconst schema = useSchema();\nconst staging = useStaging();\nconst toast = useToast();\n\nconst definition = computed(() => schema.collection(props.collection));\n\nconst entry = ref<EntryDetail | null>(null);\nconst values = ref<Record<string, unknown>>({});\nconst issues = ref<FieldIssue[]>([]);\nconst loading = ref(true);\nconst saving = ref(false);\nconst conflict = ref(false);\nconst confirmDelete = ref(false);\n\n/** This entry was changed on the website while it has a draft here: Mica's own clone only. */\nconst websiteConflict = computed(() =>\n (staging.status.value?.sync?.conflicts ?? []).some(\n (item) =>\n item.target?.kind === \"entry\" && item.target.collection === props.collection && item.target.slug === props.slug,\n ),\n);\nconst references = ref<Array<{ collection: string; title: string }>>([]);\n\n/**\n * The version this form was opened against; `null` on a new entry, so a\n * duplicate slug is a conflict.\n */\nconst revision = ref<string | null>(null);\n\nconst unsaved = useUnsavedChanges(values);\nconst dirty = unsaved.dirty;\n\nconst isNew = computed(() => !props.slug);\n\nconst title = computed(() => {\n const def = definition.value;\n if (!def) return \"Entry\";\n const value = values.value[def.titleField];\n if (typeof value === \"string\" && value.trim()) return value.trim();\n return isNew.value ? `New ${def.labelSingular.toLowerCase()}` : \"Untitled\";\n});\n\n/**\n * Where this entry lives, from the server: a directory with rich text or media,\n * else one `.json` file.\n */\nconst location = computed(() => entry.value?.path ?? \"Not saved yet\");\n\nfunction setBaseline(detail: EntryDetail): void {\n entry.value = detail;\n values.value = structuredClone(detail.data);\n revision.value = detail.revision;\n issues.value = [];\n conflict.value = false;\n unsaved.markClean();\n}\n\nasync function open(slug: string): Promise<void> {\n setBaseline(await api.entries.get(props.collection, slug));\n}\n\nonMounted(async () => {\n const def = definition.value;\n if (!def) {\n loading.value = false;\n return;\n }\n\n try {\n if (props.slug) {\n await open(props.slug);\n } else {\n // A fresh entry starts as a full skeleton so every input is bound. The\n // skeleton itself is not an edit, so it becomes the baseline.\n values.value = defaultValues(def.fields);\n revision.value = null;\n unsaved.markClean();\n }\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : \"Could not open this entry.\");\n await router.replace({ name: \"collection\", params: { collection: props.collection } });\n } finally {\n loading.value = false;\n }\n});\n\nasync function save(): Promise<void> {\n saving.value = true;\n issues.value = [];\n\n const payload = { data: values.value, revision: revision.value };\n\n try {\n const saved = props.slug\n ? await api.entries.update(props.collection, props.slug, payload)\n : await api.entries.create(props.collection, payload);\n\n // The server derives and de-duplicates the slug and can move the entry:\n // re-open at what it decided.\n unsaved.markClean();\n toast.success(saved.renamed ? `Saved as “${saved.slug}”.` : \"Saved.\");\n void staging.refresh();\n\n if (saved.slug !== props.slug) {\n await router.replace({\n name: \"entry\",\n params: { collection: props.collection, slug: saved.slug },\n });\n return;\n }\n\n await open(saved.slug);\n } catch (error) {\n if (error instanceof ApiError && error.status === 409) {\n // The form keeps the editor's work; a reload is their decision.\n conflict.value = true;\n toast.error(\"This entry was changed while you were editing it — by a colleague or on the website.\");\n } else if (error instanceof ApiError && error.issues.length > 0) {\n issues.value = error.issues;\n toast.error(error.message, `${error.issues.length} field(s) need attention.`);\n requestAnimationFrame(() => {\n document\n .querySelector('[aria-invalid=\"true\"], .field-error')\n ?.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n });\n } else {\n toast.error(error instanceof ApiError ? error.message : \"Could not save this entry.\");\n }\n } finally {\n saving.value = false;\n }\n}\n\n/** Takes the version now in the repository, discarding this form's edits. */\nasync function reload(): Promise<void> {\n if (!props.slug) return;\n try {\n await open(props.slug);\n toast.success(\"Reloaded from the repository.\");\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : \"Could not reload this entry.\");\n }\n}\n\nasync function askDelete(): Promise<void> {\n references.value = [];\n if (props.slug) {\n try {\n const { references: found } = await api.entries.references(props.collection, props.slug);\n references.value = found.map((item) => ({\n collection: schema.collection(item.collection)?.label ?? item.collection,\n title: item.title,\n }));\n } catch {\n // The warning is a courtesy; a failure to fetch it must not block deletion.\n }\n }\n confirmDelete.value = true;\n}\n\nasync function remove(): Promise<void> {\n if (!props.slug) return;\n try {\n await api.entries.remove(props.collection, props.slug);\n unsaved.release();\n toast.success(\"Deleted.\");\n void staging.refresh();\n await router.replace({ name: \"collection\", params: { collection: props.collection } });\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : \"Could not delete this entry.\");\n } finally {\n confirmDelete.value = false;\n }\n}\n</script>\n\n<template>\n <template v-if=\"definition\">\n <PageHeader\n :title=\"title\"\n :subtitle=\"location\"\n :back=\"{ name: 'collection', params: { collection } }\"\n :back-label=\"definition.label\"\n >\n <span :class=\"['badge', dirty ? 'border-accent bg-accent-soft text-accent' : 'text-success']\">\n <span class=\"h-1.5 w-1.5 rounded-full bg-current\" aria-hidden=\"true\" />\n {{ dirty ? \"Unsaved\" : \"Saved\" }}\n </span>\n <button type=\"button\" class=\"btn-primary\" :disabled=\"saving || !dirty\" @click=\"save\">\n {{ saving ? \"Saving…\" : \"Save changes\" }}\n </button>\n </PageHeader>\n\n <div class=\"mx-auto max-w-3xl px-4 py-6 sm:px-6\">\n <p v-if=\"loading\" class=\"text-sm text-muted\">Loading…</p>\n\n <template v-else>\n <div\n v-if=\"websiteConflict\"\n role=\"alert\"\n class=\"mb-5 rounded-md border border-warning/15 bg-warning-soft p-4\"\n >\n <p class=\"text-sm font-medium text-warning\">This entry was changed on the website while you edited</p>\n <p class=\"mt-1 text-xs text-muted\">\n Your saved version is kept. Choose which version to publish on the Publish page.\n </p>\n <RouterLink :to=\"{ name: 'changes' }\" class=\"btn-secondary mt-2.5 inline-flex\">Choose a version</RouterLink>\n </div>\n\n <div\n v-if=\"conflict\"\n role=\"alert\"\n class=\"mb-5 rounded-md border border-warning/15 bg-warning-soft p-4\"\n >\n <p class=\"text-sm font-medium text-warning\">This entry changed while you were editing</p>\n <p class=\"mt-1 text-xs text-muted\">\n Your changes are still here and have not been saved. Copy anything you need, then load\n the current version — or open the repository to see what changed.\n </p>\n <button type=\"button\" class=\"btn-secondary mt-2.5\" @click=\"reload\">\n Discard mine and reload\n </button>\n </div>\n\n <div\n v-if=\"issues.length\"\n role=\"alert\"\n class=\"mb-5 rounded-md border border-danger/15 bg-danger-soft p-4\"\n >\n <p class=\"text-sm font-medium text-danger\">\n {{ issues.length }} field{{ issues.length === 1 ? \"\" : \"s\" }} need attention\n </p>\n <ul class=\"mt-1.5 space-y-0.5 text-xs text-danger\">\n <li v-for=\"issue in issues\" :key=\"issue.path\">{{ issue.message }}</li>\n </ul>\n </div>\n\n <form novalidate @submit.prevent=\"save\">\n <FieldList\n v-model=\"values\"\n :fields=\"definition.fields\"\n :issues=\"issues\"\n :disabled=\"saving\"\n :collection=\"collection\"\n :entry-slug=\"slug\"\n />\n\n <!-- Enter in a text input should save, not do nothing. -->\n <button type=\"submit\" class=\"sr-only\">Save</button>\n </form>\n\n <div v-if=\"!isNew\" class=\"mt-10 border-t border-line pt-5\">\n <button type=\"button\" class=\"btn-danger\" @click=\"askDelete\">\n Delete this {{ definition.labelSingular.toLowerCase() }}\n </button>\n </div>\n </template>\n </div>\n\n <ConfirmDialog\n :open=\"confirmDelete\"\n :title=\"`Delete “${title}”?`\"\n confirm-label=\"Delete\"\n destructive\n @cancel=\"confirmDelete = false\"\n @confirm=\"remove\"\n >\n <p>\n This removes <code>{{ location }}</code>. Like any edit, the removal joins the pending\n changes and reaches the repository when they are published.\n </p>\n\n <div v-if=\"references.length\" class=\"mt-3 rounded-md border border-warning/15 bg-warning-soft p-4\">\n <p class=\"text-sm font-medium text-warning\">\n {{ references.length }} other entr{{ references.length === 1 ? \"y\" : \"ies\" }} link to this\n one:\n </p>\n <ul class=\"mt-1 list-inside list-disc text-xs\">\n <li v-for=\"(item, index) in references\" :key=\"index\">\n {{ item.title }} ({{ item.collection }})\n </li>\n </ul>\n </div>\n </ConfirmDialog>\n </template>\n\n <div v-else class=\"p-8 text-center text-sm text-muted\">\n There is no collection called \"{{ collection }}\".\n </div>\n</template>\n"],"names":["props","__props","router","useRouter","schema","useSchema","staging","useStaging","toast","useToast","definition","computed","entry","ref","values","issues","loading","saving","conflict","confirmDelete","websiteConflict","_b","_a","item","references","revision","unsaved","useUnsavedChanges","dirty","isNew","title","def","value","location","setBaseline","detail","open","slug","api","onMounted","defaultValues","error","ApiError","save","payload","saved","reload","askDelete","found","remove","_createElementBlock","_Fragment","_createVNode","PageHeader","_createElementVNode","_unref","_createTextVNode","_hoisted_1","_hoisted_2","_hoisted_3","_openBlock","_hoisted_4","_cache","_component_RouterLink","_hoisted_5","_hoisted_6","_hoisted_7","_toDisplayString","_hoisted_8","_renderList","issue","FieldList","$event","_hoisted_9","ConfirmDialog","_hoisted_10","_hoisted_11","_hoisted_12","index","_hoisted_13"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{d as _,o as h,c as a,a as t,_ as b,t as u,b as d,e as v,u as w,f as k,r as f,g as c,h as i}from"./index-Colfqb-9.js";const S={key:0,class:"grid min-h-full place-items-center p-6"},q={key:1,class:"grid min-h-full place-items-center p-6"},T={class:"w-full max-w-sm"},C={class:"card space-y-4 p-5"},M={key:0,role:"alert",class:"rounded-md border border-danger/15 bg-danger-soft px-4 py-3 text-sm text-danger"},R=["disabled"],V={key:1,class:"text-center text-xs text-muted"},B={class:"font-medium"},N={key:0,class:"mt-4 text-center text-xs text-muted"},E=_({__name:"LoginView",setup(A){const r=w(),m=k(),l=f(!1),o=f(null),p=c(()=>{var e;return(e=r.config.value)==null?void 0:e.provider}),n=c(()=>{var e;return((e=r.config.value)==null?void 0:e.authRequired)===!1}),y=c(()=>{const e=m.query.redirect;return typeof e=="string"&&e.startsWith("/admin")?e:"/admin/"}),x={rate_limited:"Too many sign-in attempts. Wait a few minutes, then sign in again.",expired:"That sign-in attempt expired or was not started here. Please try again.",failed:"Sign-in could not be completed. Please try again.",not_admitted:"You are signed in at your provider, but that account has no access to this site. Ask whoever runs it to add you.",too_large:"Sign-in could not be completed: your account details are too large."};h(()=>{const e=m.query.error;typeof e=="string"&&(o.value=x[e]??"Sign-in could not be completed."),n.value&&!o.value&&g()});function g(){l.value=!0,r.signIn(y.value)}return(e,s)=>n.value&&!o.value?(i(),a("div",S,[...s[0]||(s[0]=[t("p",{class:"text-sm text-muted"},"Opening the editor…",-1)])])):(i(),a("div",q,[t("div",T,[s[3]||(s[3]=t("div",{class:"mb-7 flex flex-col items-center gap-4 text-center"},[t("img",{src:b,alt:"micacms",class:"h-8 w-auto"}),t("div",null,[t("h1",{class:"text-lg font-semibold tracking-tight"},"Sign in to Mica"),t("p",{class:"mt-0.5 text-sm text-muted"},"Manage your website content.")])],-1)),t("div",C,[o.value?(i(),a("p",M,u(o.value),1)):d("",!0),t("button",{type:"button",class:"btn-primary w-full",disabled:l.value,onClick:g},u(l.value?"Redirecting…":n.value?"Continue":"Sign in"),9,R),p.value&&!n.value?(i(),a("p",V,[s[1]||(s[1]=v(" You will be taken to ",-1)),t("span",B,u(p.value),1),s[2]||(s[2]=v(" to sign in. ",-1))])):d("",!0)]),n.value?d("",!0):(i(),a("p",N," Mica does not hold your password. Trouble signing in is a question for whoever runs your identity provider; no access once you are in is a question for a site administrator. "))])]))}});export{E as default};
2
- //# sourceMappingURL=LoginView-BCrrdqnf.js.map
1
+ import{d as _,o as h,c as a,a as t,_ as b,t as u,b as d,e as v,u as w,f as k,r as f,g as c,h as i}from"./index-BsOOwFCf.js";const S={key:0,class:"grid min-h-full place-items-center p-6"},q={key:1,class:"grid min-h-full place-items-center p-6"},T={class:"w-full max-w-sm"},C={class:"card space-y-4 p-5"},M={key:0,role:"alert",class:"rounded-md border border-danger/15 bg-danger-soft px-4 py-3 text-sm text-danger"},R=["disabled"],V={key:1,class:"text-center text-xs text-muted"},B={class:"font-medium"},N={key:0,class:"mt-4 text-center text-xs text-muted"},E=_({__name:"LoginView",setup(A){const r=w(),m=k(),l=f(!1),o=f(null),p=c(()=>{var e;return(e=r.config.value)==null?void 0:e.provider}),n=c(()=>{var e;return((e=r.config.value)==null?void 0:e.authRequired)===!1}),y=c(()=>{const e=m.query.redirect;return typeof e=="string"&&e.startsWith("/admin")?e:"/admin/"}),x={rate_limited:"Too many sign-in attempts. Wait a few minutes, then sign in again.",expired:"That sign-in attempt expired or was not started here. Please try again.",failed:"Sign-in could not be completed. Please try again.",not_admitted:"You are signed in at your provider, but that account has no access to this site. Ask whoever runs it to add you.",too_large:"Sign-in could not be completed: your account details are too large."};h(()=>{const e=m.query.error;typeof e=="string"&&(o.value=x[e]??"Sign-in could not be completed."),n.value&&!o.value&&g()});function g(){l.value=!0,r.signIn(y.value)}return(e,s)=>n.value&&!o.value?(i(),a("div",S,[...s[0]||(s[0]=[t("p",{class:"text-sm text-muted"},"Opening the editor…",-1)])])):(i(),a("div",q,[t("div",T,[s[3]||(s[3]=t("div",{class:"mb-7 flex flex-col items-center gap-4 text-center"},[t("img",{src:b,alt:"micacms",class:"h-8 w-auto"}),t("div",null,[t("h1",{class:"text-lg font-semibold tracking-tight"},"Sign in to Mica"),t("p",{class:"mt-0.5 text-sm text-muted"},"Manage your website content.")])],-1)),t("div",C,[o.value?(i(),a("p",M,u(o.value),1)):d("",!0),t("button",{type:"button",class:"btn-primary w-full",disabled:l.value,onClick:g},u(l.value?"Redirecting…":n.value?"Continue":"Sign in"),9,R),p.value&&!n.value?(i(),a("p",V,[s[1]||(s[1]=v(" You will be taken to ",-1)),t("span",B,u(p.value),1),s[2]||(s[2]=v(" to sign in. ",-1))])):d("",!0)]),n.value?d("",!0):(i(),a("p",N," Mica does not hold your password. Trouble signing in is a question for whoever runs your identity provider; no access once you are in is a question for a site administrator. "))])]))}});export{E as default};
2
+ //# sourceMappingURL=LoginView-BmnX2zOD.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"LoginView-BCrrdqnf.js","sources":["../../src/views/LoginView.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Sign in.\n *\n * There is no form. Mica does not hold passwords, so there is nothing for an\n * editor to type here — the button hands the browser to the identity provider,\n * which does the actual authenticating and sends it back.\n *\n * The error banner exists because the round trip can fail after the provider\n * is satisfied: someone can authenticate perfectly well and still not be\n * admitted to this site, and that has to read as \"ask for access\", not as\n * \"your password was wrong\".\n *\n * In local mode this screen is not reached at all: there is no provider, no\n * account and nobody to be, and the session starts as the page is served. The\n * redirect below is the fallback for the one way in that skips that — a\n * browser still holding `/admin/login` from an earlier session.\n */\n\nimport { computed, onMounted, ref } from \"vue\";\nimport { useRoute } from \"vue-router\";\n\nimport { useSession } from \"@/stores/session\";\n\nconst session = useSession();\nconst route = useRoute();\n\nconst busy = ref(false);\n\n/** Set by the callback when it bounces the browser back here with a reason. */\nconst error = ref<string | null>(null);\n\nconst provider = computed(() => session.config.value?.provider);\n/** Local mode: no provider to send anybody to, so there is nothing to show. */\nconst authDisabled = computed(() => session.config.value?.authRequired === false);\n\n/** Where to land afterwards. Always a path on this server. */\nconst returnTo = computed(() => {\n const redirect = route.query[\"redirect\"];\n return typeof redirect === \"string\" && redirect.startsWith(\"/admin\")\n ? redirect\n : \"/admin/\";\n});\n\n/** The callback sends a code, never a sentence, so the URL carries no address (#150). */\nconst REFUSALS: Record<string, string> = {\n rate_limited: \"Too many sign-in attempts. Wait a few minutes, then sign in again.\",\n expired: \"That sign-in attempt expired or was not started here. Please try again.\",\n failed: \"Sign-in could not be completed. Please try again.\",\n not_admitted: \"You are signed in at your provider, but that account has no access to this site. Ask whoever runs it to add you.\",\n too_large: \"Sign-in could not be completed: your account details are too large.\",\n};\n\nonMounted(() => {\n const reason = route.query[\"error\"];\n if (typeof reason === \"string\") error.value = REFUSALS[reason] ?? \"Sign-in could not be completed.\";\n\n // No provider, no question to ask. Straight to the editor.\n if (authDisabled.value && !error.value) signIn();\n});\n\nfunction signIn(): void {\n busy.value = true;\n session.signIn(returnTo.value);\n}\n</script>\n\n<template>\n <!-- Local mode: a held breath, not a screen. -->\n <div v-if=\"authDisabled && !error\" class=\"grid min-h-full place-items-center p-6\">\n <p class=\"text-sm text-muted\">Opening the editor…</p>\n </div>\n\n <div v-else class=\"grid min-h-full place-items-center p-6\">\n <div class=\"w-full max-w-sm\">\n <div class=\"mb-7 flex flex-col items-center gap-4 text-center\">\n <img src=\"/brand/micacms-wordmark.svg\" alt=\"micacms\" class=\"h-8 w-auto\" />\n <div>\n <h1 class=\"text-lg font-semibold tracking-tight\">Sign in to Mica</h1>\n <p class=\"mt-0.5 text-sm text-muted\">Manage your website content.</p>\n </div>\n </div>\n\n <div class=\"card space-y-4 p-5\">\n <p v-if=\"error\" role=\"alert\" class=\"rounded-md border border-danger/15 bg-danger-soft px-4 py-3 text-sm text-danger\">\n {{ error }}\n </p>\n\n <button type=\"button\" class=\"btn-primary w-full\" :disabled=\"busy\" @click=\"signIn\">\n {{ busy ? \"Redirecting…\" : authDisabled ? \"Continue\" : \"Sign in\" }}\n </button>\n\n <p v-if=\"provider && !authDisabled\" class=\"text-center text-xs text-muted\">\n You will be taken to <span class=\"font-medium\">{{ provider }}</span> to sign in.\n </p>\n </div>\n\n <p v-if=\"!authDisabled\" class=\"mt-4 text-center text-xs text-muted\">\n Mica does not hold your password. Trouble signing in is a question for\n whoever runs your identity provider; no access once you are in is a\n question for a site administrator.\n </p>\n </div>\n </div>\n</template>\n"],"names":["session","useSession","route","useRoute","busy","ref","error","provider","computed","_a","authDisabled","returnTo","redirect","REFUSALS","onMounted","reason","signIn","_openBlock","_createElementBlock","_hoisted_1","_cache","_createElementVNode","_hoisted_2","_hoisted_3","_imports_0","_hoisted_4","_hoisted_5","_toDisplayString","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9"],"mappings":"6kBAwBA,MAAMA,EAAUC,EAAA,EACVC,EAAQC,EAAA,EAERC,EAAOC,EAAI,EAAK,EAGhBC,EAAQD,EAAmB,IAAI,EAE/BE,EAAWC,EAAS,IAAA,OAAM,OAAAC,EAAAT,EAAQ,OAAO,QAAf,YAAAS,EAAsB,SAAQ,EAExDC,EAAeF,EAAS,IAAA,OAAM,QAAAC,EAAAT,EAAQ,OAAO,QAAf,YAAAS,EAAsB,gBAAiB,GAAK,EAG1EE,EAAWH,EAAS,IAAM,CAC9B,MAAMI,EAAWV,EAAM,MAAM,SAC7B,OAAO,OAAOU,GAAa,UAAYA,EAAS,WAAW,QAAQ,EAC/DA,EACA,SACN,CAAC,EAGKC,EAAmC,CACvC,aAAc,qEACd,QAAS,0EACT,OAAQ,oDACR,aAAc,mHACd,UAAW,qEAAA,EAGbC,EAAU,IAAM,CACd,MAAMC,EAASb,EAAM,MAAM,MACvB,OAAOa,GAAW,aAAgB,MAAQF,EAASE,CAAM,GAAK,mCAG9DL,EAAa,OAAS,CAACJ,EAAM,OAAOU,EAAA,CAC1C,CAAC,EAED,SAASA,GAAe,CACtBZ,EAAK,MAAQ,GACbJ,EAAQ,OAAOW,EAAS,KAAK,CAC/B,cAKaD,EAAA,QAAiBJ,EAAA,OAA5BW,IAAAC,EAEM,MAFNC,EAEM,CAAA,GAAAC,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAA,CADJC,EAAqD,IAAA,CAAlD,MAAM,oBAAA,EAAqB,sBAAmB,EAAA,CAAA,OAGnDJ,IAAAC,EA8BM,MA9BNI,EA8BM,CA7BJD,EA4BM,MA5BNE,EA4BM,aA3BJF,EAMM,MAAA,CAND,MAAM,qDAAmD,CAC5DA,EAA0E,MAAA,CAArE,IAAAG,EAAkC,IAAI,UAAU,MAAM,YAAA,GAC3DH,EAGM,MAAA,KAAA,CAFJA,EAAqE,KAAA,CAAjE,MAAM,sCAAA,EAAuC,iBAAe,EAChEA,EAAqE,IAAA,CAAlE,MAAM,2BAAA,EAA4B,8BAA4B,CAAA,SAIrEA,EAYM,MAZNI,EAYM,CAXKnB,EAAA,WAATY,EAEI,IAFJQ,EAEIC,EADCrB,EAAA,KAAK,EAAA,CAAA,YAGVe,EAES,SAAA,CAFD,KAAK,SAAS,MAAM,qBAAsB,SAAUjB,EAAA,MAAO,QAAOY,CAAA,EACrEW,EAAAvB,EAAA,qBAAwBM,EAAA,MAAY,WAAA,SAAA,EAAA,EAAAkB,CAAA,EAGhCrB,EAAA,QAAaG,EAAA,OAAtBO,IAAAC,EAEI,IAFJW,EAEI,eAFuE,yBACpD,EAAA,GAAAR,EAA+C,OAA/CS,EAA+CH,EAAlBpB,EAAA,KAAQ,EAAA,CAAA,gBAAU,gBACtE,EAAA,EAAA,eAGQG,EAAA,oBAAVQ,EAII,IAJJa,EAAoE,iLAIpE"}
1
+ {"version":3,"file":"LoginView-BmnX2zOD.js","sources":["../../src/views/LoginView.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Sign in.\n *\n * There is no form. Mica does not hold passwords, so there is nothing for an\n * editor to type here — the button hands the browser to the identity provider,\n * which does the actual authenticating and sends it back.\n *\n * The error banner exists because the round trip can fail after the provider\n * is satisfied: someone can authenticate perfectly well and still not be\n * admitted to this site, and that has to read as \"ask for access\", not as\n * \"your password was wrong\".\n *\n * In local mode this screen is not reached at all: there is no provider, no\n * account and nobody to be, and the session starts as the page is served. The\n * redirect below is the fallback for the one way in that skips that — a\n * browser still holding `/admin/login` from an earlier session.\n */\n\nimport { computed, onMounted, ref } from \"vue\";\nimport { useRoute } from \"vue-router\";\n\nimport { useSession } from \"@/stores/session\";\n\nconst session = useSession();\nconst route = useRoute();\n\nconst busy = ref(false);\n\n/** Set by the callback when it bounces the browser back here with a reason. */\nconst error = ref<string | null>(null);\n\nconst provider = computed(() => session.config.value?.provider);\n/** Local mode: no provider to send anybody to, so there is nothing to show. */\nconst authDisabled = computed(() => session.config.value?.authRequired === false);\n\n/** Where to land afterwards. Always a path on this server. */\nconst returnTo = computed(() => {\n const redirect = route.query[\"redirect\"];\n return typeof redirect === \"string\" && redirect.startsWith(\"/admin\")\n ? redirect\n : \"/admin/\";\n});\n\n/** The callback sends a code, never a sentence, so the URL carries no address (#150). */\nconst REFUSALS: Record<string, string> = {\n rate_limited: \"Too many sign-in attempts. Wait a few minutes, then sign in again.\",\n expired: \"That sign-in attempt expired or was not started here. Please try again.\",\n failed: \"Sign-in could not be completed. Please try again.\",\n not_admitted: \"You are signed in at your provider, but that account has no access to this site. Ask whoever runs it to add you.\",\n too_large: \"Sign-in could not be completed: your account details are too large.\",\n};\n\nonMounted(() => {\n const reason = route.query[\"error\"];\n if (typeof reason === \"string\") error.value = REFUSALS[reason] ?? \"Sign-in could not be completed.\";\n\n // No provider, no question to ask. Straight to the editor.\n if (authDisabled.value && !error.value) signIn();\n});\n\nfunction signIn(): void {\n busy.value = true;\n session.signIn(returnTo.value);\n}\n</script>\n\n<template>\n <!-- Local mode: a held breath, not a screen. -->\n <div v-if=\"authDisabled && !error\" class=\"grid min-h-full place-items-center p-6\">\n <p class=\"text-sm text-muted\">Opening the editor…</p>\n </div>\n\n <div v-else class=\"grid min-h-full place-items-center p-6\">\n <div class=\"w-full max-w-sm\">\n <div class=\"mb-7 flex flex-col items-center gap-4 text-center\">\n <img src=\"/brand/micacms-wordmark.svg\" alt=\"micacms\" class=\"h-8 w-auto\" />\n <div>\n <h1 class=\"text-lg font-semibold tracking-tight\">Sign in to Mica</h1>\n <p class=\"mt-0.5 text-sm text-muted\">Manage your website content.</p>\n </div>\n </div>\n\n <div class=\"card space-y-4 p-5\">\n <p v-if=\"error\" role=\"alert\" class=\"rounded-md border border-danger/15 bg-danger-soft px-4 py-3 text-sm text-danger\">\n {{ error }}\n </p>\n\n <button type=\"button\" class=\"btn-primary w-full\" :disabled=\"busy\" @click=\"signIn\">\n {{ busy ? \"Redirecting…\" : authDisabled ? \"Continue\" : \"Sign in\" }}\n </button>\n\n <p v-if=\"provider && !authDisabled\" class=\"text-center text-xs text-muted\">\n You will be taken to <span class=\"font-medium\">{{ provider }}</span> to sign in.\n </p>\n </div>\n\n <p v-if=\"!authDisabled\" class=\"mt-4 text-center text-xs text-muted\">\n Mica does not hold your password. Trouble signing in is a question for\n whoever runs your identity provider; no access once you are in is a\n question for a site administrator.\n </p>\n </div>\n </div>\n</template>\n"],"names":["session","useSession","route","useRoute","busy","ref","error","provider","computed","_a","authDisabled","returnTo","redirect","REFUSALS","onMounted","reason","signIn","_openBlock","_createElementBlock","_hoisted_1","_cache","_createElementVNode","_hoisted_2","_hoisted_3","_imports_0","_hoisted_4","_hoisted_5","_toDisplayString","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9"],"mappings":"6kBAwBA,MAAMA,EAAUC,EAAA,EACVC,EAAQC,EAAA,EAERC,EAAOC,EAAI,EAAK,EAGhBC,EAAQD,EAAmB,IAAI,EAE/BE,EAAWC,EAAS,IAAA,OAAM,OAAAC,EAAAT,EAAQ,OAAO,QAAf,YAAAS,EAAsB,SAAQ,EAExDC,EAAeF,EAAS,IAAA,OAAM,QAAAC,EAAAT,EAAQ,OAAO,QAAf,YAAAS,EAAsB,gBAAiB,GAAK,EAG1EE,EAAWH,EAAS,IAAM,CAC9B,MAAMI,EAAWV,EAAM,MAAM,SAC7B,OAAO,OAAOU,GAAa,UAAYA,EAAS,WAAW,QAAQ,EAC/DA,EACA,SACN,CAAC,EAGKC,EAAmC,CACvC,aAAc,qEACd,QAAS,0EACT,OAAQ,oDACR,aAAc,mHACd,UAAW,qEAAA,EAGbC,EAAU,IAAM,CACd,MAAMC,EAASb,EAAM,MAAM,MACvB,OAAOa,GAAW,aAAgB,MAAQF,EAASE,CAAM,GAAK,mCAG9DL,EAAa,OAAS,CAACJ,EAAM,OAAOU,EAAA,CAC1C,CAAC,EAED,SAASA,GAAe,CACtBZ,EAAK,MAAQ,GACbJ,EAAQ,OAAOW,EAAS,KAAK,CAC/B,cAKaD,EAAA,QAAiBJ,EAAA,OAA5BW,IAAAC,EAEM,MAFNC,EAEM,CAAA,GAAAC,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAA,CADJC,EAAqD,IAAA,CAAlD,MAAM,oBAAA,EAAqB,sBAAmB,EAAA,CAAA,OAGnDJ,IAAAC,EA8BM,MA9BNI,EA8BM,CA7BJD,EA4BM,MA5BNE,EA4BM,aA3BJF,EAMM,MAAA,CAND,MAAM,qDAAmD,CAC5DA,EAA0E,MAAA,CAArE,IAAAG,EAAkC,IAAI,UAAU,MAAM,YAAA,GAC3DH,EAGM,MAAA,KAAA,CAFJA,EAAqE,KAAA,CAAjE,MAAM,sCAAA,EAAuC,iBAAe,EAChEA,EAAqE,IAAA,CAAlE,MAAM,2BAAA,EAA4B,8BAA4B,CAAA,SAIrEA,EAYM,MAZNI,EAYM,CAXKnB,EAAA,WAATY,EAEI,IAFJQ,EAEIC,EADCrB,EAAA,KAAK,EAAA,CAAA,YAGVe,EAES,SAAA,CAFD,KAAK,SAAS,MAAM,qBAAsB,SAAUjB,EAAA,MAAO,QAAOY,CAAA,EACrEW,EAAAvB,EAAA,qBAAwBM,EAAA,MAAY,WAAA,SAAA,EAAA,EAAAkB,CAAA,EAGhCrB,EAAA,QAAaG,EAAA,OAAtBO,IAAAC,EAEI,IAFJW,EAEI,eAFuE,yBACpD,EAAA,GAAAR,EAA+C,OAA/CS,EAA+CH,EAAlBpB,EAAA,KAAQ,EAAA,CAAA,gBAAU,gBACtE,EAAA,EAAA,eAGQG,EAAA,oBAAVQ,EAII,IAJJa,EAAoE,iLAIpE"}
@@ -1,2 +1,2 @@
1
- import{d as I,o as P,c as o,j as E,w as O,l as p,a as t,s as W,v as q,t as r,F as _,k as N,i as v,b as S,z as b,C as V,r as c,g as w,A as B,n as G,h as n,e as H,m as J}from"./index-Colfqb-9.js";import{_ as K}from"./PageHeader.vue_vue_type_script_setup_true_lang-B-fBJAjn.js";import{f as k}from"./format-Cpnr2As6.js";const Q={class:"btn-primary cursor-pointer"},R=["disabled"],X={class:"mb-3 flex flex-wrap items-center gap-2"},Y={key:0,class:"py-12 text-center text-sm text-muted"},Z={key:1,class:"card px-4 py-12 text-center"},ee={class:"text-sm font-medium"},te={key:2,class:"grid gap-5 lg:grid-cols-[1fr_20rem]"},se={class:"space-y-6"},le={class:"mb-2 font-mono text-xs text-muted"},ae={class:"grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4"},oe=["aria-pressed","onClick"],ne={class:"block aspect-4/3 bg-canvas"},ie=["src"],re={key:1,class:"flex h-full w-full items-center justify-center text-xs font-semibold uppercase text-muted"},de={class:"block px-2 py-1.5"},ue={class:"block truncate text-xs font-medium"},ce={class:"block text-[11px] text-muted"},pe={key:0,class:"card h-fit p-4 lg:sticky lg:top-5"},fe={class:"mt-3 overflow-hidden rounded-lg border border-line bg-canvas"},me=["src"],ve={key:1,class:"p-6 text-center text-xs text-muted"},he={class:"mt-4 space-y-1 text-xs text-muted"},ge={class:"flex justify-between gap-3"},ye={key:0,class:"flex justify-between gap-3"},xe={class:"flex justify-between gap-3"},_e={class:"mt-3 border-t border-line pt-3"},be={class:"mt-1 break-all font-mono text-[11px]"},we={class:"mt-3 flex gap-2"},ke=["href"],De=I({__name:"MediaView",setup(Ce){const f=G(),u=c([]),y=c(!0),h=c(!1),m=c(""),g=c(!1),a=c(null),x=c(null),C=l=>l.mimeType.startsWith("image/"),L=l=>{const e=l.split("/");return e.pop(),e.join("/")||"/"},T=w(()=>{const l=m.value.trim().toLowerCase();return l?u.value.filter(e=>e.path.toLowerCase().includes(l)):u.value}),U=w(()=>{const l=new Map;for(const e of T.value){const s=L(e.path),d=l.get(s);d?d.push(e):l.set(s,[e])}return[...l].sort(([e],[s])=>e.localeCompare(s))}),A=w(()=>u.value.reduce((l,e)=>l+e.size,0));async function $(){y.value=!0;try{u.value=(await v.media.list()).items;const l=u.value.find(e=>{var s;return e.path===((s=a.value)==null?void 0:s.path)});a.value=l??null}catch(l){f.error(l instanceof B?l.message:"Could not load the media library.")}finally{y.value=!1}}P($);async function j(l){const e=Array.from(l??[]);if(e.length!==0){h.value=!0;try{const{items:s}=await v.media.upload(e);f.success(`Uploaded ${s.length} file${s.length===1?"":"s"}.`),J().refresh(),await $(),a.value=u.value.find(d=>{var i;return d.path===((i=s[0])==null?void 0:i.path)})??null}catch(s){f.error(s instanceof B?s.message:"The upload failed.")}finally{h.value=!1,x.value&&(x.value.value="")}}}async function F(l){try{await navigator.clipboard.writeText(l),f.success("Path copied.")}catch{f.error("Could not copy. Select the path and copy it by hand.")}}return(l,e)=>(n(),o(_,null,[E(K,{title:"Media",subtitle:`${u.value.length} file${u.value.length===1?"":"s"} · ${p(k)(A.value)}`},{default:O(()=>[t("label",Q,[t("input",{ref_key:"fileInput",ref:x,type:"file",multiple:"",class:"sr-only",disabled:h.value,onChange:e[0]||(e[0]=s=>j(s.target.files))},null,40,R),H(" "+r(h.value?"Uploading…":"Upload files"),1)])]),_:1},8,["subtitle"]),t("div",{class:V(["mx-auto max-w-5xl px-4 py-5 sm:px-6",g.value&&"rounded-md outline-2 outline-dashed outline-ink"]),onDragover:e[3]||(e[3]=b(s=>g.value=!0,["prevent"])),onDragleave:e[4]||(e[4]=b(s=>g.value=!1,["prevent"])),onDrop:e[5]||(e[5]=b(s=>{var d;g.value=!1,j(((d=s.dataTransfer)==null?void 0:d.files)??null)},["prevent"]))},[t("div",X,[W(t("input",{"onUpdate:modelValue":e[1]||(e[1]=s=>m.value=s),type:"search",placeholder:"Search by name or folder…",class:"input max-w-64","aria-label":"Search media"},null,512),[[q,m.value]])]),y.value?(n(),o("p",Y,"Loading…")):T.value.length===0?(n(),o("div",Z,[t("p",ee,r(m.value?`Nothing matches "${m.value}".`:"No files yet."),1),e[6]||(e[6]=t("p",{class:"mt-1 text-sm text-muted"}," Drag files anywhere on this page to upload them. ",-1))])):(n(),o("div",te,[t("div",se,[(n(!0),o(_,null,N(U.value,([s,d])=>(n(),o("section",{key:s},[t("h2",le,r(s),1),t("ul",ae,[(n(!0),o(_,null,N(d,i=>{var z,D;return n(),o("li",{key:i.path},[t("button",{type:"button","aria-pressed":((z=a.value)==null?void 0:z.path)===i.path,class:V(["block w-full overflow-hidden rounded-lg border text-left transition",((D=a.value)==null?void 0:D.path)===i.path?"border-accent ring-2 ring-accent/25":"border-line hover:border-accent/40"]),onClick:Te=>{var M;return a.value=((M=a.value)==null?void 0:M.path)===i.path?null:i}},[t("span",ne,[C(i)?(n(),o("img",{key:0,src:p(v).media.url(i.path),alt:"",loading:"lazy",class:"h-full w-full object-cover"},null,8,ie)):(n(),o("span",re,r(i.mimeType.split("/")[1]),1))]),t("span",de,[t("span",ue,r(i.filename),1),t("span",ce,r(p(k)(i.size)),1)])],10,oe)])}),128))])]))),128))]),a.value?(n(),o("aside",pe,[e[11]||(e[11]=t("h2",{class:"text-sm font-semibold"},"File details",-1)),t("div",fe,[C(a.value)?(n(),o("img",{key:0,src:p(v).media.url(a.value.path),alt:"",class:"max-h-48 w-full object-contain"},null,8,me)):(n(),o("p",ve,r(a.value.mimeType),1))]),t("dl",he,[t("div",ge,[e[7]||(e[7]=t("dt",null,"Type",-1)),t("dd",null,r(a.value.mimeType),1)]),a.value.width?(n(),o("div",ye,[e[8]||(e[8]=t("dt",null,"Dimensions",-1)),t("dd",null,r(a.value.width)+" × "+r(a.value.height),1)])):S("",!0),t("div",xe,[e[9]||(e[9]=t("dt",null,"Size",-1)),t("dd",null,r(p(k)(a.value.size)),1)])]),t("div",_e,[e[10]||(e[10]=t("p",{class:"field-label"},"Path in the repository",-1)),t("p",be,r(a.value.path),1)]),t("div",we,[t("button",{type:"button",class:"btn-secondary flex-1",onClick:e[2]||(e[2]=s=>F(a.value.path))}," Copy path "),t("a",{href:p(v).media.url(a.value.path),target:"_blank",rel:"noopener noreferrer",class:"btn-secondary flex-1"}," Open ",8,ke)])])):S("",!0)]))],34)],64))}});export{De as default};
2
- //# sourceMappingURL=MediaView-KlQHfWAi.js.map
1
+ import{d as I,o as P,c as o,j as E,w as O,l as p,a as t,s as W,v as q,t as r,F as _,k as N,i as v,b as S,z as b,C as V,r as c,g as w,A as B,n as G,h as n,e as H,m as J}from"./index-BsOOwFCf.js";import{_ as K}from"./PageHeader.vue_vue_type_script_setup_true_lang-DJWhe1Ye.js";import{f as k}from"./format-Cpnr2As6.js";const Q={class:"btn-primary cursor-pointer"},R=["disabled"],X={class:"mb-3 flex flex-wrap items-center gap-2"},Y={key:0,class:"py-12 text-center text-sm text-muted"},Z={key:1,class:"card px-4 py-12 text-center"},ee={class:"text-sm font-medium"},te={key:2,class:"grid gap-5 lg:grid-cols-[1fr_20rem]"},se={class:"space-y-6"},le={class:"mb-2 font-mono text-xs text-muted"},ae={class:"grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4"},oe=["aria-pressed","onClick"],ne={class:"block aspect-4/3 bg-canvas"},ie=["src"],re={key:1,class:"flex h-full w-full items-center justify-center text-xs font-semibold uppercase text-muted"},de={class:"block px-2 py-1.5"},ue={class:"block truncate text-xs font-medium"},ce={class:"block text-[11px] text-muted"},pe={key:0,class:"card h-fit p-4 lg:sticky lg:top-5"},fe={class:"mt-3 overflow-hidden rounded-lg border border-line bg-canvas"},me=["src"],ve={key:1,class:"p-6 text-center text-xs text-muted"},he={class:"mt-4 space-y-1 text-xs text-muted"},ge={class:"flex justify-between gap-3"},ye={key:0,class:"flex justify-between gap-3"},xe={class:"flex justify-between gap-3"},_e={class:"mt-3 border-t border-line pt-3"},be={class:"mt-1 break-all font-mono text-[11px]"},we={class:"mt-3 flex gap-2"},ke=["href"],De=I({__name:"MediaView",setup(Ce){const f=G(),u=c([]),y=c(!0),h=c(!1),m=c(""),g=c(!1),a=c(null),x=c(null),C=l=>l.mimeType.startsWith("image/"),L=l=>{const e=l.split("/");return e.pop(),e.join("/")||"/"},T=w(()=>{const l=m.value.trim().toLowerCase();return l?u.value.filter(e=>e.path.toLowerCase().includes(l)):u.value}),U=w(()=>{const l=new Map;for(const e of T.value){const s=L(e.path),d=l.get(s);d?d.push(e):l.set(s,[e])}return[...l].sort(([e],[s])=>e.localeCompare(s))}),A=w(()=>u.value.reduce((l,e)=>l+e.size,0));async function $(){y.value=!0;try{u.value=(await v.media.list()).items;const l=u.value.find(e=>{var s;return e.path===((s=a.value)==null?void 0:s.path)});a.value=l??null}catch(l){f.error(l instanceof B?l.message:"Could not load the media library.")}finally{y.value=!1}}P($);async function j(l){const e=Array.from(l??[]);if(e.length!==0){h.value=!0;try{const{items:s}=await v.media.upload(e);f.success(`Uploaded ${s.length} file${s.length===1?"":"s"}.`),J().refresh(),await $(),a.value=u.value.find(d=>{var i;return d.path===((i=s[0])==null?void 0:i.path)})??null}catch(s){f.error(s instanceof B?s.message:"The upload failed.")}finally{h.value=!1,x.value&&(x.value.value="")}}}async function F(l){try{await navigator.clipboard.writeText(l),f.success("Path copied.")}catch{f.error("Could not copy. Select the path and copy it by hand.")}}return(l,e)=>(n(),o(_,null,[E(K,{title:"Media",subtitle:`${u.value.length} file${u.value.length===1?"":"s"} · ${p(k)(A.value)}`},{default:O(()=>[t("label",Q,[t("input",{ref_key:"fileInput",ref:x,type:"file",multiple:"",class:"sr-only",disabled:h.value,onChange:e[0]||(e[0]=s=>j(s.target.files))},null,40,R),H(" "+r(h.value?"Uploading…":"Upload files"),1)])]),_:1},8,["subtitle"]),t("div",{class:V(["mx-auto max-w-5xl px-4 py-5 sm:px-6",g.value&&"rounded-md outline-2 outline-dashed outline-ink"]),onDragover:e[3]||(e[3]=b(s=>g.value=!0,["prevent"])),onDragleave:e[4]||(e[4]=b(s=>g.value=!1,["prevent"])),onDrop:e[5]||(e[5]=b(s=>{var d;g.value=!1,j(((d=s.dataTransfer)==null?void 0:d.files)??null)},["prevent"]))},[t("div",X,[W(t("input",{"onUpdate:modelValue":e[1]||(e[1]=s=>m.value=s),type:"search",placeholder:"Search by name or folder…",class:"input max-w-64","aria-label":"Search media"},null,512),[[q,m.value]])]),y.value?(n(),o("p",Y,"Loading…")):T.value.length===0?(n(),o("div",Z,[t("p",ee,r(m.value?`Nothing matches "${m.value}".`:"No files yet."),1),e[6]||(e[6]=t("p",{class:"mt-1 text-sm text-muted"}," Drag files anywhere on this page to upload them. ",-1))])):(n(),o("div",te,[t("div",se,[(n(!0),o(_,null,N(U.value,([s,d])=>(n(),o("section",{key:s},[t("h2",le,r(s),1),t("ul",ae,[(n(!0),o(_,null,N(d,i=>{var z,D;return n(),o("li",{key:i.path},[t("button",{type:"button","aria-pressed":((z=a.value)==null?void 0:z.path)===i.path,class:V(["block w-full overflow-hidden rounded-lg border text-left transition",((D=a.value)==null?void 0:D.path)===i.path?"border-accent ring-2 ring-accent/25":"border-line hover:border-accent/40"]),onClick:Te=>{var M;return a.value=((M=a.value)==null?void 0:M.path)===i.path?null:i}},[t("span",ne,[C(i)?(n(),o("img",{key:0,src:p(v).media.url(i.path),alt:"",loading:"lazy",class:"h-full w-full object-cover"},null,8,ie)):(n(),o("span",re,r(i.mimeType.split("/")[1]),1))]),t("span",de,[t("span",ue,r(i.filename),1),t("span",ce,r(p(k)(i.size)),1)])],10,oe)])}),128))])]))),128))]),a.value?(n(),o("aside",pe,[e[11]||(e[11]=t("h2",{class:"text-sm font-semibold"},"File details",-1)),t("div",fe,[C(a.value)?(n(),o("img",{key:0,src:p(v).media.url(a.value.path),alt:"",class:"max-h-48 w-full object-contain"},null,8,me)):(n(),o("p",ve,r(a.value.mimeType),1))]),t("dl",he,[t("div",ge,[e[7]||(e[7]=t("dt",null,"Type",-1)),t("dd",null,r(a.value.mimeType),1)]),a.value.width?(n(),o("div",ye,[e[8]||(e[8]=t("dt",null,"Dimensions",-1)),t("dd",null,r(a.value.width)+" × "+r(a.value.height),1)])):S("",!0),t("div",xe,[e[9]||(e[9]=t("dt",null,"Size",-1)),t("dd",null,r(p(k)(a.value.size)),1)])]),t("div",_e,[e[10]||(e[10]=t("p",{class:"field-label"},"Path in the repository",-1)),t("p",be,r(a.value.path),1)]),t("div",we,[t("button",{type:"button",class:"btn-secondary flex-1",onClick:e[2]||(e[2]=s=>F(a.value.path))}," Copy path "),t("a",{href:p(v).media.url(a.value.path),target:"_blank",rel:"noopener noreferrer",class:"btn-secondary flex-1"}," Open ",8,ke)])])):S("",!0)]))],34)],64))}});export{De as default};
2
+ //# sourceMappingURL=MediaView-Dp2botAQ.js.map