@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 +12 -0
- package/dist/browser.global.js +17 -2
- package/dist/browser.global.js.map +1 -1
- package/dist/index.cjs +402 -72
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +402 -72
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
package/dist/browser.global.js
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
-
"use strict";var CollideEditMode=(()=>{var
|
|
2
|
-
`),document.head.appendChild(e),e}function
|
|
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"&";case"<":return"<";case">":return">";case'"':return""";default:return"'"}})}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 \"&\";\n case \"<\":\n return \"<\";\n case \">\":\n return \">\";\n case '\"':\n return \""\";\n default:\n return \"'\";\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"]}
|