@obelism/content 0.1.1 → 0.3.0
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.
- package/README.md +77 -5
- package/dist/bin/content.js +400 -12
- package/dist/bridge.d.mts +19 -3
- package/dist/bridge.d.mts.map +1 -1
- package/dist/bridge.d.ts +19 -3
- package/dist/bridge.d.ts.map +1 -1
- package/dist/bridge.global.js +158 -0
- package/dist/bridge.js +156 -21
- package/dist/bridge.mjs +156 -22
- package/dist/index.d.mts +7 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -0
- package/dist/index.mjs +15 -1
- package/dist/site.js +4 -0
- package/dist/site.mjs +4 -0
- package/dist/{types-pa96z6po.d.ts → types-nyvor49r.d.ts} +7 -2
- package/dist/types-nyvor49r.d.ts.map +1 -0
- package/package.json +5 -2
- package/dist/types-pa96z6po.d.ts.map +0 -1
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
!/*! Obelism Content bridge (@obelism/content), MIT. https://content.obelism.studio/docs/visual-editor */function(){"use strict";var e;let t="data-cms-uid",r="data-cms-entry",n="cms_environment",o="#d58a1f",a=(e="")=>`${e}[${t}]{cursor:pointer;transition:outline-color .15s}${e}[${t}]:hover{outline:2px dashed ${o};outline-offset:2px}${e}[${t}][data-cms-selected]{outline:2px solid ${o};outline-offset:2px}`,i={features:[],selectedUid:null,prompting:!1,canUndo:!1,unsaved:!1,saving:!1,publishing:!1,selecting:!1},s=()=>{let e={update:new Set,saved:new Set,published:new Set,change:new Set};return{listeners:e,on:(t,r)=>(e[t].add(r),()=>{e[t].delete(r)}),emit(t,r){for(let n of e[t])try{n(r)}catch(e){console.error("[@obelism/content bridge]",e)}}}},l=()=>Promise.reject(Error("Save and publish in the visual editor itself")),d={prompt:()=>Promise.reject(Error("Prompts only work inside the visual editor")),createPage:()=>Promise.reject(Error("New pages are made on the site itself")),undo:()=>{},save:l,publish:l,signOut:()=>{},select:()=>{},setSelecting:()=>{}},c=e=>`[${t}="${e.replace(/["\\]/g,"\\$&")}"]`,p=(e,t)=>{let r=s();e.on("saved",()=>r.emit("saved",{})),e.on("published",()=>r.emit("published",{}));let o=!1;return{isEditing:e.isEditing,mode:e.isEditing?"editor":null,entryId:e.entryId,environment:t.get(n),on:(t,n)=>("update"!==t||o||(o=!0,e.on("update",e=>{let t=e?.entry;t&&r.emit("update",{entry:t,selectedUid:null})})),r.on(t,n)),getState:()=>i,...d,destroy:()=>{}}},g={open:"Edit with a prompt",placeholder:"Describe a change, e.g. “make the heading shorter”",send:"Apply",undo:"Undo",close:"Close",scopeEntry:"Whole page",scopeBlock:"Selected block",clearBlock:"Edit the whole page instead",working:"Working on it…",save:"Save",publish:"Publish",saving:"Saving…",publishing:"Publishing…",saved:"Saved as a draft",published:"Published",unsaved:"Unsaved changes",signOut:"Exit editing",viewingDrafts:"Viewing drafts",selectHint:"Click a block to change only that block",newPage:"New page",newPageCancel:"Edit this page instead",newPagePlaceholder:"Describe the new page, e.g. “a pricing page for agencies”",path:"Path",pathPlaceholder:"/pricing",creating:"Writing the page…",created:"Opening the new page…"},u={page:'<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/>',newPage:'<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M9 15h6"/><path d="M12 18v-6"/>',block:'<rect width="18" height="18" x="3" y="3" rx="2"/><path d="M7 8h10"/><path d="M7 12h6"/>',undo:'<path d="M9 14 4 9l5-5"/><path d="M4 9h10.5a5.5 5.5 0 0 1 0 11H11"/>',close:'<path d="M18 6 6 18"/><path d="m6 6 12 12"/>',send:'<path d="M3.714 3.048a.498.498 0 0 0-.683.627l2.843 7.627a2 2 0 0 1 0 1.396l-2.842 7.627a.498.498 0 0 0 .682.627l18-8.5a.5.5 0 0 0 0-.904z"/><path d="M6 12h16"/>'},h=`
|
|
2
|
+
:host { all: initial; }
|
|
3
|
+
* { box-sizing: border-box; font: inherit; color: inherit; margin: 0; }
|
|
4
|
+
.root {
|
|
5
|
+
--color-white-rgb: 248 248 248; --color-beige-rgb: 238 236 233; --color-beige-off-rgb: 228 225 217;
|
|
6
|
+
--color-grey-light-rgb: 132 132 132; --color-grey-rgb: 108 108 108; --color-grey-mid-rgb: 100 100 100; --color-grey-dark-rgb: 40 40 40;
|
|
7
|
+
--color-black-off-rgb: 20 20 20; --color-black-rgb: 12 12 12;
|
|
8
|
+
--color-orange-rgb: 212 130 30; --color-red-rgb: 240 93 93;
|
|
9
|
+
--theme-foreground-rgb: var(--color-black-rgb);
|
|
10
|
+
--theme-background-alt-rgb: var(--color-white-rgb);
|
|
11
|
+
--theme-background-actions-rgb: var(--color-beige-off-rgb);
|
|
12
|
+
--theme-border-rgb: var(--color-grey-light-rgb);
|
|
13
|
+
--theme-accent-rgb: var(--color-orange-rgb);
|
|
14
|
+
--theme-foreground-muted-rgb: var(--color-grey-mid-rgb);
|
|
15
|
+
--theme-error: #b3261e;
|
|
16
|
+
--border-radius-medium: 20px; --border-radius-pill: 5em;
|
|
17
|
+
--ease-roll: cubic-bezier(0.65, 0, 0.35, 1);
|
|
18
|
+
--shadow: 0 18px 40px -16px rgb(var(--color-black-rgb) / .3), 0 2px 6px rgb(var(--color-black-rgb) / .05);
|
|
19
|
+
--font-inter: 'InterVariable', 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
|
|
20
|
+
Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif;
|
|
21
|
+
position: fixed; left: 50%; bottom: 20px; transform: translateX(-50%);
|
|
22
|
+
z-index: 2147483647; width: min(640px, calc(100vw - 32px)); color-scheme: light dark;
|
|
23
|
+
font: 400 14px/1.4 var(--font-inter); color: rgb(var(--theme-foreground-rgb));
|
|
24
|
+
-webkit-font-smoothing: antialiased;
|
|
25
|
+
}
|
|
26
|
+
@media (prefers-color-scheme: dark) {
|
|
27
|
+
.root {
|
|
28
|
+
--theme-foreground-rgb: var(--color-white-rgb);
|
|
29
|
+
--theme-background-alt-rgb: var(--color-black-off-rgb);
|
|
30
|
+
--theme-background-actions-rgb: var(--color-grey-dark-rgb);
|
|
31
|
+
--theme-border-rgb: var(--color-grey-rgb);
|
|
32
|
+
--theme-foreground-muted-rgb: var(--color-grey-light-rgb);
|
|
33
|
+
--theme-error: rgb(var(--color-red-rgb));
|
|
34
|
+
--shadow: 0 18px 40px -14px rgb(0 0 0 / .7), 0 2px 6px rgb(0 0 0 / .3);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
.icon { display: inline-flex; flex: none; }
|
|
38
|
+
.icon svg {
|
|
39
|
+
width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.75;
|
|
40
|
+
stroke-linecap: round; stroke-linejoin: round;
|
|
41
|
+
}
|
|
42
|
+
.visually-hidden {
|
|
43
|
+
position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
|
|
44
|
+
}
|
|
45
|
+
button { cursor: pointer; background: none; border: 0; padding: 0; }
|
|
46
|
+
button:disabled { cursor: not-allowed; }
|
|
47
|
+
button:focus-visible { outline: 2px solid rgb(var(--theme-accent-rgb)); outline-offset: 2px; }
|
|
48
|
+
|
|
49
|
+
/* Closed: one floating pill that reads like an empty field, with the mark. */
|
|
50
|
+
.bar { display: flex; justify-content: center; }
|
|
51
|
+
.pill {
|
|
52
|
+
display: flex; align-items: center; gap: 12px; width: min(560px, 100%); height: 56px;
|
|
53
|
+
padding: 0 22px 0 26px; border-radius: var(--border-radius-pill);
|
|
54
|
+
background: rgb(var(--theme-background-alt-rgb));
|
|
55
|
+
border: 1px solid rgb(var(--theme-border-rgb) / .25); box-shadow: var(--shadow);
|
|
56
|
+
color: rgb(var(--theme-foreground-muted-rgb)); font-size: 16px; text-align: left;
|
|
57
|
+
transition: transform .25s var(--ease-roll), color .25s var(--ease-roll);
|
|
58
|
+
}
|
|
59
|
+
button.pill:hover { color: rgb(var(--theme-foreground-rgb)); transform: translateY(-2px); }
|
|
60
|
+
.pill > span:first-child { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
61
|
+
.mark { color: rgb(var(--theme-accent-rgb)); font-size: 22px; line-height: 1; }
|
|
62
|
+
.pill.compact { width: auto; height: 40px; padding: 0 18px; font-size: 14px; gap: 4px; }
|
|
63
|
+
|
|
64
|
+
/* Open: a floating card, the text on top and a row of pill buttons below. */
|
|
65
|
+
.panel {
|
|
66
|
+
display: grid; background: rgb(var(--theme-background-alt-rgb));
|
|
67
|
+
border: 1px solid rgb(var(--theme-border-rgb) / .25); border-radius: var(--border-radius-medium);
|
|
68
|
+
box-shadow: var(--shadow); overflow: hidden;
|
|
69
|
+
transition: border-color .25s var(--ease-roll), box-shadow .25s var(--ease-roll);
|
|
70
|
+
animation: rise .25s var(--ease-roll);
|
|
71
|
+
}
|
|
72
|
+
.panel:focus-within {
|
|
73
|
+
border-color: rgb(var(--theme-accent-rgb) / .6);
|
|
74
|
+
box-shadow: 0 0 0 3px rgb(var(--theme-accent-rgb) / .18), var(--shadow);
|
|
75
|
+
}
|
|
76
|
+
@keyframes rise { from { opacity: 0; transform: translateY(8px) scale(.98); } }
|
|
77
|
+
@keyframes spin { to { transform: rotate(1turn); } }
|
|
78
|
+
@media (prefers-reduced-motion: reduce) {
|
|
79
|
+
.panel { animation: none; }
|
|
80
|
+
.pill { transition: none; }
|
|
81
|
+
}
|
|
82
|
+
.status {
|
|
83
|
+
padding: 16px 22px 0; font-size: 14px; color: rgb(var(--theme-foreground-muted-rgb));
|
|
84
|
+
display: flex; align-items: center; gap: 8px;
|
|
85
|
+
}
|
|
86
|
+
.status.working::before {
|
|
87
|
+
content: ''; width: 12px; height: 12px; flex: none; border-radius: 50%;
|
|
88
|
+
border: 2px solid rgb(var(--theme-border-rgb) / .3); border-top-color: rgb(var(--theme-accent-rgb));
|
|
89
|
+
animation: spin .7s linear infinite;
|
|
90
|
+
}
|
|
91
|
+
.status.error { color: var(--theme-error); }
|
|
92
|
+
textarea {
|
|
93
|
+
display: block; width: 100%; resize: none; min-height: 76px; max-height: 200px;
|
|
94
|
+
padding: 16px 22px 6px; border: 0; outline: 0; background: transparent;
|
|
95
|
+
font-size: 16px; line-height: 1.6; color: rgb(var(--theme-foreground-rgb));
|
|
96
|
+
}
|
|
97
|
+
textarea::placeholder { color: rgb(var(--theme-foreground-muted-rgb)); }
|
|
98
|
+
/* New page: where it goes, above what it is for. */
|
|
99
|
+
.path {
|
|
100
|
+
display: flex; align-items: center; gap: 10px; padding: 16px 22px 0;
|
|
101
|
+
color: rgb(var(--theme-foreground-muted-rgb));
|
|
102
|
+
}
|
|
103
|
+
.path input {
|
|
104
|
+
flex: 1; min-width: 0; height: 34px; padding: 0 14px; outline: 0;
|
|
105
|
+
border: 1px solid rgb(var(--theme-border-rgb) / .25); border-radius: var(--border-radius-pill);
|
|
106
|
+
background: rgb(var(--theme-background-actions-rgb) / .45); color: rgb(var(--theme-foreground-rgb));
|
|
107
|
+
transition: border-color .25s var(--ease-roll);
|
|
108
|
+
}
|
|
109
|
+
.path input:focus-visible { border-color: rgb(var(--theme-accent-rgb)); }
|
|
110
|
+
.path input::placeholder { color: rgb(var(--theme-foreground-muted-rgb)); }
|
|
111
|
+
.path input:disabled { opacity: .5; }
|
|
112
|
+
textarea:disabled { opacity: .5; }
|
|
113
|
+
.toolbar { display: flex; align-items: center; gap: 6px; padding: 8px 12px 12px; }
|
|
114
|
+
.spacer { flex: 1; }
|
|
115
|
+
.chip {
|
|
116
|
+
display: inline-flex; align-items: center; gap: 6px; height: 34px; padding: .4em 1.1em .4em .9em;
|
|
117
|
+
border: 1px solid rgb(var(--theme-border-rgb) / .2); border-radius: var(--border-radius-pill);
|
|
118
|
+
background: rgb(var(--theme-background-actions-rgb) / .6); white-space: nowrap;
|
|
119
|
+
transition: background-color .25s var(--ease-roll);
|
|
120
|
+
}
|
|
121
|
+
button.chip:hover:not(:disabled) { background: rgb(var(--theme-background-actions-rgb)); }
|
|
122
|
+
button.chip:disabled { opacity: .5; }
|
|
123
|
+
.scope { min-width: 0; background: transparent; }
|
|
124
|
+
.scope > span:not(.icon) { overflow: hidden; text-overflow: ellipsis; }
|
|
125
|
+
.scope button {
|
|
126
|
+
display: inline-flex; margin-right: -6px; padding: 3px; border-radius: 50%;
|
|
127
|
+
color: rgb(var(--theme-foreground-muted-rgb));
|
|
128
|
+
}
|
|
129
|
+
.scope button:hover { background: rgb(var(--theme-background-actions-rgb)); color: rgb(var(--theme-foreground-rgb)); }
|
|
130
|
+
.scope button svg { width: 14px; height: 14px; }
|
|
131
|
+
.icon-button {
|
|
132
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
133
|
+
width: 34px; height: 34px; border-radius: 50%; color: rgb(var(--theme-foreground-muted-rgb));
|
|
134
|
+
transition: background-color .25s var(--ease-roll), color .25s var(--ease-roll), opacity .25s var(--ease-roll);
|
|
135
|
+
}
|
|
136
|
+
.icon-button:hover:not(:disabled) { background: rgb(var(--theme-background-actions-rgb)); color: rgb(var(--theme-foreground-rgb)); }
|
|
137
|
+
/* The one call to action: white on orange. */
|
|
138
|
+
.icon-button.primary { background: rgb(var(--theme-accent-rgb)); color: #fff; }
|
|
139
|
+
.icon-button.primary:hover:not(:disabled) { background: rgb(var(--theme-accent-rgb)); color: #fff; opacity: .88; }
|
|
140
|
+
.icon-button.primary:disabled { opacity: .35; }
|
|
141
|
+
.divider { width: 1px; height: 20px; background: rgb(var(--theme-border-rgb) / .3); margin: 0 4px; }
|
|
142
|
+
|
|
143
|
+
/* On the site: storing the draft, and the way out. */
|
|
144
|
+
.footer {
|
|
145
|
+
display: flex; align-items: center; gap: 8px; padding: 10px 12px 10px 22px;
|
|
146
|
+
border-top: 1px solid rgb(var(--theme-border-rgb) / .2);
|
|
147
|
+
background: rgb(var(--theme-background-actions-rgb) / .45);
|
|
148
|
+
}
|
|
149
|
+
.footer .chip { height: 30px; padding: .4em 1.2em; background: rgb(var(--theme-background-alt-rgb)); }
|
|
150
|
+
.unsaved { color: rgb(var(--theme-foreground-muted-rgb)); display: inline-flex; align-items: center; }
|
|
151
|
+
.link { color: rgb(var(--theme-foreground-muted-rgb)); text-decoration: underline; text-underline-offset: 3px; padding: 0 6px; }
|
|
152
|
+
.link:hover { color: rgb(var(--theme-foreground-rgb)); }
|
|
153
|
+
.dot {
|
|
154
|
+
display: inline-block; width: 7px; height: 7px; border-radius: 50%;
|
|
155
|
+
background: rgb(var(--theme-accent-rgb)); margin-right: 8px;
|
|
156
|
+
}
|
|
157
|
+
[hidden] { display: none !important; }
|
|
158
|
+
`,b=(e,t,r={},...n)=>{let o=e.createElement(t);for(let t of(Object.assign(o,r),n))o.appendChild("string"==typeof t?e.createTextNode(t):t);return o},m=(e,t)=>{let r=b(e,"span",{className:"icon"});return r.setAttribute("aria-hidden","true"),r.innerHTML=`<svg viewBox="0 0 24 24">${u[t]}</svg>`,r},f=(e,t,r={})=>{let n={...g,...r.labels},o=b(e,"div");o.setAttribute("data-obelism-content-prompt","");let a=o.attachShadow({mode:"open"}),i="site"===t.mode,s=b(e,"span",{textContent:n.open}),l=b(e,"button",{className:"pill",type:"button"},s,b(e,"span",{className:"mark",textContent:"⁜"})),d=b(e,"span",{className:"pill compact"},`${n.viewingDrafts} \xb7 `),c=b(e,"button",{className:"link",type:"button",textContent:n.signOut});d.appendChild(c);let p=b(e,"p",{className:"status"});p.setAttribute("role","status");let u=b(e,"textarea",{placeholder:n.placeholder,rows:1});u.setAttribute("aria-label",n.open);let f=m(e,"page"),v=m(e,"block"),y=b(e,"span"),x=b(e,"button",{type:"button",title:n.clearBlock});x.setAttribute("aria-label",n.clearBlock),x.appendChild(m(e,"close"));let w=b(e,"span",{className:"chip scope"},f,v,y,x),k=b(e,"button",{className:"action chip",type:"button"},m(e,"undo"),n.undo),E=b(e,"button",{className:"action chip",type:"button"},m(e,"newPage"),n.newPage),S=b(e,"button",{type:"button",title:n.newPageCancel});S.setAttribute("aria-label",n.newPageCancel),S.appendChild(m(e,"close"));let C=b(e,"span",{className:"chip scope"},m(e,"newPage"),b(e,"span",{textContent:n.newPage}),S),N=b(e,"input",{type:"text",placeholder:n.pathPlaceholder,autocomplete:"off",spellcheck:!1}),U=b(e,"label",{className:"path"},b(e,"span",{textContent:n.path}),N),P=b(e,"button",{className:"close icon-button",type:"button",title:n.close},m(e,"close"));P.setAttribute("aria-label",n.close);let L=b(e,"button",{className:"action primary icon-button",type:"submit"},b(e,"span",{className:"visually-hidden",textContent:n.send}),m(e,"send"));L.title=n.send;let $=b(e,"span",{className:"unsaved"});$.appendChild(b(e,"span",{className:"dot"})),$.appendChild(e.createTextNode(n.unsaved));let A=b(e,"button",{className:"link",type:"button",textContent:n.signOut}),O=b(e,"button",{className:"action chip",type:"button",textContent:n.save}),I=b(e,"button",{className:"action chip",type:"button",textContent:n.publish}),M=b(e,"div",{className:"footer"},$,b(e,"span",{className:"spacer"}),A,O,I),j=b(e,"form",{className:"panel"},p,U,u,b(e,"div",{className:"toolbar"},w,C,k,E,b(e,"span",{className:"spacer"}),P,b(e,"span",{className:"divider"}),L),M),R=b(e,"div",{className:"root"},b(e,"div",{className:"bar"},l,d),j);a.append(b(e,"style",{textContent:h}),R);let z=!1,D=!1,T=null,B=e=>{let r=e.features.includes("prompt"),o=e.saving||e.publishing,a=e.prompting||o;R.hidden=!r&&!i,l.hidden=z||!r,d.hidden=r||!i,j.hidden=!z||!r;let c=i&&e.features.includes("create"),g=i&&null===t.entryId,h=g&&c;c||(D=!1),h&&(D=!0),s.textContent=h?n.newPage:n.open,S.hidden=h,w.hidden=D,C.hidden=!D,E.hidden=D||!c,E.disabled=a,U.hidden=!D,N.disabled=a,u.placeholder=D?n.newPagePlaceholder:n.placeholder,y.textContent=e.selectedUid?n.scopeBlock:n.scopeEntry,x.hidden=!e.selectedUid,f.hidden=!!e.selectedUid,v.hidden=!e.selectedUid,k.hidden=D||!e.canUndo,k.disabled=a,L.disabled=a||""===u.value.trim()||D&&""===N.value.trim(),u.disabled=a,O.hidden=!e.features.includes("save")||g,O.disabled=a||!e.unsaved,O.textContent=e.saving?n.saving:n.save,I.hidden=!e.features.includes("publish")||g,I.disabled=a,I.textContent=e.publishing?n.publishing:n.publish,A.hidden=!i,M.hidden=!i,$.hidden=!e.unsaved;let b=e.prompting?D?n.creating:n.working:T?.text??(!i||D||e.selectedUid?"":n.selectHint);p.textContent=b,p.hidden=!b,p.className=`status${e.prompting?" working":""}${!a&&T?.error?" error":""}`},q=()=>B(t.getState()),H=()=>{u.style.height="auto",u.style.height=`${u.scrollHeight}px`},Y=async()=>{let e=u.value.trim();if(e&&!t.getState().prompting){T=null;try{if(D){let r=N.value.trim();if(!r)return N.focus();await t.createPage(r,e),T={text:n.created,error:!1}}else{let{summary:r}=await t.prompt(e);T={text:r,error:!1}}u.value="",H()}catch(e){T={text:e instanceof Error?e.message:String(e),error:!0}}q(),u.focus()}},_=e=>{z=e,q(),i&&t.setSelecting(z&&!D)},V=e=>{D=e,T=null,t.setSelecting(z&&!D),D&&t.select(null),q(),(D?N:u).focus()},W=async e=>{T=null;try{await t[e](),T={text:"save"===e?n.saved:n.published,error:!1}}catch(e){T={text:e instanceof Error?e.message:String(e),error:!0}}q()};l.addEventListener("click",()=>{_(!0),(D?N:u).focus()}),P.addEventListener("click",()=>_(!1)),O.addEventListener("click",()=>void W("save")),I.addEventListener("click",()=>void W("publish")),A.addEventListener("click",()=>t.signOut()),c.addEventListener("click",()=>t.signOut()),x.addEventListener("click",()=>t.select(null)),E.addEventListener("click",()=>V(!0)),S.addEventListener("click",()=>V(!1)),N.addEventListener("input",q),N.addEventListener("keydown",e=>{"Enter"!==e.key||e.isComposing?"Escape"===e.key&&_(!1):(e.preventDefault(),u.focus())}),k.addEventListener("click",()=>{T=null,t.undo()}),j.addEventListener("submit",e=>{e.preventDefault(),Y()}),u.addEventListener("input",()=>{H(),q()}),u.addEventListener("keydown",e=>{"Enter"!==e.key||e.shiftKey||e.isComposing?"Escape"===e.key&&_(!1):(e.preventDefault(),Y())});let F=t.on("change",B);return q(),e.body.appendChild(o),()=>{F(),o.remove()}},v="https://content.obelism.studio",y=(e,t)=>JSON.stringify(e)===JSON.stringify(t),x=Symbol.for("@obelism/content/bridge"),w=p({isEditing:!1,entryId:null,on:()=>{}},new URLSearchParams),k=({url:e,prompt:o=!0,pagePath:l}={})=>{let g;if("u"<typeof window)return w;let u=window;if(u[x])return u[x];if(u.ObelismBridge)g=p(u.ObelismBridge,new URLSearchParams(u.location.search));else{let p=(g=((e,r)=>{let o=new URLSearchParams(e.location.search),l=e.parent!==e&&o.has("cms_preview"),p=s(),g=[],u={...i,selecting:!0},h=e=>{u={...u,...e},p.emit("change",u)},b={isEditing:l,mode:l?"editor":null,entryId:o.get("cms_entry"),environment:o.get(n),on:p.on,getState:()=>u,...d,destroy:()=>{g.splice(0).forEach(e=>e())}};if(!l)return b;let m=e.document,f=(t,n={})=>{e.parent.postMessage({source:"obelism-content-bridge",type:t,...n},r)},v=m.createElement("style");v.textContent=a(),m.head.appendChild(v),g.push(()=>v.remove());let y=(e,t)=>{if(m.querySelectorAll("[data-cms-selected]").forEach(e=>{e.removeAttribute("data-cms-selected")}),u.selectedUid!==(e??null)&&h({selectedUid:e??null}),!e)return;let r=m.querySelector(c(e));r&&(r.setAttribute("data-cms-selected",""),t&&r.scrollIntoView({behavior:"smooth",block:"center"}))},x=e=>{let r=e.target,n=r?.closest?.(`[${t}]`);if(!n)return;e.preventDefault(),e.stopPropagation();let o=n.getAttribute(t);y(o,!1),f("select",{uid:o})};m.addEventListener("click",x,!0),g.push(()=>m.removeEventListener("click",x,!0));let w=()=>f("ready",{url:e.location.href}),k=new Map,E=0,S=e=>{let t=k.get(e);return t?(clearTimeout(t.timer),k.delete(e),0===k.size&&h({prompting:!1}),t):null};b.prompt=(e,t={})=>{let r=e.trim();if(!r)return Promise.reject(Error("Say what to change"));if(!u.features.includes("prompt"))return Promise.reject(Error("The editor does not offer prompts here"));let n=`prompt_${++E}`,o=void 0===t.uid?u.selectedUid:t.uid;return new Promise((e,t)=>{let a=setTimeout(()=>{S(n)?.reject(Error("The editor did not answer in time"))},12e4);k.set(n,{resolve:e,reject:t,timer:a}),h({prompting:!0}),f("prompt",{id:n,prompt:r,uid:o})})},b.undo=()=>f("undo"),b.select=e=>{y(e,!1),f("select",{uid:e})},g.push(()=>{for(let e of k.keys())S(e)?.reject(Error("The bridge was destroyed"))});let C=t=>{if(t.origin!==r||t.source!==e.parent)return;let n=t.data;if(n&&"obelism-content-editor"===n.source){if("connect"===n.type)w();else if("update"===n.type&&n.entry)p.emit("update",{entry:n.entry,selectedUid:n.selectedUid??null}),e.requestAnimationFrame(()=>y(n.selectedUid,!1));else if("select"===n.type)y(n.uid,!0);else if("saved"===n.type||"published"===n.type)p.emit(n.type,{}),0===p.listeners.update.size&&e.location.reload();else if("features"===n.type&&Array.isArray(n.features))h({features:n.features.filter(e=>"string"==typeof e)});else if("promptState"===n.type)h({canUndo:!0===n.canUndo});else if("promptResult"===n.type&&"string"==typeof n.id){let e=S(n.id);if(!e)return;!0===n.ok?e.resolve({summary:"string"==typeof n.summary?n.summary:""}):e.reject(Error("string"==typeof n.error?n.error:"The prompt failed"))}}};return e.addEventListener("message",C),g.push(()=>e.removeEventListener("message",C)),"loading"===m.readyState?(m.addEventListener("DOMContentLoaded",w,{once:!0}),g.push(()=>m.removeEventListener("DOMContentLoaded",w))):w(),b})(u,new URL(e||v).origin)).isEditing||u.parent!==u?null:(e=>{for(let t of e.cookie.split(";")){let[e,...r]=t.trim().split("=");if("obelism_content_editing"===e)try{let e=decodeURIComponent(r.join("="));return e.startsWith("/")&&!e.startsWith("//")?e:null}catch{break}}return null})(u.document);if(p&&(g=((e,n,{pagePath:o=e=>`/${e}`}={})=>{let l=e.document,d=s(),p=[],g=new Map,u=[],h=new Set,b=null,m=i,f=e=>{m={...m,...e},d.emit("change",m)},v=()=>f({unsaved:[...g.values()].some(({content:e,saved:t})=>!y(e,t)),canUndo:u.length>0}),x=async(t,r)=>{let o=await e.fetch(`${n}/api/${t}`,void 0===r?{credentials:"same-origin"}:{method:"POST",credentials:"same-origin",headers:{"Content-Type":"application/json","x-obelism-content":"1"},body:JSON.stringify(r)}),a=await o.json().catch(()=>({}));if(401===o.status)throw f({features:[]}),Error("You were signed out. Sign in again to edit.");if(!o.ok)throw Error("string"==typeof a.error?a.error:`Obelism Content answered ${o.status}`);return a},w=()=>l.querySelector(`[${r}]`)?.getAttribute(r)??null,k=()=>{if(!b)return;let e=b.space.locales.map(({code:e})=>e),t=l.documentElement.lang;if(e.includes(t))return t;let r=t.split("-")[0]??"";return e.includes(r)?r:b.space.defaultLocale},E=async e=>{let t=g.get(e);if(t)return t;let r=await x(`entries/${encodeURIComponent(e)}`),n={content:r.content,saved:r.content,revision:r.draftRevision};return g.set(e,n),n},S=e=>{l.querySelectorAll("[data-cms-selected]").forEach(e=>{e.removeAttribute("data-cms-selected")}),m.selectedUid!==e&&f({selectedUid:e}),e&&l.querySelector(c(e))?.setAttribute("data-cms-selected","")},C=async t=>{if(0===d.listeners.update.size)return;let r=g.get(t)?.content,{entry:n}=await x(`entries/${encodeURIComponent(t)}/preview`,{content:r,locale:k()});d.emit("update",{entry:n,selectedUid:m.selectedUid}),e.requestAnimationFrame(()=>S(m.selectedUid))},N=t=>{d.emit(t,{}),0===d.listeners.update.size&&e.location.reload()},U=async()=>{for(let[e,t]of g){if(y(t.content,t.saved))continue;let{draftRevision:r}=await x(`entries/${encodeURIComponent(e)}/save`,{content:t.content,baseRevision:t.revision});t.saved=t.content,t.revision=r,h.add(e)}u.length=0,v()},P={isEditing:!0,mode:"site",get entryId(){return w()},environment:null,on:d.on,getState:()=>m,async prompt(e,t={}){let n,o=e.trim();if(!o)throw Error("Say what to change");if(!m.features.includes("prompt"))throw Error("You may not edit content here");let a=void 0===t.uid?m.selectedUid:t.uid,i=(n=a?l.querySelector(c(a)):null,n?.closest(`[${r}]`)?.getAttribute(r)??w());if(!i)throw Error("Nothing on this page is marked as an entry: add editable(entry)");f({prompting:!0});try{let e=await E(i),t=await x(`entries/${encodeURIComponent(i)}/prompt`,{prompt:o,content:e.content,locale:k(),uid:a});return u.push({entryId:i,content:e.content}),e.content=t.content,v(),await C(i),{summary:t.summary}}finally{f({prompting:!1})}},async createPage(t,r){let n=r.trim();if(!n)throw Error("Say what the page is for");if(!t.trim())throw Error("Say where the page goes, like /pricing");if(!m.features.includes("create"))throw Error("You may not add pages here");if(m.unsaved)throw Error("Save or undo your changes before adding a page");f({prompting:!0});try{let r=await x("entries",{path:t,prompt:n,locale:k(),from:w()}),a=o(r.path);return e.location.assign(a),{summary:r.summary,entryId:r.entryId,path:r.path,url:a}}finally{f({prompting:!1})}},undo(){let e=u.pop(),t=e&&g.get(e.entryId);e&&t&&(t.content=e.content,v(),C(e.entryId).catch(e=>console.error("[@obelism/content bridge]",e)))},async save(){if(!m.features.includes("save"))throw Error("You may not edit content here");f({saving:!0});try{await U()}finally{f({saving:!1})}N("saved")},async publish(){if(!m.features.includes("publish"))throw Error("You may not publish here");f({publishing:!0});try{await U();let e=w(),t=h.size>0?[...h]:e?[e]:[];if(0===t.length)throw Error("Nothing on this page is marked as an entry: add editable(entry)");for(let e of t)await x(`entries/${encodeURIComponent(e)}/publish`,{}),h.delete(e)}finally{f({publishing:!1})}N("published")},signOut(){if(m.unsaved&&!e.confirm("Sign out and lose the changes you have not saved?"))return;g.clear(),v();let t=e.location.pathname+e.location.search;e.location.href=`${n}/logout?return_to=${encodeURIComponent(t)}`},select(e){S(e)},setSelecting(e){e?l.documentElement.setAttribute("data-cms-selecting",""):l.documentElement.removeAttribute("data-cms-selecting"),f({selecting:e})},destroy(){p.splice(0).forEach(e=>e())}},L=l.createElement("style");L.textContent=a("html[data-cms-selecting] "),l.head.appendChild(L),p.push(()=>L.remove()),p.push(()=>l.documentElement.removeAttribute("data-cms-selecting"));let $=e=>{if(!m.selecting)return;let r=e.target,n=r?.closest?.(`[${t}]`);n&&(e.preventDefault(),e.stopPropagation(),S(n.getAttribute(t)))};l.addEventListener("click",$,!0),p.push(()=>l.removeEventListener("click",$,!0));let A=e=>{m.unsaved&&(e.preventDefault(),e.returnValue="")};return e.addEventListener("beforeunload",A),p.push(()=>e.removeEventListener("beforeunload",A)),x("session").then(e=>{b=e,f({features:e.features})}).catch(e=>console.error("[@obelism/content bridge]",e)),P})(u,p,{pagePath:l})),g.isEditing&&o){let e=f(u.document,g,!0===o?{}:o),t=g.destroy;g.destroy=()=>{e(),t()}}}return u[x]=g,g};(e=window).ObelismBridge=k((e=>{let t=e?.dataset.url,r=t||v;if(!t&&e?.src){let t=new URL(e.src);"/bridge.js"===t.pathname&&(r=t.origin)}return{url:r,prompt:e?.dataset.prompt!=="false"}})(document.currentScript)),e.ObelismContent={getContentBridge:k,mountPromptBox:f}}();
|
package/dist/bridge.js
CHANGED
|
@@ -64,6 +64,7 @@ const onlyOnSite = ()=>Promise.reject(new Error('Save and publish in the visual
|
|
|
64
64
|
// What every bridge without a feature does for it.
|
|
65
65
|
const INERT_METHODS = {
|
|
66
66
|
prompt: inertPrompt,
|
|
67
|
+
createPage: ()=>Promise.reject(new Error('New pages are made on the site itself')),
|
|
67
68
|
undo: ()=>{},
|
|
68
69
|
save: onlyOnSite,
|
|
69
70
|
publish: onlyOnSite,
|
|
@@ -306,11 +307,19 @@ const DEFAULT_PROMPT_LABELS = {
|
|
|
306
307
|
unsaved: 'Unsaved changes',
|
|
307
308
|
signOut: 'Exit editing',
|
|
308
309
|
viewingDrafts: 'Viewing drafts',
|
|
309
|
-
selectHint: 'Click a block to change only that block'
|
|
310
|
+
selectHint: 'Click a block to change only that block',
|
|
311
|
+
newPage: 'New page',
|
|
312
|
+
newPageCancel: 'Edit this page instead',
|
|
313
|
+
newPagePlaceholder: 'Describe the new page, e.g. “a pricing page for agencies”',
|
|
314
|
+
path: 'Path',
|
|
315
|
+
pathPlaceholder: '/pricing',
|
|
316
|
+
creating: 'Writing the page…',
|
|
317
|
+
created: 'Opening the new page…'
|
|
310
318
|
};
|
|
311
319
|
// Icons (Lucide), drawn in the text colour.
|
|
312
320
|
const ICONS = {
|
|
313
321
|
page: '<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/>',
|
|
322
|
+
newPage: '<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M9 15h6"/><path d="M12 18v-6"/>',
|
|
314
323
|
block: '<rect width="18" height="18" x="3" y="3" rx="2"/><path d="M7 8h10"/><path d="M7 12h6"/>',
|
|
315
324
|
undo: '<path d="M9 14 4 9l5-5"/><path d="M4 9h10.5a5.5 5.5 0 0 1 0 11H11"/>',
|
|
316
325
|
close: '<path d="M18 6 6 18"/><path d="m6 6 12 12"/>',
|
|
@@ -415,6 +424,20 @@ textarea {
|
|
|
415
424
|
font-size: 16px; line-height: 1.6; color: rgb(var(--theme-foreground-rgb));
|
|
416
425
|
}
|
|
417
426
|
textarea::placeholder { color: rgb(var(--theme-foreground-muted-rgb)); }
|
|
427
|
+
/* New page: where it goes, above what it is for. */
|
|
428
|
+
.path {
|
|
429
|
+
display: flex; align-items: center; gap: 10px; padding: 16px 22px 0;
|
|
430
|
+
color: rgb(var(--theme-foreground-muted-rgb));
|
|
431
|
+
}
|
|
432
|
+
.path input {
|
|
433
|
+
flex: 1; min-width: 0; height: 34px; padding: 0 14px; outline: 0;
|
|
434
|
+
border: 1px solid rgb(var(--theme-border-rgb) / .25); border-radius: var(--border-radius-pill);
|
|
435
|
+
background: rgb(var(--theme-background-actions-rgb) / .45); color: rgb(var(--theme-foreground-rgb));
|
|
436
|
+
transition: border-color .25s var(--ease-roll);
|
|
437
|
+
}
|
|
438
|
+
.path input:focus-visible { border-color: rgb(var(--theme-accent-rgb)); }
|
|
439
|
+
.path input::placeholder { color: rgb(var(--theme-foreground-muted-rgb)); }
|
|
440
|
+
.path input:disabled { opacity: .5; }
|
|
418
441
|
textarea:disabled { opacity: .5; }
|
|
419
442
|
.toolbar { display: flex; align-items: center; gap: 6px; padding: 8px 12px 12px; }
|
|
420
443
|
.spacer { flex: 1; }
|
|
@@ -492,12 +515,13 @@ const mountPromptBox = (doc, bridge, options = {})=>{
|
|
|
492
515
|
mode: 'open'
|
|
493
516
|
});
|
|
494
517
|
const site = bridge.mode === 'site';
|
|
518
|
+
const pillText = element(doc, 'span', {
|
|
519
|
+
textContent: labels.open
|
|
520
|
+
});
|
|
495
521
|
const pill = element(doc, 'button', {
|
|
496
522
|
className: 'pill',
|
|
497
523
|
type: 'button'
|
|
498
|
-
},
|
|
499
|
-
textContent: labels.open
|
|
500
|
-
}), // The Obelism mark.
|
|
524
|
+
}, pillText, // The Obelism mark.
|
|
501
525
|
element(doc, 'span', {
|
|
502
526
|
className: 'mark',
|
|
503
527
|
textContent: '⁜'
|
|
@@ -540,6 +564,34 @@ const mountPromptBox = (doc, bridge, options = {})=>{
|
|
|
540
564
|
className: 'action chip',
|
|
541
565
|
type: 'button'
|
|
542
566
|
}, icon(doc, 'undo'), labels.undo);
|
|
567
|
+
// New page (on the site, for people who may add pages): the scope becomes
|
|
568
|
+
// the new page, with a way back, and a path field shows above the prompt.
|
|
569
|
+
const newPage = element(doc, 'button', {
|
|
570
|
+
className: 'action chip',
|
|
571
|
+
type: 'button'
|
|
572
|
+
}, icon(doc, 'newPage'), labels.newPage);
|
|
573
|
+
const cancelNew = element(doc, 'button', {
|
|
574
|
+
type: 'button',
|
|
575
|
+
title: labels.newPageCancel
|
|
576
|
+
});
|
|
577
|
+
cancelNew.setAttribute('aria-label', labels.newPageCancel);
|
|
578
|
+
cancelNew.appendChild(icon(doc, 'close'));
|
|
579
|
+
const newScope = element(doc, 'span', {
|
|
580
|
+
className: 'chip scope'
|
|
581
|
+
}, icon(doc, 'newPage'), element(doc, 'span', {
|
|
582
|
+
textContent: labels.newPage
|
|
583
|
+
}), cancelNew);
|
|
584
|
+
const pathInput = element(doc, 'input', {
|
|
585
|
+
type: 'text',
|
|
586
|
+
placeholder: labels.pathPlaceholder,
|
|
587
|
+
autocomplete: 'off',
|
|
588
|
+
spellcheck: false
|
|
589
|
+
});
|
|
590
|
+
const pathRow = element(doc, 'label', {
|
|
591
|
+
className: 'path'
|
|
592
|
+
}, element(doc, 'span', {
|
|
593
|
+
textContent: labels.path
|
|
594
|
+
}), pathInput);
|
|
543
595
|
const close = element(doc, 'button', {
|
|
544
596
|
className: 'close icon-button',
|
|
545
597
|
type: 'button',
|
|
@@ -583,9 +635,9 @@ const mountPromptBox = (doc, bridge, options = {})=>{
|
|
|
583
635
|
}), signOut, save, publish);
|
|
584
636
|
const panel = element(doc, 'form', {
|
|
585
637
|
className: 'panel'
|
|
586
|
-
}, status, input, element(doc, 'div', {
|
|
638
|
+
}, status, pathRow, input, element(doc, 'div', {
|
|
587
639
|
className: 'toolbar'
|
|
588
|
-
}, scope, undo, element(doc, 'span', {
|
|
640
|
+
}, scope, newScope, undo, newPage, element(doc, 'span', {
|
|
589
641
|
className: 'spacer'
|
|
590
642
|
}), close, element(doc, 'span', {
|
|
591
643
|
className: 'divider'
|
|
@@ -599,6 +651,7 @@ const mountPromptBox = (doc, bridge, options = {})=>{
|
|
|
599
651
|
textContent: CSS
|
|
600
652
|
}), root);
|
|
601
653
|
let open = false;
|
|
654
|
+
let creating = false;
|
|
602
655
|
let message = null;
|
|
603
656
|
const render = (state)=>{
|
|
604
657
|
const available = state.features.includes('prompt');
|
|
@@ -608,24 +661,40 @@ const mountPromptBox = (doc, bridge, options = {})=>{
|
|
|
608
661
|
pill.hidden = open || !available;
|
|
609
662
|
viewing.hidden = available || !site;
|
|
610
663
|
panel.hidden = !open || !available;
|
|
664
|
+
const canCreate = site && state.features.includes('create');
|
|
665
|
+
// A page of the site that isn't an entry (no `editable(entry)`, like a
|
|
666
|
+
// hand-written /about) has nothing to edit: only New page.
|
|
667
|
+
const pageless = site && bridge.entryId === null;
|
|
668
|
+
const onlyNew = pageless && canCreate;
|
|
669
|
+
if (!canCreate) creating = false;
|
|
670
|
+
if (onlyNew) creating = true;
|
|
671
|
+
pillText.textContent = onlyNew ? labels.newPage : labels.open;
|
|
672
|
+
cancelNew.hidden = onlyNew;
|
|
673
|
+
scope.hidden = creating;
|
|
674
|
+
newScope.hidden = !creating;
|
|
675
|
+
newPage.hidden = creating || !canCreate;
|
|
676
|
+
newPage.disabled = busy;
|
|
677
|
+
pathRow.hidden = !creating;
|
|
678
|
+
pathInput.disabled = busy;
|
|
679
|
+
input.placeholder = creating ? labels.newPagePlaceholder : labels.placeholder;
|
|
611
680
|
scopeText.textContent = state.selectedUid ? labels.scopeBlock : labels.scopeEntry;
|
|
612
681
|
clearBlock.hidden = !state.selectedUid;
|
|
613
682
|
pageIcon.hidden = !!state.selectedUid;
|
|
614
683
|
blockIcon.hidden = !state.selectedUid;
|
|
615
|
-
undo.hidden = !state.canUndo;
|
|
684
|
+
undo.hidden = creating || !state.canUndo;
|
|
616
685
|
undo.disabled = busy;
|
|
617
|
-
send.disabled = busy || input.value.trim() === '';
|
|
686
|
+
send.disabled = busy || input.value.trim() === '' || creating && pathInput.value.trim() === '';
|
|
618
687
|
input.disabled = busy;
|
|
619
|
-
save.hidden = !state.features.includes('save');
|
|
688
|
+
save.hidden = !state.features.includes('save') || pageless;
|
|
620
689
|
save.disabled = busy || !state.unsaved;
|
|
621
690
|
save.textContent = state.saving ? labels.saving : labels.save;
|
|
622
|
-
publish.hidden = !state.features.includes('publish');
|
|
691
|
+
publish.hidden = !state.features.includes('publish') || pageless;
|
|
623
692
|
publish.disabled = busy;
|
|
624
693
|
publish.textContent = state.publishing ? labels.publishing : labels.publish;
|
|
625
694
|
signOut.hidden = !site;
|
|
626
695
|
footer.hidden = !site;
|
|
627
696
|
unsaved.hidden = !state.unsaved;
|
|
628
|
-
const text = state.prompting ? labels.working : message?.text ?? (site && !state.selectedUid ? labels.selectHint : '');
|
|
697
|
+
const text = state.prompting ? creating ? labels.creating : labels.working : message?.text ?? (site && !creating && !state.selectedUid ? labels.selectHint : '');
|
|
629
698
|
status.textContent = text;
|
|
630
699
|
status.hidden = !text;
|
|
631
700
|
status.className = `status${state.prompting ? ' working' : ''}${!busy && message?.error ? ' error' : ''}`;
|
|
@@ -640,11 +709,22 @@ const mountPromptBox = (doc, bridge, options = {})=>{
|
|
|
640
709
|
if (!prompt || bridge.getState().prompting) return;
|
|
641
710
|
message = null;
|
|
642
711
|
try {
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
712
|
+
if (creating) {
|
|
713
|
+
const path = pathInput.value.trim();
|
|
714
|
+
if (!path) return pathInput.focus();
|
|
715
|
+
// The bridge opens the new page; this shows until it does.
|
|
716
|
+
await bridge.createPage(path, prompt);
|
|
717
|
+
message = {
|
|
718
|
+
text: labels.created,
|
|
719
|
+
error: false
|
|
720
|
+
};
|
|
721
|
+
} else {
|
|
722
|
+
const { summary } = await bridge.prompt(prompt);
|
|
723
|
+
message = {
|
|
724
|
+
text: summary,
|
|
725
|
+
error: false
|
|
726
|
+
};
|
|
727
|
+
}
|
|
648
728
|
input.value = '';
|
|
649
729
|
grow();
|
|
650
730
|
} catch (error) {
|
|
@@ -658,8 +738,16 @@ const mountPromptBox = (doc, bridge, options = {})=>{
|
|
|
658
738
|
};
|
|
659
739
|
const setOpen = (next)=>{
|
|
660
740
|
open = next;
|
|
661
|
-
if (site) bridge.setSelecting(open);
|
|
662
741
|
refresh();
|
|
742
|
+
if (site) bridge.setSelecting(open && !creating);
|
|
743
|
+
};
|
|
744
|
+
const setCreating = (next)=>{
|
|
745
|
+
creating = next;
|
|
746
|
+
message = null;
|
|
747
|
+
bridge.setSelecting(open && !creating);
|
|
748
|
+
if (creating) bridge.select(null);
|
|
749
|
+
refresh();
|
|
750
|
+
(creating ? pathInput : input).focus();
|
|
663
751
|
};
|
|
664
752
|
const store = async (action)=>{
|
|
665
753
|
message = null;
|
|
@@ -679,7 +767,7 @@ const mountPromptBox = (doc, bridge, options = {})=>{
|
|
|
679
767
|
};
|
|
680
768
|
pill.addEventListener('click', ()=>{
|
|
681
769
|
setOpen(true);
|
|
682
|
-
input.focus();
|
|
770
|
+
(creating ? pathInput : input).focus();
|
|
683
771
|
});
|
|
684
772
|
close.addEventListener('click', ()=>setOpen(false));
|
|
685
773
|
save.addEventListener('click', ()=>void store('save'));
|
|
@@ -687,6 +775,17 @@ const mountPromptBox = (doc, bridge, options = {})=>{
|
|
|
687
775
|
signOut.addEventListener('click', ()=>bridge.signOut());
|
|
688
776
|
pillSignOut.addEventListener('click', ()=>bridge.signOut());
|
|
689
777
|
clearBlock.addEventListener('click', ()=>bridge.select(null));
|
|
778
|
+
newPage.addEventListener('click', ()=>setCreating(true));
|
|
779
|
+
cancelNew.addEventListener('click', ()=>setCreating(false));
|
|
780
|
+
pathInput.addEventListener('input', refresh);
|
|
781
|
+
pathInput.addEventListener('keydown', (event)=>{
|
|
782
|
+
if (event.key === 'Enter' && !event.isComposing) {
|
|
783
|
+
event.preventDefault();
|
|
784
|
+
input.focus();
|
|
785
|
+
} else if (event.key === 'Escape') {
|
|
786
|
+
setOpen(false);
|
|
787
|
+
}
|
|
788
|
+
});
|
|
690
789
|
undo.addEventListener('click', ()=>{
|
|
691
790
|
message = null;
|
|
692
791
|
bridge.undo();
|
|
@@ -740,7 +839,7 @@ const readSiteEditing = (doc)=>{
|
|
|
740
839
|
return null;
|
|
741
840
|
};
|
|
742
841
|
const same = (a, b)=>JSON.stringify(a) === JSON.stringify(b);
|
|
743
|
-
const createSiteBridge = (win, basePath)=>{
|
|
842
|
+
const createSiteBridge = (win, basePath, { pagePath = (path)=>`/${path}` } = {})=>{
|
|
744
843
|
const doc = win.document;
|
|
745
844
|
const emitter = createEmitter();
|
|
746
845
|
const cleanup = [];
|
|
@@ -903,6 +1002,39 @@ const createSiteBridge = (win, basePath)=>{
|
|
|
903
1002
|
});
|
|
904
1003
|
}
|
|
905
1004
|
},
|
|
1005
|
+
async createPage (path, prompt) {
|
|
1006
|
+
const text = prompt.trim();
|
|
1007
|
+
if (!text) throw new Error('Say what the page is for');
|
|
1008
|
+
if (!path.trim()) throw new Error('Say where the page goes, like /pricing');
|
|
1009
|
+
if (!has('create')) throw new Error('You may not add pages here');
|
|
1010
|
+
// Opening the new page would lose them.
|
|
1011
|
+
if (state.unsaved) {
|
|
1012
|
+
throw new Error('Save or undo your changes before adding a page');
|
|
1013
|
+
}
|
|
1014
|
+
setState({
|
|
1015
|
+
prompting: true
|
|
1016
|
+
});
|
|
1017
|
+
try {
|
|
1018
|
+
const created = await api('entries', {
|
|
1019
|
+
path,
|
|
1020
|
+
prompt: text,
|
|
1021
|
+
locale: locale(),
|
|
1022
|
+
from: pageEntryId()
|
|
1023
|
+
});
|
|
1024
|
+
const url = pagePath(created.path);
|
|
1025
|
+
win.location.assign(url);
|
|
1026
|
+
return {
|
|
1027
|
+
summary: created.summary,
|
|
1028
|
+
entryId: created.entryId,
|
|
1029
|
+
path: created.path,
|
|
1030
|
+
url
|
|
1031
|
+
};
|
|
1032
|
+
} finally{
|
|
1033
|
+
setState({
|
|
1034
|
+
prompting: false
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
1037
|
+
},
|
|
906
1038
|
undo () {
|
|
907
1039
|
const last = history.pop();
|
|
908
1040
|
const entry = last && working.get(last.entryId);
|
|
@@ -1029,7 +1161,7 @@ const peekContentBridge = ()=>typeof window === 'undefined' ? null : window[KEY]
|
|
|
1029
1161
|
// if (bridge.isEditing) bridge.on('update', ({ entry }) => render(entry))
|
|
1030
1162
|
//
|
|
1031
1163
|
// The first call's options win: there is one bridge per page.
|
|
1032
|
-
const getContentBridge = ({ url, prompt = true } = {})=>{
|
|
1164
|
+
const getContentBridge = ({ url, prompt = true, pagePath } = {})=>{
|
|
1033
1165
|
if (typeof window === 'undefined') return SERVER_BRIDGE;
|
|
1034
1166
|
const win = window;
|
|
1035
1167
|
if (win[KEY]) return win[KEY];
|
|
@@ -1041,7 +1173,9 @@ const getContentBridge = ({ url, prompt = true } = {})=>{
|
|
|
1041
1173
|
// Not in the visual editor, but signed in to edit through the site's
|
|
1042
1174
|
// own `/cms/login` (@obelism/content/site).
|
|
1043
1175
|
const sitePath = bridge.isEditing || win.parent !== win ? null : readSiteEditing(win.document);
|
|
1044
|
-
if (sitePath) bridge = createSiteBridge(win, sitePath
|
|
1176
|
+
if (sitePath) bridge = createSiteBridge(win, sitePath, {
|
|
1177
|
+
pagePath
|
|
1178
|
+
});
|
|
1045
1179
|
if (bridge.isEditing && prompt) {
|
|
1046
1180
|
const unmount = mountPromptBox(win.document, bridge, prompt === true ? {} : prompt);
|
|
1047
1181
|
const destroy = bridge.destroy;
|
|
@@ -1058,4 +1192,5 @@ const getContentBridge = ({ url, prompt = true } = {})=>{
|
|
|
1058
1192
|
exports.DEFAULT_PROMPT_LABELS = DEFAULT_PROMPT_LABELS;
|
|
1059
1193
|
exports.INITIAL_STATE = INITIAL_STATE;
|
|
1060
1194
|
exports.getContentBridge = getContentBridge;
|
|
1195
|
+
exports.mountPromptBox = mountPromptBox;
|
|
1061
1196
|
exports.peekContentBridge = peekContentBridge;
|