@collidecreatives/edit-mode 0.1.0 → 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 CHANGED
@@ -56,6 +56,8 @@ initClientEditMode({
56
56
  queryParam: "edit",
57
57
  queryValue: "true",
58
58
  sessionKey: "collide-edit-mode",
59
+ storageKey: "collide-edit-mode:draft",
60
+ autoSave: true,
59
61
  brandName: "Edit Mode",
60
62
  accentColour: "#1e40af",
61
63
  editableSelector: "h1,h2,h3,h4,h5,h6,p,li,blockquote,figcaption,label,legend,dt,dd,th,td,a,button",
@@ -64,6 +66,16 @@ initClientEditMode({
64
66
  });
65
67
  ```
66
68
 
69
+ ## Navigation while editing
70
+
71
+ Internal links are rewritten to keep `?edit=true` when clients move around the site.
72
+ Link text is editable. To visit a link while edit mode is on:
73
+
74
+ - select the link and use **Open link** in the panel, or
75
+ - `Ctrl`/`⌘`-click the link.
76
+
77
+ Drafts auto-save before navigation, on each edit, and when the page is hidden.
78
+
67
79
  ## Opt out in templates
68
80
 
69
81
  ```html
@@ -1,3 +1,18 @@
1
- "use strict";var CollideEditMode=(()=>{var g=Object.defineProperty;var E=Object.getOwnPropertyDescriptor;var x=Object.getOwnPropertyNames;var C=Object.prototype.hasOwnProperty;var w=(t,e)=>{for(var n in e)g(t,n,{get:e[n],enumerable:!0})},S=(t,e,n,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of x(e))!C.call(t,o)&&o!==n&&g(t,o,{get:()=>e[o],enumerable:!(i=E(e,o))||i.enumerable});return t};var M=t=>S(g({},"__esModule",{value:!0}),t);var D={};w(D,{initClientEditMode:()=>m});var v="h1,h2,h3,h4,h5,h6,p,li,blockquote,figcaption,label,legend,dt,dd,th,td,a,button",L="[data-no-edit],[data-edit-mode-skip],form,input,textarea,select,option,script,style,svg,canvas,iframe,.cem-edit-banner,.cem-copy-btn,.cem-toast",k=new Set(["IMG","PICTURE","SVG","VIDEO","CANVAS","IFRAME","SCRIPT","STYLE"]),I={queryParam:"edit",queryValue:"true",sessionKey:"collide-edit-mode",brandName:"Edit Mode",accentColour:"#1e40af",editableSelector:v,skipSelector:L};function T(){return typeof window<"u"&&typeof document<"u"}function O(t){if(t.enabled!==void 0)return t.enabled;let n=new URL(window.location.href).searchParams.get(t.queryParam);return(t.queryValue===null?n!==null:n===t.queryValue)||window.sessionStorage.getItem(t.sessionKey)==="1"}function q(t){let e=document.createElement("style");return e.dataset.editModeStyle="true",e.textContent=[`.cem-edit-banner{position:fixed;top:0;left:0;right:0;z-index:2147483640;background:${t};color:#fff;text-align:center;padding:7px 56px 7px 16px;font-size:13px;font-family:system-ui,-apple-system,sans-serif;line-height:1.4}`,".cem-edit-banner kbd{background:rgba(255,255,255,0.15);padding:0 4px;border-radius:3px;font-size:11px}",".cem-exit-btn{position:absolute;right:8px;top:50%;transform:translateY(-50%);background:rgba(255,255,255,0.15);border:1px solid rgba(255,255,255,0.25);color:#fff;padding:3px 10px;border-radius:4px;font-size:12px;cursor:pointer;font-family:inherit;white-space:nowrap}",".cem-exit-btn:hover{background:rgba(255,255,255,0.25)}","[contenteditable=true]{cursor:text!important}","[contenteditable=true]:hover{outline:2px dashed rgba(59,130,246,0.5)!important;outline-offset:2px}","[contenteditable=true]:focus{outline:2px solid #3b82f6!important;outline-offset:2px;background:rgba(59,130,246,0.04)}",`.cem-copy-btn{position:fixed;bottom:80px;right:16px;z-index:2147483640;background:${t};color:#fff;border:none;border-radius:8px;padding:10px 18px;font-size:14px;font-weight:600;font-family:system-ui,-apple-system,sans-serif;cursor:pointer;box-shadow:0 2px 12px rgba(0,0,0,0.3);transition:transform .15s,opacity .15s;white-space:nowrap}`,".cem-copy-btn:hover{transform:scale(1.05)}",".cem-copy-btn:active{transform:scale(.97)}",".cem-toast{position:fixed;bottom:132px;right:16px;z-index:2147483641;background:#065f46;color:#fff;padding:10px 18px;border-radius:8px;font-size:14px;font-family:system-ui,-apple-system,sans-serif;box-shadow:0 2px 12px rgba(0,0,0,0.3);transition:opacity .3s}","@media(min-width:768px){.cem-copy-btn{bottom:24px;right:24px}.cem-toast{bottom:76px;right:24px}}"].join(`
2
- `),document.head.appendChild(e),e}function y(t){document.querySelector(".cem-toast")?.remove();let e=document.createElement("div");e.className="cem-toast",e.textContent=t,document.body.appendChild(e),window.setTimeout(()=>{e.style.opacity="0",window.setTimeout(()=>e.parentNode?.removeChild(e),300)},2500)}function h(t,e){let n=document.createElement("textarea");n.value=t,n.style.position="fixed",n.style.left="-9999px",document.body.appendChild(n),n.select();try{document.execCommand("copy")}catch{}document.body.removeChild(n),y(e)}function P(t,e){if(t.closest(e)||!(t.textContent?.trim()??""))return!1;let i=Array.from(t.children);return!(i.length>0&&i.every(o=>k.has(o.tagName)))}function A(t){let e="",n=t.parentElement;for(;n&&n!==document.body&&n!==document.documentElement;){if(n.id){e=`#${n.id}`;break}let s=n.getAttribute("data-section");if(s){e=s;break}if(n.tagName==="SECTION"){let r=n.querySelector("h1,h2,h3");if(r?.textContent){e=r.textContent.trim().slice(0,40);break}}if(["HEADER","FOOTER","MAIN","NAV"].includes(n.tagName)){e=n.tagName.toLowerCase();break}n=n.parentElement}let i=t.tagName.toLowerCase(),o=t.dataset.editOriginal||t.textContent?.trim()||"",l=o.slice(0,40)+(o.length>40?"...":"");return`${e?`${e} > `:""}${i}: "${l}"`}function N(t,e){document.querySelectorAll("a[href]").forEach(n=>{let i=n.getAttribute("href");if(!(!i||/^(https?:|mailto:|tel:|javascript:)/i.test(i)))try{let o=new URL(i,window.location.href);if(o.origin!==window.location.origin)return;o.searchParams.set(t,e??"1");let l=o.hash;o.hash="",n.setAttribute("href",`${o.pathname}${o.search}${l}`)}catch{}})}function m(t={}){if(!T())return{active:!1,getChanges:()=>[],destroy:()=>{}};let e={...I,...t};if(!O(e))return{active:!1,getChanges:()=>[],destroy:()=>{}};if(window.__COLLIDE_EDIT_MODE_ACTIVE)return{active:!0,getChanges:()=>u(e.editableSelector),destroy:()=>{}};window.__COLLIDE_EDIT_MODE_ACTIVE=!0,window.sessionStorage.setItem(e.sessionKey,"1");let n=q(e.accentColour),i=document.createElement("div");i.className="cem-edit-banner",i.innerHTML=`\u270F\uFE0F <strong>${e.brandName}</strong> \u2014 Click any text to edit. Press <kbd>Esc</kbd> to finish editing.`;let o=document.createElement("button");o.className="cem-exit-btn",o.textContent="\u2715 Exit",o.addEventListener("click",()=>{window.sessionStorage.removeItem(e.sessionKey);let a=new URL(window.location.href);a.searchParams.delete(e.queryParam),window.location.href=a.toString()}),i.appendChild(o),document.body.prepend(i),document.querySelectorAll("[data-reveal]").forEach(a=>a.classList.add("is-visible")),N(e.queryParam,e.queryValue),document.querySelectorAll(e.editableSelector).forEach(a=>{if(!P(a,e.skipSelector))return;let d=a.textContent?.trim()??"";a.contentEditable="true",a.dataset.editOriginal||(a.dataset.editOriginal=d)});let s=a=>{let d=a.target;if(!(d instanceof Element))return;let c=d.closest("button");c&&!c.classList.contains("cem-copy-btn")&&!c.closest(".cem-edit-banner")&&(a.preventDefault(),a.stopImmediatePropagation())};document.addEventListener("click",s,!0);let r=document.createElement("button");r.className="cem-copy-btn",document.body.appendChild(r);let p=()=>{let a=u(e.editableSelector).length;r.textContent=a>0?`\u{1F4CB} Copy Changes (${a})`:"\u{1F4CB} Copy Changes"};return document.addEventListener("input",p),p(),r.addEventListener("click",()=>{let a=u(e.editableSelector),d={site:window.location.hostname,page:window.location.pathname,pageTitle:document.title,url:window.location.href,timestamp:new Date().toISOString(),changes:a};t.onCopy?.(d);let c=t.mapPayload?t.mapPayload(d):d,f=JSON.stringify(c,null,2),b=`\u2713 Copied ${a.length} change${a.length!==1?"s":""} to clipboard`;navigator.clipboard?.writeText?navigator.clipboard.writeText(f).then(()=>y(b)).catch(()=>h(f,b)):h(f,b)}),{active:!0,getChanges:()=>u(e.editableSelector),destroy:()=>{document.removeEventListener("click",s,!0),document.removeEventListener("input",p),i.remove(),r.remove(),n.remove(),document.querySelectorAll(e.editableSelector).forEach(a=>{a.dataset.editOriginal!==void 0&&(a.contentEditable="false",delete a.dataset.editOriginal)}),window.__COLLIDE_EDIT_MODE_ACTIVE=!1}}}function u(t){let e=[];return document.querySelectorAll(t).forEach(n=>{if(!n.isContentEditable&&n.contentEditable!=="true")return;let i=n.dataset.editOriginal;if(i===void 0)return;let o=n.textContent?.trim()??"";i!==o&&e.push({path:A(n),tag:n.tagName,original:i,new:o})}),e}var _={initClientEditMode:m};if(typeof window<"u"){window.CollideEditMode=_;let t=document.currentScript,e={};t?.dataset.brandName&&(e.brandName=t.dataset.brandName),t?.dataset.queryParam&&(e.queryParam=t.dataset.queryParam),t?.dataset.queryValue&&(e.queryValue=t.dataset.queryValue),t?.dataset.sessionKey&&(e.sessionKey=t.dataset.sessionKey),t?.dataset.accentColour&&(e.accentColour=t.dataset.accentColour),t?.dataset.editableSelector&&(e.editableSelector=t.dataset.editableSelector),t?.dataset.skipSelector&&(e.skipSelector=t.dataset.skipSelector),m(e)}return M(D);})();
1
+ "use strict";var CollideEditMode=(()=>{var k=Object.defineProperty;var G=Object.getOwnPropertyDescriptor;var J=Object.getOwnPropertyNames;var Y=Object.prototype.hasOwnProperty;var Q=(t,e)=>{for(var n in e)k(t,n,{get:e[n],enumerable:!0})},W=(t,e,n,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of J(e))!Y.call(t,i)&&i!==n&&k(t,i,{get:()=>e[i],enumerable:!(a=G(e,i))||a.enumerable});return t};var X=t=>W(k({},"__esModule",{value:!0}),t);var Ce={};Q(Ce,{initClientEditMode:()=>E});var Z="h1,h2,h3,h4,h5,h6,p,li,blockquote,figcaption,label,legend,dt,dd,th,td,a,button",ee="[data-no-edit],[data-edit-mode-skip],form,input,textarea,select,option,script,style,svg,canvas,iframe,.cem-edit-banner,.cem-panel,.cem-toast",te=new Set(["IMG","PICTURE","SVG","VIDEO","CANVAS","IFRAME","SCRIPT","STYLE"]),ne=new Set(["H1","H2","H3","H4","H5","H6","BUTTON","LABEL","LEGEND","DT","TH","TD","FIGCAPTION","A"]),V=1,oe=400,ie={queryParam:"edit",queryValue:"true",sessionKey:"collide-edit-mode",brandName:"Edit Mode",accentColour:"#1e40af",editableSelector:Z,skipSelector:ee,autoSave:!0};function ae(){return typeof window<"u"&&typeof document<"u"}function re(t){if(t.enabled!==void 0)return t.enabled;let n=new URL(window.location.href).searchParams.get(t.queryParam);return(t.queryValue===null?n!==null:n===t.queryValue)||window.sessionStorage.getItem(t.sessionKey)==="1"}function se(t){let e=document.createElement("style");return e.dataset.editModeStyle="true",e.textContent=[`.cem-edit-banner{position:fixed;top:0;left:0;right:0;z-index:2147483640;background:${t};color:#fff;display:flex;align-items:center;justify-content:center;gap:8px;padding:8px 92px 8px 16px;font:13px/1.4 system-ui,-apple-system,sans-serif;box-shadow:0 1px 8px rgba(0,0,0,.18)}`,".cem-edit-banner kbd{background:rgba(255,255,255,.18);padding:0 4px;border-radius:3px;font-size:11px}",".cem-exit-btn{position:absolute;right:8px;top:50%;transform:translateY(-50%);background:rgba(255,255,255,.15);border:1px solid rgba(255,255,255,.25);color:#fff;padding:4px 10px;border-radius:5px;font:12px system-ui,-apple-system,sans-serif;cursor:pointer;white-space:nowrap}",".cem-exit-btn:hover,.cem-exit-btn:focus-visible{background:rgba(255,255,255,.25);outline:none}","[contenteditable=true]{cursor:text!important}","[contenteditable=true]:hover{outline:2px dashed rgba(59,130,246,.5)!important;outline-offset:2px}","[contenteditable=true]:focus{outline:2px solid #3b82f6!important;outline-offset:2px;background:rgba(59,130,246,.04)}","[contenteditable=true].cem-changed{box-shadow:inset 3px 0 0 #10b981}",".cem-panel{position:fixed;right:16px;bottom:16px;z-index:2147483640;background:#fff;color:#111827;border:1px solid #e5e7eb;border-radius:12px;box-shadow:0 8px 30px rgba(0,0,0,.22);padding:10px;display:grid;gap:8px;width:min(320px,calc(100vw - 32px));font:13px/1.4 system-ui,-apple-system,sans-serif}",".cem-panel-actions{display:flex;gap:8px;flex-wrap:wrap}",".cem-copy-btn,.cem-review-btn,.cem-download-btn,.cem-open-link-btn{border:0;border-radius:8px;padding:9px 12px;font:600 13px system-ui,-apple-system,sans-serif;cursor:pointer;white-space:nowrap}",`.cem-copy-btn{background:${t};color:#fff;flex:1}`,".cem-review-btn,.cem-download-btn,.cem-open-link-btn{background:#f3f4f6;color:#111827;border:1px solid #e5e7eb}",".cem-open-link-btn[hidden]{display:none}",".cem-copy-btn:hover,.cem-review-btn:hover,.cem-download-btn:hover,.cem-open-link-btn:hover{filter:brightness(.97)}",".cem-status{color:#4b5563;font-size:12px;min-height:17px}",".cem-review-list{max-height:220px;overflow-y:auto;display:grid;gap:6px;border-top:1px solid #e5e7eb;padding-top:8px}",".cem-review-list[hidden]{display:none}",".cem-review-empty{color:#6b7280;font-size:12px;margin:0;padding:4px 0}",".cem-review-item{display:grid;gap:4px;padding:8px;background:#f9fafb;border-radius:8px;border:1px solid #e5e7eb}",".cem-review-path{font-size:11px;color:#6b7280;font-weight:600;text-transform:uppercase;letter-spacing:.02em}",".cem-review-diff{font-size:12px;word-break:break-word}",".cem-review-diff del{color:#b91c1c;text-decoration:line-through;opacity:.75;display:block}",".cem-review-diff ins{color:#065f46;text-decoration:none;display:block}",".cem-revert-btn{justify-self:start;background:transparent;border:1px solid #e5e7eb;border-radius:6px;padding:3px 8px;font:12px system-ui,-apple-system,sans-serif;cursor:pointer;color:#374151}",".cem-revert-btn:hover{background:#fff}",".cem-toast{position:fixed;bottom:116px;right:16px;z-index:2147483641;background:#065f46;color:#fff;padding:10px 18px;border-radius:8px;font:14px system-ui,-apple-system,sans-serif;box-shadow:0 2px 12px rgba(0,0,0,.3);transition:opacity .3s}","@media(max-width:520px){.cem-edit-banner{justify-content:flex-start;text-align:left}.cem-panel{left:16px;right:16px;width:auto}}"].join(`
2
+ `),document.head.appendChild(e),e}function D(t){document.querySelector(".cem-toast")?.remove();let e=document.createElement("div");e.className="cem-toast",e.textContent=t,document.body.appendChild(e),window.setTimeout(()=>{e.style.opacity="0",window.setTimeout(()=>e.parentNode?.removeChild(e),300)},2500)}function R(t,e){let n=document.createElement("textarea");n.value=t,n.style.position="fixed",n.style.left="-9999px",document.body.appendChild(n),n.select();try{document.execCommand("copy")}catch{}document.body.removeChild(n),D(e)}function de(){try{let t="__cem_storage_test__";return window.localStorage.setItem(t,"1"),window.localStorage.removeItem(t),window.localStorage}catch{try{return window.sessionStorage}catch{return null}}}function ce(t){return`${t.storageKey||`${t.sessionKey}:draft`}:${window.location.origin}${window.location.pathname}`}function T(t){return t.replace(/[&<>"']/g,e=>{switch(e){case"&":return"&amp;";case"<":return"&lt;";case">":return"&gt;";case'"':return"&quot;";default:return"&#39;"}})}function le(t,e){if(t.closest(e)||!(t.textContent?.trim()??""))return!1;let a=Array.from(t.children);return!(a.length>0&&a.every(i=>te.has(i.tagName)))}function ue(t){if(t.dataset.editId)return`data-edit-id:${t.dataset.editId}`;if(t.id)return`id:${t.id}`;let e=[],n=t;for(;n&&n!==document.body&&n!==document.documentElement;){let a=n.parentElement;if(!a)break;let i=n.tagName,s=Array.from(a.children).filter(c=>c.tagName===i).indexOf(n)+1;e.unshift(`${n.tagName.toLowerCase()}:nth-of-type(${s})`),n=a}return e.join(" > ")}function me(t){let e="",n=t.parentElement;for(;n&&n!==document.body&&n!==document.documentElement;){if(n.id){e=`#${n.id}`;break}let s=n.getAttribute("data-section");if(s){e=s;break}if(n.tagName==="SECTION"){let c=n.querySelector("h1,h2,h3");if(c?.textContent){e=c.textContent.trim().slice(0,40);break}}if(["HEADER","FOOTER","MAIN","NAV"].includes(n.tagName)){e=n.tagName.toLowerCase();break}n=n.parentElement}let a=t.tagName.toLowerCase(),i=t.dataset.editOriginal||t.textContent?.trim()||"",d=i.slice(0,40)+(i.length>40?"...":"");return`${e?`${e} > `:""}${a}: "${d}"`}function pe(t,e){document.querySelectorAll("a[href]").forEach(n=>{let a=n.getAttribute("href");if(!(!a||/^(https?:|mailto:|tel:|javascript:)/i.test(a)))try{let i=new URL(a,window.location.href);if(i.origin!==window.location.origin)return;i.searchParams.set(t,e??"1");let d=i.hash;i.hash="",n.setAttribute("href",`${i.pathname}${i.search}${d}`)}catch{}})}function B(t){return{site:window.location.hostname,page:window.location.pathname,pageTitle:document.title,url:window.location.href,timestamp:new Date().toISOString(),changes:t}}function fe(t,e){if(!t)return null;try{let n=t.getItem(e);if(!n)return null;let a=JSON.parse(n);return a?.version===V&&Array.isArray(a.changes)?a:null}catch{return null}}function ge(t,e,n){if(!t)return!1;try{return t.setItem(e,JSON.stringify(n)),!0}catch{return!1}}function be(t,e){let n=new Blob([e],{type:"application/json"}),a=URL.createObjectURL(n),i=document.createElement("a");i.href=a,i.download=t,i.style.display="none",document.body.appendChild(i),i.click(),i.remove(),window.setTimeout(()=>URL.revokeObjectURL(a),1e3)}function he(t=new Date){return t.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}function ye(t){document.querySelectorAll(t.editableSelector).forEach(e=>{if(!le(e,t.skipSelector)||e.closest("[data-edit-key]"))return;let n=e.textContent?.trim()??"";e.contentEditable="true",e.dataset.editOriginal||(e.dataset.editOriginal=n),e.dataset.editKey||(e.dataset.editKey=ue(e))})}function we(t,e){let n=document.querySelectorAll(t);for(let a of Array.from(n))if(a.dataset.editKey===e)return a;return null}function Ee(t,e){if(!t)return 0;let n=new Map(t.changes.map(i=>[i.key,i])),a=0;return document.querySelectorAll(e).forEach(i=>{let d=i.dataset.editKey;if(!d)return;let s=n.get(d);s&&i.dataset.editOriginal===s.original&&(i.textContent=s.new,a+=1)}),a}function xe(t){let e=t.dataset.editOriginal;if(e===void 0)return;let n=t.textContent?.trim()??"";t.classList.toggle("cem-changed",e!==n)}function U(t){let e=[];return document.querySelectorAll(t).forEach(n=>{if(!n.isContentEditable&&n.contentEditable!=="true")return;let a=n.dataset.editOriginal,i=n.dataset.editKey;if(a===void 0||!i)return;let d=n.textContent?.trim()??"",s=a!==d;n.classList.toggle("cem-changed",s),s&&e.push({key:i,path:me(n),tag:n.tagName,original:a,new:d})}),e}function M(t){return U(t).map(({key:e,...n})=>n)}function E(t={}){if(!ae())return{active:!1,getChanges:()=>[],destroy:()=>{}};let e={...ie,...t};if(!re(e))return{active:!1,getChanges:()=>[],destroy:()=>{}};if(window.__COLLIDE_EDIT_MODE_ACTIVE)return{active:!0,getChanges:()=>M(e.editableSelector),destroy:()=>{}};window.__COLLIDE_EDIT_MODE_ACTIVE=!0,window.sessionStorage.setItem(e.sessionKey,"1");let n=de(),a=ce(e),i=se(e.accentColour),d=document.createElement("div");d.className="cem-edit-banner",d.innerHTML=`\u270F\uFE0F <strong>${e.brandName}</strong> <span>Click text to edit. Press <kbd>Esc</kbd> when done.</span>`;let s=document.createElement("button");s.className="cem-exit-btn",s.type="button",s.textContent="Exit",d.appendChild(s),document.body.prepend(d);let c=document.createElement("div");c.className="cem-panel",c.innerHTML=`
3
+ <div class="cem-panel-actions">
4
+ <button class="cem-copy-btn" type="button">\u{1F4CB} Copy Changes</button>
5
+ <button class="cem-review-btn" type="button" aria-expanded="false">Review</button>
6
+ <button class="cem-download-btn" type="button">Download backup</button>
7
+ <button class="cem-open-link-btn" type="button" hidden>Open link</button>
8
+ </div>
9
+ <div class="cem-review-list" hidden></div>
10
+ <div class="cem-status" aria-live="polite">Auto-save ready</div>
11
+ `,document.body.appendChild(c);let x=c.querySelector(".cem-copy-btn"),v=c.querySelector(".cem-review-btn"),j=c.querySelector(".cem-download-btn"),y=c.querySelector(".cem-open-link-btn"),b=c.querySelector(".cem-review-list"),O=c.querySelector(".cem-status"),p=null,f=!1,w=[];document.querySelectorAll("[data-reveal]").forEach(o=>o.classList.add("is-visible")),pe(e.queryParam,e.queryValue),ye(e);let C=Ee(fe(n,a),e.editableSelector),z=()=>({version:V,page:window.location.pathname,url:window.location.href,updatedAt:new Date().toISOString(),changes:U(e.editableSelector)}),A=()=>{if(b){if(w.length===0){b.innerHTML='<p class="cem-review-empty">No changes yet.</p>';return}b.innerHTML=w.map((o,r)=>`
12
+ <div class="cem-review-item">
13
+ <div class="cem-review-path">${T(o.path)}</div>
14
+ <div class="cem-review-diff"><del>${T(o.original)}</del><ins>${T(o.new)}</ins></div>
15
+ <button class="cem-revert-btn" type="button" data-index="${r}">Revert</button>
16
+ </div>
17
+ `).join("")}},I=(o,r)=>{x&&(x.textContent=o>0?`\u{1F4CB} Copy Changes (${o})`:"\u{1F4CB} Copy Changes"),y&&(y.hidden=!p,y.textContent=p?`Open ${p.hostname||"link"}`:"Open link"),O&&(O.textContent=r||`Auto-saved ${he()} \u2022 ${o} change${o===1?"":"s"} \u2022 Links: edit text or Ctrl/\u2318-click to open`)},L=o=>{I(M(e.editableSelector).length,o)},S=()=>{let o=z();w=o.changes;let r=e.autoSave!==!1&&ge(n,a,o);return I(o.changes.length,r||e.autoSave===!1?void 0:"\u26A0\uFE0F Auto-save unavailable \u2014 use Download backup"),f&&A(),o},g=null,m=()=>(g!==null&&(window.clearTimeout(g),g=null),S()),F=()=>{g!==null&&window.clearTimeout(g),g=window.setTimeout(()=>{g=null,S()},oe)},H=()=>{let o=document.activeElement;return!(o instanceof HTMLElement)||!o.isContentEditable&&o.contentEditable!=="true"?!1:(o.blur(),window.getSelection()?.removeAllRanges(),m(),!0)},N=o=>{if(o.key==="Escape"){if(!H())return;o.preventDefault(),o.stopPropagation();return}if(o.key==="Enter"&&!o.shiftKey){let r=o.target;r instanceof HTMLElement&&ne.has(r.tagName)&&(r.isContentEditable||r.contentEditable==="true")&&(o.preventDefault(),H())}},q=o=>{let r=o.target;if(!(r instanceof Element))return;let l=r.closest("a[href]");if(l&&!l.closest(".cem-panel")&&!l.closest(".cem-edit-banner")){if(p=l,L("Link selected \u2014 edit the text, use Open link, or Ctrl/\u2318-click to visit it"),o.metaKey||o.ctrlKey||o.altKey){m();return}o.preventDefault();return}let u=r.closest("button");u&&!u.closest(".cem-panel")&&!u.closest(".cem-edit-banner")&&(o.preventDefault(),o.stopImmediatePropagation())},$=o=>{let r=o.target;r instanceof Element&&(p=r.closest("a[href]"),L(p?"Link selected \u2014 edit the text or use Open link to navigate":void 0))},_=o=>{o.target instanceof HTMLElement&&xe(o.target),F()},K=()=>m(),P=()=>{document.visibilityState==="hidden"&&m()};return document.addEventListener("keydown",N),document.addEventListener("click",q,!0),document.addEventListener("focusin",$),document.addEventListener("input",_),window.addEventListener("pagehide",K),document.addEventListener("visibilitychange",P),v?.addEventListener("click",()=>{f=!f,b&&(b.hidden=!f),v.textContent=f?"Hide review":"Review",v.setAttribute("aria-expanded",String(f)),f&&A()}),b?.addEventListener("click",o=>{let r=o.target;if(!(r instanceof HTMLElement))return;let l=r.closest(".cem-revert-btn");if(!l)return;let u=w[Number(l.dataset.index)];if(!u)return;let h=we(e.editableSelector,u.key);h&&(h.textContent=u.original),m()}),x?.addEventListener("click",()=>{let o=m(),r=B(o.changes);t.onCopy?.(r);let l=t.mapPayload?t.mapPayload(r):r,u=JSON.stringify(l,null,2),h=`\u2713 Copied ${o.changes.length} change${o.changes.length!==1?"s":""} to clipboard`;navigator.clipboard?.writeText?navigator.clipboard.writeText(u).then(()=>D(h)).catch(()=>R(u,h)):R(u,h)}),j?.addEventListener("click",()=>{let o=m(),r=B(o.changes),l=new Date().toISOString().slice(0,10);be(`edit-mode-${window.location.hostname}-${l}.json`,JSON.stringify(r,null,2)),D("\u2713 Backup downloaded")}),y?.addEventListener("click",()=>{p?.href&&(m(),window.location.href=p.href)}),s.addEventListener("click",()=>{m(),window.sessionStorage.removeItem(e.sessionKey);let o=new URL(window.location.href);o.searchParams.delete(e.queryParam),window.location.href=o.toString()}),S(),C>0&&L(`Restored ${C} saved edit${C===1?"":"s"} \u2022 Auto-save on`),{active:!0,getChanges:()=>M(e.editableSelector),destroy:()=>{m(),document.removeEventListener("keydown",N),document.removeEventListener("click",q,!0),document.removeEventListener("focusin",$),document.removeEventListener("input",_),window.removeEventListener("pagehide",K),document.removeEventListener("visibilitychange",P),d.remove(),c.remove(),i.remove(),document.querySelectorAll(e.editableSelector).forEach(o=>{o.dataset.editOriginal!==void 0&&(o.contentEditable="false",o.classList.remove("cem-changed"),delete o.dataset.editOriginal,delete o.dataset.editKey)}),window.__COLLIDE_EDIT_MODE_ACTIVE=!1}}}var ve={initClientEditMode:E};if(typeof window<"u"){window.CollideEditMode=ve;let t=document.currentScript,e={};t?.dataset.brandName&&(e.brandName=t.dataset.brandName),t?.dataset.queryParam&&(e.queryParam=t.dataset.queryParam),t?.dataset.queryValue&&(e.queryValue=t.dataset.queryValue),t?.dataset.sessionKey&&(e.sessionKey=t.dataset.sessionKey),t?.dataset.accentColour&&(e.accentColour=t.dataset.accentColour),t?.dataset.editableSelector&&(e.editableSelector=t.dataset.editableSelector),t?.dataset.skipSelector&&(e.skipSelector=t.dataset.skipSelector),E(e)}return X(Ce);})();
3
18
  //# sourceMappingURL=browser.global.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/browser.ts","../src/index.ts"],"sourcesContent":["import { initClientEditMode } from \"./index\";\nimport type { EditModeInstance, EditModeOptions } from \"./types\";\n\nconst api = { initClientEditMode };\n\nif (typeof window !== \"undefined\") {\n window.CollideEditMode = api;\n\n const script = document.currentScript as HTMLScriptElement | null;\n const options: EditModeOptions = {};\n\n if (script?.dataset.brandName) options.brandName = script.dataset.brandName;\n if (script?.dataset.queryParam) options.queryParam = script.dataset.queryParam;\n if (script?.dataset.queryValue) options.queryValue = script.dataset.queryValue;\n if (script?.dataset.sessionKey) options.sessionKey = script.dataset.sessionKey;\n if (script?.dataset.accentColour) options.accentColour = script.dataset.accentColour;\n if (script?.dataset.editableSelector) options.editableSelector = script.dataset.editableSelector;\n if (script?.dataset.skipSelector) options.skipSelector = script.dataset.skipSelector;\n\n initClientEditMode(options);\n}\n\nexport { initClientEditMode };\nexport type { EditModeInstance, EditModeOptions };\n","import type { EditModeChange, EditModeInstance, EditModeOptions, EditModePayload } from \"./types\";\n\nexport type { EditModeChange, EditModeInstance, EditModeOptions, EditModePayload } from \"./types\";\n\nconst DEFAULT_EDITABLE_SELECTOR =\n \"h1,h2,h3,h4,h5,h6,p,li,blockquote,figcaption,label,legend,dt,dd,th,td,a,button\";\n\nconst DEFAULT_SKIP_SELECTOR =\n \"[data-no-edit],[data-edit-mode-skip],form,input,textarea,select,option,script,style,svg,canvas,iframe,.cem-edit-banner,.cem-copy-btn,.cem-toast\";\n\nconst SKIP_CHILD_TAGS = new Set([\"IMG\", \"PICTURE\", \"SVG\", \"VIDEO\", \"CANVAS\", \"IFRAME\", \"SCRIPT\", \"STYLE\"]);\n\nconst defaults = {\n queryParam: \"edit\",\n queryValue: \"true\" as string | null,\n sessionKey: \"collide-edit-mode\",\n brandName: \"Edit Mode\",\n accentColour: \"#1e40af\",\n editableSelector: DEFAULT_EDITABLE_SELECTOR,\n skipSelector: DEFAULT_SKIP_SELECTOR,\n};\n\nfunction hasDom() {\n return typeof window !== \"undefined\" && typeof document !== \"undefined\";\n}\n\nfunction shouldEnable(options: Required<Omit<EditModeOptions, \"enabled\" | \"onCopy\" | \"mapPayload\">> & Pick<EditModeOptions, \"enabled\">) {\n if (options.enabled !== undefined) return options.enabled;\n\n const url = new URL(window.location.href);\n const queryValue = url.searchParams.get(options.queryParam);\n const queryMatches =\n options.queryValue === null ? queryValue !== null : queryValue === options.queryValue;\n\n return queryMatches || window.sessionStorage.getItem(options.sessionKey) === \"1\";\n}\n\nfunction addStyles(accentColour: string) {\n const style = document.createElement(\"style\");\n style.dataset.editModeStyle = \"true\";\n style.textContent = [\n `.cem-edit-banner{position:fixed;top:0;left:0;right:0;z-index:2147483640;background:${accentColour};color:#fff;text-align:center;padding:7px 56px 7px 16px;font-size:13px;font-family:system-ui,-apple-system,sans-serif;line-height:1.4}`,\n \".cem-edit-banner kbd{background:rgba(255,255,255,0.15);padding:0 4px;border-radius:3px;font-size:11px}\",\n \".cem-exit-btn{position:absolute;right:8px;top:50%;transform:translateY(-50%);background:rgba(255,255,255,0.15);border:1px solid rgba(255,255,255,0.25);color:#fff;padding:3px 10px;border-radius:4px;font-size:12px;cursor:pointer;font-family:inherit;white-space:nowrap}\",\n \".cem-exit-btn:hover{background:rgba(255,255,255,0.25)}\",\n \"[contenteditable=true]{cursor:text!important}\",\n \"[contenteditable=true]:hover{outline:2px dashed rgba(59,130,246,0.5)!important;outline-offset:2px}\",\n \"[contenteditable=true]:focus{outline:2px solid #3b82f6!important;outline-offset:2px;background:rgba(59,130,246,0.04)}\",\n `.cem-copy-btn{position:fixed;bottom:80px;right:16px;z-index:2147483640;background:${accentColour};color:#fff;border:none;border-radius:8px;padding:10px 18px;font-size:14px;font-weight:600;font-family:system-ui,-apple-system,sans-serif;cursor:pointer;box-shadow:0 2px 12px rgba(0,0,0,0.3);transition:transform .15s,opacity .15s;white-space:nowrap}`,\n \".cem-copy-btn:hover{transform:scale(1.05)}\",\n \".cem-copy-btn:active{transform:scale(.97)}\",\n \".cem-toast{position:fixed;bottom:132px;right:16px;z-index:2147483641;background:#065f46;color:#fff;padding:10px 18px;border-radius:8px;font-size:14px;font-family:system-ui,-apple-system,sans-serif;box-shadow:0 2px 12px rgba(0,0,0,0.3);transition:opacity .3s}\",\n \"@media(min-width:768px){.cem-copy-btn{bottom:24px;right:24px}.cem-toast{bottom:76px;right:24px}}\",\n ].join(\"\\n\");\n document.head.appendChild(style);\n return style;\n}\n\nfunction showToast(message: string) {\n document.querySelector(\".cem-toast\")?.remove();\n const toast = document.createElement(\"div\");\n toast.className = \"cem-toast\";\n toast.textContent = message;\n document.body.appendChild(toast);\n window.setTimeout(() => {\n toast.style.opacity = \"0\";\n window.setTimeout(() => toast.parentNode?.removeChild(toast), 300);\n }, 2500);\n}\n\nfunction fallbackCopy(text: string, label: string) {\n const textarea = document.createElement(\"textarea\");\n textarea.value = text;\n textarea.style.position = \"fixed\";\n textarea.style.left = \"-9999px\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n document.execCommand(\"copy\");\n } catch {\n // Ignore: the JSON remains visible via devtools and caller onCopy still runs.\n }\n document.body.removeChild(textarea);\n showToast(label);\n}\n\nfunction isEditableElement(el: Element, skipSelector: string) {\n if (el.closest(skipSelector)) return false;\n\n const text = el.textContent?.trim() ?? \"\";\n if (!text) return false;\n\n const children = Array.from(el.children);\n if (children.length > 0 && children.every((child) => SKIP_CHILD_TAGS.has(child.tagName))) {\n return false;\n }\n\n return true;\n}\n\nfunction getEditPath(el: HTMLElement) {\n let context = \"\";\n let current = el.parentElement;\n\n while (current && current !== document.body && current !== document.documentElement) {\n if (current.id) {\n context = `#${current.id}`;\n break;\n }\n\n const section = current.getAttribute(\"data-section\");\n if (section) {\n context = section;\n break;\n }\n\n if (current.tagName === \"SECTION\") {\n const heading = current.querySelector(\"h1,h2,h3\");\n if (heading?.textContent) {\n context = heading.textContent.trim().slice(0, 40);\n break;\n }\n }\n\n if ([\"HEADER\", \"FOOTER\", \"MAIN\", \"NAV\"].includes(current.tagName)) {\n context = current.tagName.toLowerCase();\n break;\n }\n\n current = current.parentElement;\n }\n\n const tag = el.tagName.toLowerCase();\n const text = el.dataset.editOriginal || el.textContent?.trim() || \"\";\n const snippet = text.slice(0, 40) + (text.length > 40 ? \"...\" : \"\");\n return `${context ? `${context} > ` : \"\"}${tag}: \"${snippet}\"`;\n}\n\nfunction rewriteLinks(queryParam: string, queryValue: string | null) {\n document.querySelectorAll<HTMLAnchorElement>(\"a[href]\").forEach((anchor) => {\n const href = anchor.getAttribute(\"href\");\n if (!href || /^(https?:|mailto:|tel:|javascript:)/i.test(href)) return;\n\n try {\n const url = new URL(href, window.location.href);\n if (url.origin !== window.location.origin) return;\n\n url.searchParams.set(queryParam, queryValue ?? \"1\");\n const hash = url.hash;\n url.hash = \"\";\n anchor.setAttribute(\"href\", `${url.pathname}${url.search}${hash}`);\n } catch {\n // Leave unusual hrefs unchanged.\n }\n });\n}\n\nexport function initClientEditMode(options: EditModeOptions = {}): EditModeInstance {\n if (!hasDom()) {\n return { active: false, getChanges: () => [], destroy: () => undefined };\n }\n\n const config = { ...defaults, ...options };\n\n if (!shouldEnable(config)) {\n return { active: false, getChanges: () => [], destroy: () => undefined };\n }\n\n if (window.__COLLIDE_EDIT_MODE_ACTIVE) {\n return { active: true, getChanges: () => collectChanges(config.editableSelector), destroy: () => undefined };\n }\n\n window.__COLLIDE_EDIT_MODE_ACTIVE = true;\n window.sessionStorage.setItem(config.sessionKey, \"1\");\n\n const style = addStyles(config.accentColour);\n\n const banner = document.createElement(\"div\");\n banner.className = \"cem-edit-banner\";\n banner.innerHTML = `✏️ <strong>${config.brandName}</strong> — Click any text to edit. Press <kbd>Esc</kbd> to finish editing.`;\n\n const exitButton = document.createElement(\"button\");\n exitButton.className = \"cem-exit-btn\";\n exitButton.textContent = \"✕ Exit\";\n exitButton.addEventListener(\"click\", () => {\n window.sessionStorage.removeItem(config.sessionKey);\n const url = new URL(window.location.href);\n url.searchParams.delete(config.queryParam);\n window.location.href = url.toString();\n });\n banner.appendChild(exitButton);\n document.body.prepend(banner);\n\n document.querySelectorAll(\"[data-reveal]\").forEach((el) => el.classList.add(\"is-visible\"));\n rewriteLinks(config.queryParam, config.queryValue);\n\n const initEditable = () => {\n document.querySelectorAll<HTMLElement>(config.editableSelector).forEach((el) => {\n if (!isEditableElement(el, config.skipSelector)) return;\n const text = el.textContent?.trim() ?? \"\";\n el.contentEditable = \"true\";\n if (!el.dataset.editOriginal) el.dataset.editOriginal = text;\n });\n };\n initEditable();\n\n const blockButtonClicks = (event: MouseEvent) => {\n const target = event.target;\n if (!(target instanceof Element)) return;\n const button = target.closest(\"button\");\n if (button && !button.classList.contains(\"cem-copy-btn\") && !button.closest(\".cem-edit-banner\")) {\n event.preventDefault();\n event.stopImmediatePropagation();\n }\n };\n document.addEventListener(\"click\", blockButtonClicks, true);\n\n const copyButton = document.createElement(\"button\");\n copyButton.className = \"cem-copy-btn\";\n document.body.appendChild(copyButton);\n\n const updateCounter = () => {\n const count = collectChanges(config.editableSelector).length;\n copyButton.textContent = count > 0 ? `📋 Copy Changes (${count})` : \"📋 Copy Changes\";\n };\n\n document.addEventListener(\"input\", updateCounter);\n updateCounter();\n\n copyButton.addEventListener(\"click\", () => {\n const changes = collectChanges(config.editableSelector);\n const payload: EditModePayload = {\n site: window.location.hostname,\n page: window.location.pathname,\n pageTitle: document.title,\n url: window.location.href,\n timestamp: new Date().toISOString(),\n changes,\n };\n\n options.onCopy?.(payload);\n\n const copyPayload = options.mapPayload ? options.mapPayload(payload) : payload;\n const json = JSON.stringify(copyPayload, null, 2);\n const label = `✓ Copied ${changes.length} change${changes.length !== 1 ? \"s\" : \"\"} to clipboard`;\n\n if (navigator.clipboard?.writeText) {\n navigator.clipboard.writeText(json).then(() => showToast(label)).catch(() => fallbackCopy(json, label));\n } else {\n fallbackCopy(json, label);\n }\n });\n\n return {\n active: true,\n getChanges: () => collectChanges(config.editableSelector),\n destroy: () => {\n document.removeEventListener(\"click\", blockButtonClicks, true);\n document.removeEventListener(\"input\", updateCounter);\n banner.remove();\n copyButton.remove();\n style.remove();\n document.querySelectorAll<HTMLElement>(config.editableSelector).forEach((el) => {\n if (el.dataset.editOriginal !== undefined) {\n el.contentEditable = \"false\";\n delete el.dataset.editOriginal;\n }\n });\n window.__COLLIDE_EDIT_MODE_ACTIVE = false;\n },\n };\n}\n\nfunction collectChanges(editableSelector: string): EditModeChange[] {\n const changes: EditModeChange[] = [];\n\n document.querySelectorAll<HTMLElement>(editableSelector).forEach((el) => {\n if (!el.isContentEditable && el.contentEditable !== \"true\") return;\n const original = el.dataset.editOriginal;\n if (original === undefined) return;\n\n const current = el.textContent?.trim() ?? \"\";\n if (original !== current) {\n changes.push({\n path: getEditPath(el),\n tag: el.tagName,\n original,\n new: current,\n });\n }\n });\n\n return changes;\n}\n"],"mappings":"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","names":["browser_exports","__export","initClientEditMode","DEFAULT_EDITABLE_SELECTOR","DEFAULT_SKIP_SELECTOR","SKIP_CHILD_TAGS","defaults","hasDom","shouldEnable","options","queryValue","addStyles","accentColour","style","showToast","message","toast","fallbackCopy","text","label","textarea","isEditableElement","el","skipSelector","children","child","getEditPath","context","current","section","heading","tag","snippet","rewriteLinks","queryParam","anchor","href","url","hash","initClientEditMode","config","collectChanges","banner","exitButton","blockButtonClicks","event","target","button","copyButton","updateCounter","count","changes","payload","copyPayload","json","editableSelector","original","api","initClientEditMode","script","options"]}
1
+ {"version":3,"sources":["../src/browser.ts","../src/index.ts"],"sourcesContent":["import { initClientEditMode } from \"./index\";\nimport type { EditModeInstance, EditModeOptions } from \"./types\";\n\nconst api = { initClientEditMode };\n\nif (typeof window !== \"undefined\") {\n window.CollideEditMode = api;\n\n const script = document.currentScript as HTMLScriptElement | null;\n const options: EditModeOptions = {};\n\n if (script?.dataset.brandName) options.brandName = script.dataset.brandName;\n if (script?.dataset.queryParam) options.queryParam = script.dataset.queryParam;\n if (script?.dataset.queryValue) options.queryValue = script.dataset.queryValue;\n if (script?.dataset.sessionKey) options.sessionKey = script.dataset.sessionKey;\n if (script?.dataset.accentColour) options.accentColour = script.dataset.accentColour;\n if (script?.dataset.editableSelector) options.editableSelector = script.dataset.editableSelector;\n if (script?.dataset.skipSelector) options.skipSelector = script.dataset.skipSelector;\n\n initClientEditMode(options);\n}\n\nexport { initClientEditMode };\nexport type { EditModeInstance, EditModeOptions };\n","import type { EditModeChange, EditModeInstance, EditModeOptions, EditModePayload } from \"./types\";\n\nexport type { EditModeChange, EditModeInstance, EditModeOptions, EditModePayload } from \"./types\";\n\nconst DEFAULT_EDITABLE_SELECTOR =\n \"h1,h2,h3,h4,h5,h6,p,li,blockquote,figcaption,label,legend,dt,dd,th,td,a,button\";\n\nconst DEFAULT_SKIP_SELECTOR =\n \"[data-no-edit],[data-edit-mode-skip],form,input,textarea,select,option,script,style,svg,canvas,iframe,.cem-edit-banner,.cem-panel,.cem-toast\";\n\nconst SKIP_CHILD_TAGS = new Set([\"IMG\", \"PICTURE\", \"SVG\", \"VIDEO\", \"CANVAS\", \"IFRAME\", \"SCRIPT\", \"STYLE\"]);\nconst SINGLE_LINE_TAGS = new Set([\n \"H1\",\n \"H2\",\n \"H3\",\n \"H4\",\n \"H5\",\n \"H6\",\n \"BUTTON\",\n \"LABEL\",\n \"LEGEND\",\n \"DT\",\n \"TH\",\n \"TD\",\n \"FIGCAPTION\",\n \"A\",\n]);\nconst DRAFT_VERSION = 1;\nconst AUTOSAVE_DEBOUNCE_MS = 400;\n\ntype Config = Required<\n Omit<EditModeOptions, \"enabled\" | \"onCopy\" | \"mapPayload\" | \"storageKey\" | \"autoSave\">\n> &\n Pick<EditModeOptions, \"enabled\" | \"storageKey\" | \"autoSave\">;\n\ntype DraftChange = EditModeChange & {\n key: string;\n};\n\ntype Draft = {\n version: number;\n page: string;\n url: string;\n updatedAt: string;\n changes: DraftChange[];\n};\n\nconst defaults = {\n queryParam: \"edit\",\n queryValue: \"true\" as string | null,\n sessionKey: \"collide-edit-mode\",\n brandName: \"Edit Mode\",\n accentColour: \"#1e40af\",\n editableSelector: DEFAULT_EDITABLE_SELECTOR,\n skipSelector: DEFAULT_SKIP_SELECTOR,\n autoSave: true,\n};\n\nfunction hasDom() {\n return typeof window !== \"undefined\" && typeof document !== \"undefined\";\n}\n\nfunction shouldEnable(options: Config) {\n if (options.enabled !== undefined) return options.enabled;\n\n const url = new URL(window.location.href);\n const queryValue = url.searchParams.get(options.queryParam);\n const queryMatches =\n options.queryValue === null ? queryValue !== null : queryValue === options.queryValue;\n\n return queryMatches || window.sessionStorage.getItem(options.sessionKey) === \"1\";\n}\n\nfunction addStyles(accentColour: string) {\n const style = document.createElement(\"style\");\n style.dataset.editModeStyle = \"true\";\n style.textContent = [\n `.cem-edit-banner{position:fixed;top:0;left:0;right:0;z-index:2147483640;background:${accentColour};color:#fff;display:flex;align-items:center;justify-content:center;gap:8px;padding:8px 92px 8px 16px;font:13px/1.4 system-ui,-apple-system,sans-serif;box-shadow:0 1px 8px rgba(0,0,0,.18)}`,\n \".cem-edit-banner kbd{background:rgba(255,255,255,.18);padding:0 4px;border-radius:3px;font-size:11px}\",\n \".cem-exit-btn{position:absolute;right:8px;top:50%;transform:translateY(-50%);background:rgba(255,255,255,.15);border:1px solid rgba(255,255,255,.25);color:#fff;padding:4px 10px;border-radius:5px;font:12px system-ui,-apple-system,sans-serif;cursor:pointer;white-space:nowrap}\",\n \".cem-exit-btn:hover,.cem-exit-btn:focus-visible{background:rgba(255,255,255,.25);outline:none}\",\n \"[contenteditable=true]{cursor:text!important}\",\n \"[contenteditable=true]:hover{outline:2px dashed rgba(59,130,246,.5)!important;outline-offset:2px}\",\n \"[contenteditable=true]:focus{outline:2px solid #3b82f6!important;outline-offset:2px;background:rgba(59,130,246,.04)}\",\n \"[contenteditable=true].cem-changed{box-shadow:inset 3px 0 0 #10b981}\",\n `.cem-panel{position:fixed;right:16px;bottom:16px;z-index:2147483640;background:#fff;color:#111827;border:1px solid #e5e7eb;border-radius:12px;box-shadow:0 8px 30px rgba(0,0,0,.22);padding:10px;display:grid;gap:8px;width:min(320px,calc(100vw - 32px));font:13px/1.4 system-ui,-apple-system,sans-serif}`,\n \".cem-panel-actions{display:flex;gap:8px;flex-wrap:wrap}\",\n `.cem-copy-btn,.cem-review-btn,.cem-download-btn,.cem-open-link-btn{border:0;border-radius:8px;padding:9px 12px;font:600 13px system-ui,-apple-system,sans-serif;cursor:pointer;white-space:nowrap}`,\n `.cem-copy-btn{background:${accentColour};color:#fff;flex:1}`,\n \".cem-review-btn,.cem-download-btn,.cem-open-link-btn{background:#f3f4f6;color:#111827;border:1px solid #e5e7eb}\",\n \".cem-open-link-btn[hidden]{display:none}\",\n \".cem-copy-btn:hover,.cem-review-btn:hover,.cem-download-btn:hover,.cem-open-link-btn:hover{filter:brightness(.97)}\",\n \".cem-status{color:#4b5563;font-size:12px;min-height:17px}\",\n \".cem-review-list{max-height:220px;overflow-y:auto;display:grid;gap:6px;border-top:1px solid #e5e7eb;padding-top:8px}\",\n \".cem-review-list[hidden]{display:none}\",\n \".cem-review-empty{color:#6b7280;font-size:12px;margin:0;padding:4px 0}\",\n \".cem-review-item{display:grid;gap:4px;padding:8px;background:#f9fafb;border-radius:8px;border:1px solid #e5e7eb}\",\n \".cem-review-path{font-size:11px;color:#6b7280;font-weight:600;text-transform:uppercase;letter-spacing:.02em}\",\n \".cem-review-diff{font-size:12px;word-break:break-word}\",\n \".cem-review-diff del{color:#b91c1c;text-decoration:line-through;opacity:.75;display:block}\",\n \".cem-review-diff ins{color:#065f46;text-decoration:none;display:block}\",\n \".cem-revert-btn{justify-self:start;background:transparent;border:1px solid #e5e7eb;border-radius:6px;padding:3px 8px;font:12px system-ui,-apple-system,sans-serif;cursor:pointer;color:#374151}\",\n \".cem-revert-btn:hover{background:#fff}\",\n \".cem-toast{position:fixed;bottom:116px;right:16px;z-index:2147483641;background:#065f46;color:#fff;padding:10px 18px;border-radius:8px;font:14px system-ui,-apple-system,sans-serif;box-shadow:0 2px 12px rgba(0,0,0,.3);transition:opacity .3s}\",\n \"@media(max-width:520px){.cem-edit-banner{justify-content:flex-start;text-align:left}.cem-panel{left:16px;right:16px;width:auto}}\",\n ].join(\"\\n\");\n document.head.appendChild(style);\n return style;\n}\n\nfunction showToast(message: string) {\n document.querySelector(\".cem-toast\")?.remove();\n const toast = document.createElement(\"div\");\n toast.className = \"cem-toast\";\n toast.textContent = message;\n document.body.appendChild(toast);\n window.setTimeout(() => {\n toast.style.opacity = \"0\";\n window.setTimeout(() => toast.parentNode?.removeChild(toast), 300);\n }, 2500);\n}\n\nfunction fallbackCopy(text: string, label: string) {\n const textarea = document.createElement(\"textarea\");\n textarea.value = text;\n textarea.style.position = \"fixed\";\n textarea.style.left = \"-9999px\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n document.execCommand(\"copy\");\n } catch {\n // Ignore: the download button and autosaved draft still protect the work.\n }\n document.body.removeChild(textarea);\n showToast(label);\n}\n\nfunction getStorage() {\n try {\n const testKey = \"__cem_storage_test__\";\n window.localStorage.setItem(testKey, \"1\");\n window.localStorage.removeItem(testKey);\n return window.localStorage;\n } catch {\n try {\n return window.sessionStorage;\n } catch {\n return null;\n }\n }\n}\n\nfunction getDraftKey(config: Config) {\n const base = config.storageKey || `${config.sessionKey}:draft`;\n return `${base}:${window.location.origin}${window.location.pathname}`;\n}\n\nfunction escapeHtml(text: string) {\n return text.replace(/[&<>\"']/g, (char) => {\n switch (char) {\n case \"&\":\n return \"&amp;\";\n case \"<\":\n return \"&lt;\";\n case \">\":\n return \"&gt;\";\n case '\"':\n return \"&quot;\";\n default:\n return \"&#39;\";\n }\n });\n}\n\nfunction isEditableElement(el: Element, skipSelector: string) {\n if (el.closest(skipSelector)) return false;\n\n const text = el.textContent?.trim() ?? \"\";\n if (!text) return false;\n\n const children = Array.from(el.children);\n if (children.length > 0 && children.every((child) => SKIP_CHILD_TAGS.has(child.tagName))) {\n return false;\n }\n\n return true;\n}\n\nfunction getElementKey(el: HTMLElement) {\n if (el.dataset.editId) return `data-edit-id:${el.dataset.editId}`;\n if (el.id) return `id:${el.id}`;\n\n const parts: string[] = [];\n let current: Element | null = el;\n\n while (current && current !== document.body && current !== document.documentElement) {\n const parentElement: HTMLElement | null = current.parentElement;\n if (!parentElement) break;\n\n const currentTag = current.tagName;\n const siblings = Array.from(parentElement.children).filter((child) => child.tagName === currentTag);\n const index = siblings.indexOf(current) + 1;\n parts.unshift(`${current.tagName.toLowerCase()}:nth-of-type(${index})`);\n current = parentElement;\n }\n\n return parts.join(\" > \");\n}\n\nfunction getEditPath(el: HTMLElement) {\n let context = \"\";\n let current = el.parentElement;\n\n while (current && current !== document.body && current !== document.documentElement) {\n if (current.id) {\n context = `#${current.id}`;\n break;\n }\n\n const section = current.getAttribute(\"data-section\");\n if (section) {\n context = section;\n break;\n }\n\n if (current.tagName === \"SECTION\") {\n const heading = current.querySelector(\"h1,h2,h3\");\n if (heading?.textContent) {\n context = heading.textContent.trim().slice(0, 40);\n break;\n }\n }\n\n if ([\"HEADER\", \"FOOTER\", \"MAIN\", \"NAV\"].includes(current.tagName)) {\n context = current.tagName.toLowerCase();\n break;\n }\n\n current = current.parentElement;\n }\n\n const tag = el.tagName.toLowerCase();\n const text = el.dataset.editOriginal || el.textContent?.trim() || \"\";\n const snippet = text.slice(0, 40) + (text.length > 40 ? \"...\" : \"\");\n return `${context ? `${context} > ` : \"\"}${tag}: \"${snippet}\"`;\n}\n\nfunction rewriteLinks(queryParam: string, queryValue: string | null) {\n document.querySelectorAll<HTMLAnchorElement>(\"a[href]\").forEach((anchor) => {\n const href = anchor.getAttribute(\"href\");\n if (!href || /^(https?:|mailto:|tel:|javascript:)/i.test(href)) return;\n\n try {\n const url = new URL(href, window.location.href);\n if (url.origin !== window.location.origin) return;\n\n url.searchParams.set(queryParam, queryValue ?? \"1\");\n const hash = url.hash;\n url.hash = \"\";\n anchor.setAttribute(\"href\", `${url.pathname}${url.search}${hash}`);\n } catch {\n // Leave unusual hrefs unchanged.\n }\n });\n}\n\nfunction buildPayload(changes: EditModeChange[]): EditModePayload {\n return {\n site: window.location.hostname,\n page: window.location.pathname,\n pageTitle: document.title,\n url: window.location.href,\n timestamp: new Date().toISOString(),\n changes,\n };\n}\n\nfunction loadDraft(storage: Storage | null, key: string): Draft | null {\n if (!storage) return null;\n\n try {\n const raw = storage.getItem(key);\n if (!raw) return null;\n const draft = JSON.parse(raw) as Draft;\n return draft?.version === DRAFT_VERSION && Array.isArray(draft.changes) ? draft : null;\n } catch {\n return null;\n }\n}\n\nfunction saveDraft(storage: Storage | null, key: string, draft: Draft) {\n if (!storage) return false;\n\n try {\n storage.setItem(key, JSON.stringify(draft));\n return true;\n } catch {\n return false;\n }\n}\n\nfunction downloadText(filename: string, text: string) {\n const blob = new Blob([text], { type: \"application/json\" });\n const url = URL.createObjectURL(blob);\n const anchor = document.createElement(\"a\");\n anchor.href = url;\n anchor.download = filename;\n anchor.style.display = \"none\";\n document.body.appendChild(anchor);\n anchor.click();\n anchor.remove();\n window.setTimeout(() => URL.revokeObjectURL(url), 1000);\n}\n\nfunction formatTime(date = new Date()) {\n return date.toLocaleTimeString([], { hour: \"2-digit\", minute: \"2-digit\" });\n}\n\nfunction initEditableElements(config: Config) {\n // querySelectorAll returns document order, so an ancestor is always visited\n // before its descendants — skipping already-marked ancestors here prevents\n // nested contenteditable regions (e.g. an <a> inside an editable <p>), which\n // would otherwise report the same edit twice.\n document.querySelectorAll<HTMLElement>(config.editableSelector).forEach((el) => {\n if (!isEditableElement(el, config.skipSelector)) return;\n if (el.closest(\"[data-edit-key]\")) return;\n\n const text = el.textContent?.trim() ?? \"\";\n el.contentEditable = \"true\";\n if (!el.dataset.editOriginal) el.dataset.editOriginal = text;\n if (!el.dataset.editKey) el.dataset.editKey = getElementKey(el);\n });\n}\n\nfunction findEditableByKey(editableSelector: string, key: string) {\n const elements = document.querySelectorAll<HTMLElement>(editableSelector);\n for (const el of Array.from(elements)) {\n if (el.dataset.editKey === key) return el;\n }\n return null;\n}\n\nfunction applyDraft(draft: Draft | null, editableSelector: string) {\n if (!draft) return 0;\n\n const edits = new Map(draft.changes.map((change) => [change.key, change]));\n let restored = 0;\n\n document.querySelectorAll<HTMLElement>(editableSelector).forEach((el) => {\n const key = el.dataset.editKey;\n if (!key) return;\n\n const saved = edits.get(key);\n if (!saved) return;\n\n // Avoid stomping over changed templates. Restore only when the original still matches.\n if (el.dataset.editOriginal !== saved.original) return;\n\n el.textContent = saved.new;\n restored += 1;\n });\n\n return restored;\n}\n\nfunction markChanged(el: HTMLElement) {\n const original = el.dataset.editOriginal;\n if (original === undefined) return;\n const current = el.textContent?.trim() ?? \"\";\n el.classList.toggle(\"cem-changed\", original !== current);\n}\n\nfunction collectDraftChanges(editableSelector: string): DraftChange[] {\n const changes: DraftChange[] = [];\n\n document.querySelectorAll<HTMLElement>(editableSelector).forEach((el) => {\n if (!el.isContentEditable && el.contentEditable !== \"true\") return;\n const original = el.dataset.editOriginal;\n const key = el.dataset.editKey;\n if (original === undefined || !key) return;\n\n const current = el.textContent?.trim() ?? \"\";\n const changed = original !== current;\n el.classList.toggle(\"cem-changed\", changed);\n\n if (changed) {\n changes.push({\n key,\n path: getEditPath(el),\n tag: el.tagName,\n original,\n new: current,\n });\n }\n });\n\n return changes;\n}\n\nfunction collectChanges(editableSelector: string): EditModeChange[] {\n return collectDraftChanges(editableSelector).map(({ key: _key, ...change }) => change);\n}\n\nexport function initClientEditMode(options: EditModeOptions = {}): EditModeInstance {\n if (!hasDom()) {\n return { active: false, getChanges: () => [], destroy: () => undefined };\n }\n\n const config: Config = { ...defaults, ...options };\n\n if (!shouldEnable(config)) {\n return { active: false, getChanges: () => [], destroy: () => undefined };\n }\n\n if (window.__COLLIDE_EDIT_MODE_ACTIVE) {\n return { active: true, getChanges: () => collectChanges(config.editableSelector), destroy: () => undefined };\n }\n\n window.__COLLIDE_EDIT_MODE_ACTIVE = true;\n window.sessionStorage.setItem(config.sessionKey, \"1\");\n\n const storage = getStorage();\n const draftKey = getDraftKey(config);\n const style = addStyles(config.accentColour);\n\n const banner = document.createElement(\"div\");\n banner.className = \"cem-edit-banner\";\n banner.innerHTML = `✏️ <strong>${config.brandName}</strong> <span>Click text to edit. Press <kbd>Esc</kbd> when done.</span>`;\n\n const exitButton = document.createElement(\"button\");\n exitButton.className = \"cem-exit-btn\";\n exitButton.type = \"button\";\n exitButton.textContent = \"Exit\";\n banner.appendChild(exitButton);\n document.body.prepend(banner);\n\n const panel = document.createElement(\"div\");\n panel.className = \"cem-panel\";\n panel.innerHTML = `\n <div class=\"cem-panel-actions\">\n <button class=\"cem-copy-btn\" type=\"button\">📋 Copy Changes</button>\n <button class=\"cem-review-btn\" type=\"button\" aria-expanded=\"false\">Review</button>\n <button class=\"cem-download-btn\" type=\"button\">Download backup</button>\n <button class=\"cem-open-link-btn\" type=\"button\" hidden>Open link</button>\n </div>\n <div class=\"cem-review-list\" hidden></div>\n <div class=\"cem-status\" aria-live=\"polite\">Auto-save ready</div>\n `;\n document.body.appendChild(panel);\n\n const copyButton = panel.querySelector<HTMLButtonElement>(\".cem-copy-btn\");\n const reviewButton = panel.querySelector<HTMLButtonElement>(\".cem-review-btn\");\n const downloadButton = panel.querySelector<HTMLButtonElement>(\".cem-download-btn\");\n const openLinkButton = panel.querySelector<HTMLButtonElement>(\".cem-open-link-btn\");\n const reviewList = panel.querySelector<HTMLElement>(\".cem-review-list\");\n const status = panel.querySelector<HTMLElement>(\".cem-status\");\n let activeLink: HTMLAnchorElement | null = null;\n let reviewOpen = false;\n let currentDraftChanges: DraftChange[] = [];\n\n document.querySelectorAll(\"[data-reveal]\").forEach((el) => el.classList.add(\"is-visible\"));\n rewriteLinks(config.queryParam, config.queryValue);\n initEditableElements(config);\n\n const restoredCount = applyDraft(loadDraft(storage, draftKey), config.editableSelector);\n\n const makeDraft = (): Draft => ({\n version: DRAFT_VERSION,\n page: window.location.pathname,\n url: window.location.href,\n updatedAt: new Date().toISOString(),\n changes: collectDraftChanges(config.editableSelector),\n });\n\n const renderReviewList = () => {\n if (!reviewList) return;\n\n if (currentDraftChanges.length === 0) {\n reviewList.innerHTML = '<p class=\"cem-review-empty\">No changes yet.</p>';\n return;\n }\n\n reviewList.innerHTML = currentDraftChanges\n .map(\n (change, index) => `\n <div class=\"cem-review-item\">\n <div class=\"cem-review-path\">${escapeHtml(change.path)}</div>\n <div class=\"cem-review-diff\"><del>${escapeHtml(change.original)}</del><ins>${escapeHtml(change.new)}</ins></div>\n <button class=\"cem-revert-btn\" type=\"button\" data-index=\"${index}\">Revert</button>\n </div>\n `,\n )\n .join(\"\");\n };\n\n const renderStatus = (count: number, message?: string) => {\n if (copyButton) copyButton.textContent = count > 0 ? `📋 Copy Changes (${count})` : \"📋 Copy Changes\";\n if (openLinkButton) {\n openLinkButton.hidden = !activeLink;\n openLinkButton.textContent = activeLink ? `Open ${activeLink.hostname || \"link\"}` : \"Open link\";\n }\n if (status) {\n status.textContent =\n message ||\n `Auto-saved ${formatTime()} • ${count} change${count === 1 ? \"\" : \"s\"} • Links: edit text or Ctrl/⌘-click to open`;\n }\n };\n\n const updateUi = (message?: string) => {\n renderStatus(collectChanges(config.editableSelector).length, message);\n };\n\n const persistDraft = () => {\n const draft = makeDraft();\n currentDraftChanges = draft.changes;\n const saved = config.autoSave !== false && saveDraft(storage, draftKey, draft);\n renderStatus(draft.changes.length, saved || config.autoSave === false ? undefined : \"⚠️ Auto-save unavailable — use Download backup\");\n if (reviewOpen) renderReviewList();\n return draft;\n };\n\n let saveTimeout: number | null = null;\n\n const flushDraftSave = () => {\n if (saveTimeout !== null) {\n window.clearTimeout(saveTimeout);\n saveTimeout = null;\n }\n return persistDraft();\n };\n\n const scheduleDraftSave = () => {\n if (saveTimeout !== null) window.clearTimeout(saveTimeout);\n saveTimeout = window.setTimeout(() => {\n saveTimeout = null;\n persistDraft();\n }, AUTOSAVE_DEBOUNCE_MS);\n };\n\n const finishEditing = () => {\n const activeElement = document.activeElement;\n if (!(activeElement instanceof HTMLElement)) return false;\n if (!activeElement.isContentEditable && activeElement.contentEditable !== \"true\") return false;\n\n activeElement.blur();\n window.getSelection()?.removeAllRanges();\n flushDraftSave();\n return true;\n };\n\n const handleEditingKeydown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n if (!finishEditing()) return;\n event.preventDefault();\n event.stopPropagation();\n return;\n }\n\n if (event.key === \"Enter\" && !event.shiftKey) {\n const target = event.target;\n if (\n target instanceof HTMLElement &&\n SINGLE_LINE_TAGS.has(target.tagName) &&\n (target.isContentEditable || target.contentEditable === \"true\")\n ) {\n event.preventDefault();\n finishEditing();\n }\n }\n };\n\n const handleDocumentClick = (event: MouseEvent) => {\n const target = event.target;\n if (!(target instanceof Element)) return;\n\n const anchor = target.closest<HTMLAnchorElement>(\"a[href]\");\n if (anchor && !anchor.closest(\".cem-panel\") && !anchor.closest(\".cem-edit-banner\")) {\n activeLink = anchor;\n updateUi(\"Link selected — edit the text, use Open link, or Ctrl/⌘-click to visit it\");\n\n if (event.metaKey || event.ctrlKey || event.altKey) {\n flushDraftSave();\n return;\n }\n\n event.preventDefault();\n return;\n }\n\n const button = target.closest(\"button\");\n if (button && !button.closest(\".cem-panel\") && !button.closest(\".cem-edit-banner\")) {\n event.preventDefault();\n event.stopImmediatePropagation();\n }\n };\n\n const handleFocusIn = (event: FocusEvent) => {\n const target = event.target;\n if (!(target instanceof Element)) return;\n\n activeLink = target.closest<HTMLAnchorElement>(\"a[href]\");\n updateUi(activeLink ? \"Link selected — edit the text or use Open link to navigate\" : undefined);\n };\n\n const handleInput = (event: Event) => {\n if (event.target instanceof HTMLElement) markChanged(event.target);\n scheduleDraftSave();\n };\n const handlePageHide = () => flushDraftSave();\n const handleVisibilityChange = () => {\n if (document.visibilityState === \"hidden\") flushDraftSave();\n };\n\n document.addEventListener(\"keydown\", handleEditingKeydown);\n document.addEventListener(\"click\", handleDocumentClick, true);\n document.addEventListener(\"focusin\", handleFocusIn);\n document.addEventListener(\"input\", handleInput);\n window.addEventListener(\"pagehide\", handlePageHide);\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\n\n reviewButton?.addEventListener(\"click\", () => {\n reviewOpen = !reviewOpen;\n if (reviewList) reviewList.hidden = !reviewOpen;\n reviewButton.textContent = reviewOpen ? \"Hide review\" : \"Review\";\n reviewButton.setAttribute(\"aria-expanded\", String(reviewOpen));\n if (reviewOpen) renderReviewList();\n });\n\n reviewList?.addEventListener(\"click\", (event) => {\n const target = event.target;\n if (!(target instanceof HTMLElement)) return;\n\n const revertButton = target.closest<HTMLButtonElement>(\".cem-revert-btn\");\n if (!revertButton) return;\n\n const change = currentDraftChanges[Number(revertButton.dataset.index)];\n if (!change) return;\n\n const el = findEditableByKey(config.editableSelector, change.key);\n if (el) el.textContent = change.original;\n\n flushDraftSave();\n });\n\n copyButton?.addEventListener(\"click\", () => {\n const draft = flushDraftSave();\n const payload = buildPayload(draft.changes);\n options.onCopy?.(payload);\n\n const copyPayload = options.mapPayload ? options.mapPayload(payload) : payload;\n const json = JSON.stringify(copyPayload, null, 2);\n const label = `✓ Copied ${draft.changes.length} change${draft.changes.length !== 1 ? \"s\" : \"\"} to clipboard`;\n\n if (navigator.clipboard?.writeText) {\n navigator.clipboard.writeText(json).then(() => showToast(label)).catch(() => fallbackCopy(json, label));\n } else {\n fallbackCopy(json, label);\n }\n });\n\n downloadButton?.addEventListener(\"click\", () => {\n const draft = flushDraftSave();\n const payload = buildPayload(draft.changes);\n const date = new Date().toISOString().slice(0, 10);\n downloadText(`edit-mode-${window.location.hostname}-${date}.json`, JSON.stringify(payload, null, 2));\n showToast(\"✓ Backup downloaded\");\n });\n\n openLinkButton?.addEventListener(\"click\", () => {\n if (!activeLink?.href) return;\n\n flushDraftSave();\n window.location.href = activeLink.href;\n });\n\n exitButton.addEventListener(\"click\", () => {\n flushDraftSave();\n window.sessionStorage.removeItem(config.sessionKey);\n const url = new URL(window.location.href);\n url.searchParams.delete(config.queryParam);\n window.location.href = url.toString();\n });\n\n persistDraft();\n if (restoredCount > 0) updateUi(`Restored ${restoredCount} saved edit${restoredCount === 1 ? \"\" : \"s\"} • Auto-save on`);\n\n return {\n active: true,\n getChanges: () => collectChanges(config.editableSelector),\n destroy: () => {\n flushDraftSave();\n document.removeEventListener(\"keydown\", handleEditingKeydown);\n document.removeEventListener(\"click\", handleDocumentClick, true);\n document.removeEventListener(\"focusin\", handleFocusIn);\n document.removeEventListener(\"input\", handleInput);\n window.removeEventListener(\"pagehide\", handlePageHide);\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\n banner.remove();\n panel.remove();\n style.remove();\n document.querySelectorAll<HTMLElement>(config.editableSelector).forEach((el) => {\n if (el.dataset.editOriginal !== undefined) {\n el.contentEditable = \"false\";\n el.classList.remove(\"cem-changed\");\n delete el.dataset.editOriginal;\n delete el.dataset.editKey;\n }\n });\n window.__COLLIDE_EDIT_MODE_ACTIVE = false;\n },\n };\n}\n"],"mappings":"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","names":["browser_exports","__export","initClientEditMode","DEFAULT_EDITABLE_SELECTOR","DEFAULT_SKIP_SELECTOR","SKIP_CHILD_TAGS","SINGLE_LINE_TAGS","DRAFT_VERSION","AUTOSAVE_DEBOUNCE_MS","defaults","hasDom","shouldEnable","options","queryValue","addStyles","accentColour","style","showToast","message","toast","fallbackCopy","text","label","textarea","getStorage","testKey","getDraftKey","config","escapeHtml","char","isEditableElement","el","skipSelector","children","child","getElementKey","parts","current","parentElement","currentTag","index","getEditPath","context","section","heading","tag","snippet","rewriteLinks","queryParam","anchor","href","url","hash","buildPayload","changes","loadDraft","storage","key","raw","draft","saveDraft","downloadText","filename","blob","formatTime","date","initEditableElements","findEditableByKey","editableSelector","elements","applyDraft","edits","change","restored","saved","markChanged","original","collectDraftChanges","changed","collectChanges","_key","initClientEditMode","draftKey","banner","exitButton","panel","copyButton","reviewButton","downloadButton","openLinkButton","reviewList","status","activeLink","reviewOpen","currentDraftChanges","restoredCount","makeDraft","renderReviewList","renderStatus","count","updateUi","persistDraft","saveTimeout","flushDraftSave","scheduleDraftSave","finishEditing","activeElement","handleEditingKeydown","event","target","handleDocumentClick","button","handleFocusIn","handleInput","handlePageHide","handleVisibilityChange","revertButton","payload","copyPayload","json","api","initClientEditMode","script","options"]}