@collidecreatives/edit-mode 0.1.0 → 0.2.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 +9 -2
- package/dist/browser.global.js.map +1 -1
- package/dist/index.cjs +270 -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 +270 -72
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -24,8 +24,9 @@ __export(src_exports, {
|
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(src_exports);
|
|
26
26
|
var DEFAULT_EDITABLE_SELECTOR = "h1,h2,h3,h4,h5,h6,p,li,blockquote,figcaption,label,legend,dt,dd,th,td,a,button";
|
|
27
|
-
var DEFAULT_SKIP_SELECTOR = "[data-no-edit],[data-edit-mode-skip],form,input,textarea,select,option,script,style,svg,canvas,iframe,.cem-edit-banner,.cem-
|
|
27
|
+
var DEFAULT_SKIP_SELECTOR = "[data-no-edit],[data-edit-mode-skip],form,input,textarea,select,option,script,style,svg,canvas,iframe,.cem-edit-banner,.cem-panel,.cem-toast";
|
|
28
28
|
var SKIP_CHILD_TAGS = /* @__PURE__ */ new Set(["IMG", "PICTURE", "SVG", "VIDEO", "CANVAS", "IFRAME", "SCRIPT", "STYLE"]);
|
|
29
|
+
var DRAFT_VERSION = 1;
|
|
29
30
|
var defaults = {
|
|
30
31
|
queryParam: "edit",
|
|
31
32
|
queryValue: "true",
|
|
@@ -33,7 +34,8 @@ var defaults = {
|
|
|
33
34
|
brandName: "Edit Mode",
|
|
34
35
|
accentColour: "#1e40af",
|
|
35
36
|
editableSelector: DEFAULT_EDITABLE_SELECTOR,
|
|
36
|
-
skipSelector: DEFAULT_SKIP_SELECTOR
|
|
37
|
+
skipSelector: DEFAULT_SKIP_SELECTOR,
|
|
38
|
+
autoSave: true
|
|
37
39
|
};
|
|
38
40
|
function hasDom() {
|
|
39
41
|
return typeof window !== "undefined" && typeof document !== "undefined";
|
|
@@ -49,18 +51,23 @@ function addStyles(accentColour) {
|
|
|
49
51
|
const style = document.createElement("style");
|
|
50
52
|
style.dataset.editModeStyle = "true";
|
|
51
53
|
style.textContent = [
|
|
52
|
-
`.cem-edit-banner{position:fixed;top:0;left:0;right:0;z-index:2147483640;background:${accentColour};color:#fff;
|
|
53
|
-
".cem-edit-banner kbd{background:rgba(255,255,255
|
|
54
|
-
".cem-exit-btn{position:absolute;right:8px;top:50%;transform:translateY(-50%);background:rgba(255,255,255
|
|
55
|
-
".cem-exit-btn:hover{background:rgba(255,255,255
|
|
54
|
+
`.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)}`,
|
|
55
|
+
".cem-edit-banner kbd{background:rgba(255,255,255,.18);padding:0 4px;border-radius:3px;font-size:11px}",
|
|
56
|
+
".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}",
|
|
57
|
+
".cem-exit-btn:hover,.cem-exit-btn:focus-visible{background:rgba(255,255,255,.25);outline:none}",
|
|
56
58
|
"[contenteditable=true]{cursor:text!important}",
|
|
57
|
-
"[contenteditable=true]:hover{outline:2px dashed rgba(59,130,246
|
|
58
|
-
"[contenteditable=true]:focus{outline:2px solid #3b82f6!important;outline-offset:2px;background:rgba(59,130,246
|
|
59
|
-
`.cem-
|
|
60
|
-
".cem-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
"
|
|
59
|
+
"[contenteditable=true]:hover{outline:2px dashed rgba(59,130,246,.5)!important;outline-offset:2px}",
|
|
60
|
+
"[contenteditable=true]:focus{outline:2px solid #3b82f6!important;outline-offset:2px;background:rgba(59,130,246,.04)}",
|
|
61
|
+
`.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}`,
|
|
62
|
+
".cem-panel-actions{display:flex;gap:8px;flex-wrap:wrap}",
|
|
63
|
+
`.cem-copy-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}`,
|
|
64
|
+
`.cem-copy-btn{background:${accentColour};color:#fff;flex:1}`,
|
|
65
|
+
".cem-download-btn,.cem-open-link-btn{background:#f3f4f6;color:#111827;border:1px solid #e5e7eb}",
|
|
66
|
+
".cem-open-link-btn[hidden]{display:none}",
|
|
67
|
+
".cem-copy-btn:hover,.cem-download-btn:hover,.cem-open-link-btn:hover{filter:brightness(.97)}",
|
|
68
|
+
".cem-status{color:#4b5563;font-size:12px;min-height:17px}",
|
|
69
|
+
".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}",
|
|
70
|
+
"@media(max-width:520px){.cem-edit-banner{justify-content:flex-start;text-align:left}.cem-panel{left:16px;right:16px;width:auto}}"
|
|
64
71
|
].join("\n");
|
|
65
72
|
document.head.appendChild(style);
|
|
66
73
|
return style;
|
|
@@ -90,6 +97,24 @@ function fallbackCopy(text, label) {
|
|
|
90
97
|
document.body.removeChild(textarea);
|
|
91
98
|
showToast(label);
|
|
92
99
|
}
|
|
100
|
+
function getStorage() {
|
|
101
|
+
try {
|
|
102
|
+
const testKey = "__cem_storage_test__";
|
|
103
|
+
window.localStorage.setItem(testKey, "1");
|
|
104
|
+
window.localStorage.removeItem(testKey);
|
|
105
|
+
return window.localStorage;
|
|
106
|
+
} catch {
|
|
107
|
+
try {
|
|
108
|
+
return window.sessionStorage;
|
|
109
|
+
} catch {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function getDraftKey(config) {
|
|
115
|
+
const base = config.storageKey || `${config.sessionKey}:draft`;
|
|
116
|
+
return `${base}:${window.location.origin}${window.location.pathname}`;
|
|
117
|
+
}
|
|
93
118
|
function isEditableElement(el, skipSelector) {
|
|
94
119
|
if (el.closest(skipSelector)) return false;
|
|
95
120
|
const text = el.textContent?.trim() ?? "";
|
|
@@ -100,6 +125,22 @@ function isEditableElement(el, skipSelector) {
|
|
|
100
125
|
}
|
|
101
126
|
return true;
|
|
102
127
|
}
|
|
128
|
+
function getElementKey(el) {
|
|
129
|
+
if (el.dataset.editId) return `data-edit-id:${el.dataset.editId}`;
|
|
130
|
+
if (el.id) return `id:${el.id}`;
|
|
131
|
+
const parts = [];
|
|
132
|
+
let current = el;
|
|
133
|
+
while (current && current !== document.body && current !== document.documentElement) {
|
|
134
|
+
const parentElement = current.parentElement;
|
|
135
|
+
if (!parentElement) break;
|
|
136
|
+
const currentTag = current.tagName;
|
|
137
|
+
const siblings = Array.from(parentElement.children).filter((child) => child.tagName === currentTag);
|
|
138
|
+
const index = siblings.indexOf(current) + 1;
|
|
139
|
+
parts.unshift(`${current.tagName.toLowerCase()}:nth-of-type(${index})`);
|
|
140
|
+
current = parentElement;
|
|
141
|
+
}
|
|
142
|
+
return parts.join(" > ");
|
|
143
|
+
}
|
|
103
144
|
function getEditPath(el) {
|
|
104
145
|
let context = "";
|
|
105
146
|
let current = el.parentElement;
|
|
@@ -146,6 +187,98 @@ function rewriteLinks(queryParam, queryValue) {
|
|
|
146
187
|
}
|
|
147
188
|
});
|
|
148
189
|
}
|
|
190
|
+
function buildPayload(changes) {
|
|
191
|
+
return {
|
|
192
|
+
site: window.location.hostname,
|
|
193
|
+
page: window.location.pathname,
|
|
194
|
+
pageTitle: document.title,
|
|
195
|
+
url: window.location.href,
|
|
196
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
197
|
+
changes
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
function loadDraft(storage, key) {
|
|
201
|
+
if (!storage) return null;
|
|
202
|
+
try {
|
|
203
|
+
const raw = storage.getItem(key);
|
|
204
|
+
if (!raw) return null;
|
|
205
|
+
const draft = JSON.parse(raw);
|
|
206
|
+
return draft?.version === DRAFT_VERSION && Array.isArray(draft.changes) ? draft : null;
|
|
207
|
+
} catch {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
function saveDraft(storage, key, draft) {
|
|
212
|
+
if (!storage) return false;
|
|
213
|
+
try {
|
|
214
|
+
storage.setItem(key, JSON.stringify(draft));
|
|
215
|
+
return true;
|
|
216
|
+
} catch {
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
function downloadText(filename, text) {
|
|
221
|
+
const blob = new Blob([text], { type: "application/json" });
|
|
222
|
+
const url = URL.createObjectURL(blob);
|
|
223
|
+
const anchor = document.createElement("a");
|
|
224
|
+
anchor.href = url;
|
|
225
|
+
anchor.download = filename;
|
|
226
|
+
anchor.style.display = "none";
|
|
227
|
+
document.body.appendChild(anchor);
|
|
228
|
+
anchor.click();
|
|
229
|
+
anchor.remove();
|
|
230
|
+
window.setTimeout(() => URL.revokeObjectURL(url), 1e3);
|
|
231
|
+
}
|
|
232
|
+
function formatTime(date = /* @__PURE__ */ new Date()) {
|
|
233
|
+
return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
|
|
234
|
+
}
|
|
235
|
+
function initEditableElements(config) {
|
|
236
|
+
document.querySelectorAll(config.editableSelector).forEach((el) => {
|
|
237
|
+
if (!isEditableElement(el, config.skipSelector)) return;
|
|
238
|
+
const text = el.textContent?.trim() ?? "";
|
|
239
|
+
el.contentEditable = "true";
|
|
240
|
+
if (!el.dataset.editOriginal) el.dataset.editOriginal = text;
|
|
241
|
+
if (!el.dataset.editKey) el.dataset.editKey = getElementKey(el);
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
function applyDraft(draft, editableSelector) {
|
|
245
|
+
if (!draft) return 0;
|
|
246
|
+
const edits = new Map(draft.changes.map((change) => [change.key, change]));
|
|
247
|
+
let restored = 0;
|
|
248
|
+
document.querySelectorAll(editableSelector).forEach((el) => {
|
|
249
|
+
const key = el.dataset.editKey;
|
|
250
|
+
if (!key) return;
|
|
251
|
+
const saved = edits.get(key);
|
|
252
|
+
if (!saved) return;
|
|
253
|
+
if (el.dataset.editOriginal !== saved.original) return;
|
|
254
|
+
el.textContent = saved.new;
|
|
255
|
+
restored += 1;
|
|
256
|
+
});
|
|
257
|
+
return restored;
|
|
258
|
+
}
|
|
259
|
+
function collectDraftChanges(editableSelector) {
|
|
260
|
+
const changes = [];
|
|
261
|
+
document.querySelectorAll(editableSelector).forEach((el) => {
|
|
262
|
+
if (!el.isContentEditable && el.contentEditable !== "true") return;
|
|
263
|
+
const original = el.dataset.editOriginal;
|
|
264
|
+
const key = el.dataset.editKey;
|
|
265
|
+
if (original === void 0 || !key) return;
|
|
266
|
+
const current = el.textContent?.trim() ?? "";
|
|
267
|
+
if (original !== current) {
|
|
268
|
+
changes.push({
|
|
269
|
+
key,
|
|
270
|
+
path: getEditPath(el),
|
|
271
|
+
tag: el.tagName,
|
|
272
|
+
original,
|
|
273
|
+
new: current
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
return changes;
|
|
278
|
+
}
|
|
279
|
+
function collectChanges(editableSelector) {
|
|
280
|
+
return collectDraftChanges(editableSelector).map(({ key: _key, ...change }) => change);
|
|
281
|
+
}
|
|
149
282
|
function initClientEditMode(options = {}) {
|
|
150
283
|
if (!hasDom()) {
|
|
151
284
|
return { active: false, getChanges: () => [], destroy: () => void 0 };
|
|
@@ -159,108 +292,173 @@ function initClientEditMode(options = {}) {
|
|
|
159
292
|
}
|
|
160
293
|
window.__COLLIDE_EDIT_MODE_ACTIVE = true;
|
|
161
294
|
window.sessionStorage.setItem(config.sessionKey, "1");
|
|
295
|
+
const storage = getStorage();
|
|
296
|
+
const draftKey = getDraftKey(config);
|
|
162
297
|
const style = addStyles(config.accentColour);
|
|
163
298
|
const banner = document.createElement("div");
|
|
164
299
|
banner.className = "cem-edit-banner";
|
|
165
|
-
banner.innerHTML = `\u270F\uFE0F <strong>${config.brandName}</strong>
|
|
300
|
+
banner.innerHTML = `\u270F\uFE0F <strong>${config.brandName}</strong> <span>Click text to edit. Press <kbd>Esc</kbd> when done.</span>`;
|
|
166
301
|
const exitButton = document.createElement("button");
|
|
167
302
|
exitButton.className = "cem-exit-btn";
|
|
168
|
-
exitButton.
|
|
169
|
-
exitButton.
|
|
170
|
-
window.sessionStorage.removeItem(config.sessionKey);
|
|
171
|
-
const url = new URL(window.location.href);
|
|
172
|
-
url.searchParams.delete(config.queryParam);
|
|
173
|
-
window.location.href = url.toString();
|
|
174
|
-
});
|
|
303
|
+
exitButton.type = "button";
|
|
304
|
+
exitButton.textContent = "Exit";
|
|
175
305
|
banner.appendChild(exitButton);
|
|
176
306
|
document.body.prepend(banner);
|
|
307
|
+
const panel = document.createElement("div");
|
|
308
|
+
panel.className = "cem-panel";
|
|
309
|
+
panel.innerHTML = `
|
|
310
|
+
<div class="cem-panel-actions">
|
|
311
|
+
<button class="cem-copy-btn" type="button">\u{1F4CB} Copy Changes</button>
|
|
312
|
+
<button class="cem-download-btn" type="button">Download backup</button>
|
|
313
|
+
<button class="cem-open-link-btn" type="button" hidden>Open link</button>
|
|
314
|
+
</div>
|
|
315
|
+
<div class="cem-status" aria-live="polite">Auto-save ready</div>
|
|
316
|
+
`;
|
|
317
|
+
document.body.appendChild(panel);
|
|
318
|
+
const copyButton = panel.querySelector(".cem-copy-btn");
|
|
319
|
+
const downloadButton = panel.querySelector(".cem-download-btn");
|
|
320
|
+
const openLinkButton = panel.querySelector(".cem-open-link-btn");
|
|
321
|
+
const status = panel.querySelector(".cem-status");
|
|
322
|
+
let activeLink = null;
|
|
177
323
|
document.querySelectorAll("[data-reveal]").forEach((el) => el.classList.add("is-visible"));
|
|
178
324
|
rewriteLinks(config.queryParam, config.queryValue);
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
325
|
+
initEditableElements(config);
|
|
326
|
+
const restoredCount = applyDraft(loadDraft(storage, draftKey), config.editableSelector);
|
|
327
|
+
const makeDraft = () => ({
|
|
328
|
+
version: DRAFT_VERSION,
|
|
329
|
+
page: window.location.pathname,
|
|
330
|
+
url: window.location.href,
|
|
331
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
332
|
+
changes: collectDraftChanges(config.editableSelector)
|
|
333
|
+
});
|
|
334
|
+
const updateUi = (message) => {
|
|
335
|
+
const count = collectChanges(config.editableSelector).length;
|
|
336
|
+
if (copyButton) copyButton.textContent = count > 0 ? `\u{1F4CB} Copy Changes (${count})` : "\u{1F4CB} Copy Changes";
|
|
337
|
+
if (openLinkButton) {
|
|
338
|
+
openLinkButton.hidden = !activeLink;
|
|
339
|
+
openLinkButton.textContent = activeLink ? `Open ${activeLink.hostname || "link"}` : "Open link";
|
|
340
|
+
}
|
|
341
|
+
if (status) {
|
|
342
|
+
status.textContent = message || `Auto-saved ${formatTime()} \u2022 ${count} change${count === 1 ? "" : "s"} \u2022 Links: edit text or Ctrl/\u2318-click to open`;
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
const persistDraft = () => {
|
|
346
|
+
const draft = makeDraft();
|
|
347
|
+
const saved = config.autoSave !== false && saveDraft(storage, draftKey, draft);
|
|
348
|
+
updateUi(saved || config.autoSave === false ? void 0 : "\u26A0\uFE0F Auto-save unavailable \u2014 use Download backup");
|
|
349
|
+
return draft;
|
|
186
350
|
};
|
|
187
|
-
|
|
188
|
-
|
|
351
|
+
const finishEditing = () => {
|
|
352
|
+
const activeElement = document.activeElement;
|
|
353
|
+
if (!(activeElement instanceof HTMLElement)) return false;
|
|
354
|
+
if (!activeElement.isContentEditable && activeElement.contentEditable !== "true") return false;
|
|
355
|
+
activeElement.blur();
|
|
356
|
+
window.getSelection()?.removeAllRanges();
|
|
357
|
+
persistDraft();
|
|
358
|
+
return true;
|
|
359
|
+
};
|
|
360
|
+
const finishEditingOnEscape = (event) => {
|
|
361
|
+
if (event.key !== "Escape") return;
|
|
362
|
+
if (!finishEditing()) return;
|
|
363
|
+
event.preventDefault();
|
|
364
|
+
event.stopPropagation();
|
|
365
|
+
};
|
|
366
|
+
const handleDocumentClick = (event) => {
|
|
189
367
|
const target = event.target;
|
|
190
368
|
if (!(target instanceof Element)) return;
|
|
369
|
+
const anchor = target.closest("a[href]");
|
|
370
|
+
if (anchor && !anchor.closest(".cem-panel") && !anchor.closest(".cem-edit-banner")) {
|
|
371
|
+
activeLink = anchor;
|
|
372
|
+
updateUi("Link selected \u2014 edit the text, use Open link, or Ctrl/\u2318-click to visit it");
|
|
373
|
+
if (event.metaKey || event.ctrlKey || event.altKey) {
|
|
374
|
+
persistDraft();
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
event.preventDefault();
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
191
380
|
const button = target.closest("button");
|
|
192
|
-
if (button && !button.
|
|
381
|
+
if (button && !button.closest(".cem-panel") && !button.closest(".cem-edit-banner")) {
|
|
193
382
|
event.preventDefault();
|
|
194
383
|
event.stopImmediatePropagation();
|
|
195
384
|
}
|
|
196
385
|
};
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
386
|
+
const handleFocusIn = (event) => {
|
|
387
|
+
const target = event.target;
|
|
388
|
+
if (!(target instanceof Element)) return;
|
|
389
|
+
activeLink = target.closest("a[href]");
|
|
390
|
+
updateUi(activeLink ? "Link selected \u2014 edit the text or use Open link to navigate" : void 0);
|
|
391
|
+
};
|
|
392
|
+
const handleInput = () => persistDraft();
|
|
393
|
+
const handlePageHide = () => persistDraft();
|
|
394
|
+
const handleVisibilityChange = () => {
|
|
395
|
+
if (document.visibilityState === "hidden") persistDraft();
|
|
204
396
|
};
|
|
205
|
-
document.addEventListener("
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
215
|
-
changes
|
|
216
|
-
};
|
|
397
|
+
document.addEventListener("keydown", finishEditingOnEscape);
|
|
398
|
+
document.addEventListener("click", handleDocumentClick, true);
|
|
399
|
+
document.addEventListener("focusin", handleFocusIn);
|
|
400
|
+
document.addEventListener("input", handleInput);
|
|
401
|
+
window.addEventListener("pagehide", handlePageHide);
|
|
402
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
403
|
+
copyButton?.addEventListener("click", () => {
|
|
404
|
+
const draft = persistDraft();
|
|
405
|
+
const payload = buildPayload(draft.changes);
|
|
217
406
|
options.onCopy?.(payload);
|
|
218
407
|
const copyPayload = options.mapPayload ? options.mapPayload(payload) : payload;
|
|
219
408
|
const json = JSON.stringify(copyPayload, null, 2);
|
|
220
|
-
const label = `\u2713 Copied ${changes.length} change${changes.length !== 1 ? "s" : ""} to clipboard`;
|
|
409
|
+
const label = `\u2713 Copied ${draft.changes.length} change${draft.changes.length !== 1 ? "s" : ""} to clipboard`;
|
|
221
410
|
if (navigator.clipboard?.writeText) {
|
|
222
411
|
navigator.clipboard.writeText(json).then(() => showToast(label)).catch(() => fallbackCopy(json, label));
|
|
223
412
|
} else {
|
|
224
413
|
fallbackCopy(json, label);
|
|
225
414
|
}
|
|
226
415
|
});
|
|
416
|
+
downloadButton?.addEventListener("click", () => {
|
|
417
|
+
const draft = persistDraft();
|
|
418
|
+
const payload = buildPayload(draft.changes);
|
|
419
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
420
|
+
downloadText(`edit-mode-${window.location.hostname}-${date}.json`, JSON.stringify(payload, null, 2));
|
|
421
|
+
showToast("\u2713 Backup downloaded");
|
|
422
|
+
});
|
|
423
|
+
openLinkButton?.addEventListener("click", () => {
|
|
424
|
+
if (!activeLink?.href) return;
|
|
425
|
+
persistDraft();
|
|
426
|
+
window.location.href = activeLink.href;
|
|
427
|
+
});
|
|
428
|
+
exitButton.addEventListener("click", () => {
|
|
429
|
+
persistDraft();
|
|
430
|
+
window.sessionStorage.removeItem(config.sessionKey);
|
|
431
|
+
const url = new URL(window.location.href);
|
|
432
|
+
url.searchParams.delete(config.queryParam);
|
|
433
|
+
window.location.href = url.toString();
|
|
434
|
+
});
|
|
435
|
+
persistDraft();
|
|
436
|
+
if (restoredCount > 0) updateUi(`Restored ${restoredCount} saved edit${restoredCount === 1 ? "" : "s"} \u2022 Auto-save on`);
|
|
227
437
|
return {
|
|
228
438
|
active: true,
|
|
229
439
|
getChanges: () => collectChanges(config.editableSelector),
|
|
230
440
|
destroy: () => {
|
|
231
|
-
|
|
232
|
-
document.removeEventListener("
|
|
441
|
+
persistDraft();
|
|
442
|
+
document.removeEventListener("keydown", finishEditingOnEscape);
|
|
443
|
+
document.removeEventListener("click", handleDocumentClick, true);
|
|
444
|
+
document.removeEventListener("focusin", handleFocusIn);
|
|
445
|
+
document.removeEventListener("input", handleInput);
|
|
446
|
+
window.removeEventListener("pagehide", handlePageHide);
|
|
447
|
+
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
233
448
|
banner.remove();
|
|
234
|
-
|
|
449
|
+
panel.remove();
|
|
235
450
|
style.remove();
|
|
236
451
|
document.querySelectorAll(config.editableSelector).forEach((el) => {
|
|
237
452
|
if (el.dataset.editOriginal !== void 0) {
|
|
238
453
|
el.contentEditable = "false";
|
|
239
454
|
delete el.dataset.editOriginal;
|
|
455
|
+
delete el.dataset.editKey;
|
|
240
456
|
}
|
|
241
457
|
});
|
|
242
458
|
window.__COLLIDE_EDIT_MODE_ACTIVE = false;
|
|
243
459
|
}
|
|
244
460
|
};
|
|
245
461
|
}
|
|
246
|
-
function collectChanges(editableSelector) {
|
|
247
|
-
const changes = [];
|
|
248
|
-
document.querySelectorAll(editableSelector).forEach((el) => {
|
|
249
|
-
if (!el.isContentEditable && el.contentEditable !== "true") return;
|
|
250
|
-
const original = el.dataset.editOriginal;
|
|
251
|
-
if (original === void 0) return;
|
|
252
|
-
const current = el.textContent?.trim() ?? "";
|
|
253
|
-
if (original !== current) {
|
|
254
|
-
changes.push({
|
|
255
|
-
path: getEditPath(el),
|
|
256
|
-
tag: el.tagName,
|
|
257
|
-
original,
|
|
258
|
-
new: current
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
});
|
|
262
|
-
return changes;
|
|
263
|
-
}
|
|
264
462
|
// Annotate the CommonJS export names for ESM import in node:
|
|
265
463
|
0 && (module.exports = {
|
|
266
464
|
initClientEditMode
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["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":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["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 DRAFT_VERSION = 1;\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 `.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-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-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-download-btn:hover,.cem-open-link-btn:hover{filter:brightness(.97)}\",\n \".cem-status{color:#4b5563;font-size:12px;min-height:17px}\",\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 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 document.querySelectorAll<HTMLElement>(config.editableSelector).forEach((el) => {\n if (!isEditableElement(el, config.skipSelector)) 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 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 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 if (original !== current) {\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-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-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 downloadButton = panel.querySelector<HTMLButtonElement>(\".cem-download-btn\");\n const openLinkButton = panel.querySelector<HTMLButtonElement>(\".cem-open-link-btn\");\n const status = panel.querySelector<HTMLElement>(\".cem-status\");\n let activeLink: HTMLAnchorElement | null = null;\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 updateUi = (message?: string) => {\n const count = collectChanges(config.editableSelector).length;\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 persistDraft = () => {\n const draft = makeDraft();\n const saved = config.autoSave !== false && saveDraft(storage, draftKey, draft);\n updateUi(saved || config.autoSave === false ? undefined : \"⚠️ Auto-save unavailable — use Download backup\");\n return draft;\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 persistDraft();\n return true;\n };\n\n const finishEditingOnEscape = (event: KeyboardEvent) => {\n if (event.key !== \"Escape\") return;\n if (!finishEditing()) return;\n\n event.preventDefault();\n event.stopPropagation();\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 persistDraft();\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 = () => persistDraft();\n const handlePageHide = () => persistDraft();\n const handleVisibilityChange = () => {\n if (document.visibilityState === \"hidden\") persistDraft();\n };\n\n document.addEventListener(\"keydown\", finishEditingOnEscape);\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 copyButton?.addEventListener(\"click\", () => {\n const draft = persistDraft();\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 = persistDraft();\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 persistDraft();\n window.location.href = activeLink.href;\n });\n\n exitButton.addEventListener(\"click\", () => {\n persistDraft();\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 persistDraft();\n document.removeEventListener(\"keydown\", finishEditingOnEscape);\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 delete el.dataset.editOriginal;\n delete el.dataset.editKey;\n }\n });\n window.__COLLIDE_EDIT_MODE_ACTIVE = false;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,IAAM,4BACJ;AAEF,IAAM,wBACJ;AAEF,IAAM,kBAAkB,oBAAI,IAAI,CAAC,OAAO,WAAW,OAAO,SAAS,UAAU,UAAU,UAAU,OAAO,CAAC;AACzG,IAAM,gBAAgB;AAmBtB,IAAM,WAAW;AAAA,EACf,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,cAAc;AAAA,EACd,kBAAkB;AAAA,EAClB,cAAc;AAAA,EACd,UAAU;AACZ;AAEA,SAAS,SAAS;AAChB,SAAO,OAAO,WAAW,eAAe,OAAO,aAAa;AAC9D;AAEA,SAAS,aAAa,SAAiB;AACrC,MAAI,QAAQ,YAAY,OAAW,QAAO,QAAQ;AAElD,QAAM,MAAM,IAAI,IAAI,OAAO,SAAS,IAAI;AACxC,QAAM,aAAa,IAAI,aAAa,IAAI,QAAQ,UAAU;AAC1D,QAAM,eACJ,QAAQ,eAAe,OAAO,eAAe,OAAO,eAAe,QAAQ;AAE7E,SAAO,gBAAgB,OAAO,eAAe,QAAQ,QAAQ,UAAU,MAAM;AAC/E;AAEA,SAAS,UAAU,cAAsB;AACvC,QAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,QAAM,QAAQ,gBAAgB;AAC9B,QAAM,cAAc;AAAA,IAClB,sFAAsF,YAAY;AAAA,IAClG;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,4BAA4B,YAAY;AAAA,IACxC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACX,WAAS,KAAK,YAAY,KAAK;AAC/B,SAAO;AACT;AAEA,SAAS,UAAU,SAAiB;AAClC,WAAS,cAAc,YAAY,GAAG,OAAO;AAC7C,QAAM,QAAQ,SAAS,cAAc,KAAK;AAC1C,QAAM,YAAY;AAClB,QAAM,cAAc;AACpB,WAAS,KAAK,YAAY,KAAK;AAC/B,SAAO,WAAW,MAAM;AACtB,UAAM,MAAM,UAAU;AACtB,WAAO,WAAW,MAAM,MAAM,YAAY,YAAY,KAAK,GAAG,GAAG;AAAA,EACnE,GAAG,IAAI;AACT;AAEA,SAAS,aAAa,MAAc,OAAe;AACjD,QAAM,WAAW,SAAS,cAAc,UAAU;AAClD,WAAS,QAAQ;AACjB,WAAS,MAAM,WAAW;AAC1B,WAAS,MAAM,OAAO;AACtB,WAAS,KAAK,YAAY,QAAQ;AAClC,WAAS,OAAO;AAChB,MAAI;AACF,aAAS,YAAY,MAAM;AAAA,EAC7B,QAAQ;AAAA,EAER;AACA,WAAS,KAAK,YAAY,QAAQ;AAClC,YAAU,KAAK;AACjB;AAEA,SAAS,aAAa;AACpB,MAAI;AACF,UAAM,UAAU;AAChB,WAAO,aAAa,QAAQ,SAAS,GAAG;AACxC,WAAO,aAAa,WAAW,OAAO;AACtC,WAAO,OAAO;AAAA,EAChB,QAAQ;AACN,QAAI;AACF,aAAO,OAAO;AAAA,IAChB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEA,SAAS,YAAY,QAAgB;AACnC,QAAM,OAAO,OAAO,cAAc,GAAG,OAAO,UAAU;AACtD,SAAO,GAAG,IAAI,IAAI,OAAO,SAAS,MAAM,GAAG,OAAO,SAAS,QAAQ;AACrE;AAEA,SAAS,kBAAkB,IAAa,cAAsB;AAC5D,MAAI,GAAG,QAAQ,YAAY,EAAG,QAAO;AAErC,QAAM,OAAO,GAAG,aAAa,KAAK,KAAK;AACvC,MAAI,CAAC,KAAM,QAAO;AAElB,QAAM,WAAW,MAAM,KAAK,GAAG,QAAQ;AACvC,MAAI,SAAS,SAAS,KAAK,SAAS,MAAM,CAAC,UAAU,gBAAgB,IAAI,MAAM,OAAO,CAAC,GAAG;AACxF,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,cAAc,IAAiB;AACtC,MAAI,GAAG,QAAQ,OAAQ,QAAO,gBAAgB,GAAG,QAAQ,MAAM;AAC/D,MAAI,GAAG,GAAI,QAAO,MAAM,GAAG,EAAE;AAE7B,QAAM,QAAkB,CAAC;AACzB,MAAI,UAA0B;AAE9B,SAAO,WAAW,YAAY,SAAS,QAAQ,YAAY,SAAS,iBAAiB;AACnF,UAAM,gBAAoC,QAAQ;AAClD,QAAI,CAAC,cAAe;AAEpB,UAAM,aAAa,QAAQ;AAC3B,UAAM,WAAW,MAAM,KAAK,cAAc,QAAQ,EAAE,OAAO,CAAC,UAAU,MAAM,YAAY,UAAU;AAClG,UAAM,QAAQ,SAAS,QAAQ,OAAO,IAAI;AAC1C,UAAM,QAAQ,GAAG,QAAQ,QAAQ,YAAY,CAAC,gBAAgB,KAAK,GAAG;AACtE,cAAU;AAAA,EACZ;AAEA,SAAO,MAAM,KAAK,KAAK;AACzB;AAEA,SAAS,YAAY,IAAiB;AACpC,MAAI,UAAU;AACd,MAAI,UAAU,GAAG;AAEjB,SAAO,WAAW,YAAY,SAAS,QAAQ,YAAY,SAAS,iBAAiB;AACnF,QAAI,QAAQ,IAAI;AACd,gBAAU,IAAI,QAAQ,EAAE;AACxB;AAAA,IACF;AAEA,UAAM,UAAU,QAAQ,aAAa,cAAc;AACnD,QAAI,SAAS;AACX,gBAAU;AACV;AAAA,IACF;AAEA,QAAI,QAAQ,YAAY,WAAW;AACjC,YAAM,UAAU,QAAQ,cAAc,UAAU;AAChD,UAAI,SAAS,aAAa;AACxB,kBAAU,QAAQ,YAAY,KAAK,EAAE,MAAM,GAAG,EAAE;AAChD;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC,UAAU,UAAU,QAAQ,KAAK,EAAE,SAAS,QAAQ,OAAO,GAAG;AACjE,gBAAU,QAAQ,QAAQ,YAAY;AACtC;AAAA,IACF;AAEA,cAAU,QAAQ;AAAA,EACpB;AAEA,QAAM,MAAM,GAAG,QAAQ,YAAY;AACnC,QAAM,OAAO,GAAG,QAAQ,gBAAgB,GAAG,aAAa,KAAK,KAAK;AAClE,QAAM,UAAU,KAAK,MAAM,GAAG,EAAE,KAAK,KAAK,SAAS,KAAK,QAAQ;AAChE,SAAO,GAAG,UAAU,GAAG,OAAO,QAAQ,EAAE,GAAG,GAAG,MAAM,OAAO;AAC7D;AAEA,SAAS,aAAa,YAAoB,YAA2B;AACnE,WAAS,iBAAoC,SAAS,EAAE,QAAQ,CAAC,WAAW;AAC1E,UAAM,OAAO,OAAO,aAAa,MAAM;AACvC,QAAI,CAAC,QAAQ,uCAAuC,KAAK,IAAI,EAAG;AAEhE,QAAI;AACF,YAAM,MAAM,IAAI,IAAI,MAAM,OAAO,SAAS,IAAI;AAC9C,UAAI,IAAI,WAAW,OAAO,SAAS,OAAQ;AAE3C,UAAI,aAAa,IAAI,YAAY,cAAc,GAAG;AAClD,YAAM,OAAO,IAAI;AACjB,UAAI,OAAO;AACX,aAAO,aAAa,QAAQ,GAAG,IAAI,QAAQ,GAAG,IAAI,MAAM,GAAG,IAAI,EAAE;AAAA,IACnE,QAAQ;AAAA,IAER;AAAA,EACF,CAAC;AACH;AAEA,SAAS,aAAa,SAA4C;AAChE,SAAO;AAAA,IACL,MAAM,OAAO,SAAS;AAAA,IACtB,MAAM,OAAO,SAAS;AAAA,IACtB,WAAW,SAAS;AAAA,IACpB,KAAK,OAAO,SAAS;AAAA,IACrB,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC;AAAA,EACF;AACF;AAEA,SAAS,UAAU,SAAyB,KAA2B;AACrE,MAAI,CAAC,QAAS,QAAO;AAErB,MAAI;AACF,UAAM,MAAM,QAAQ,QAAQ,GAAG;AAC/B,QAAI,CAAC,IAAK,QAAO;AACjB,UAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,WAAO,OAAO,YAAY,iBAAiB,MAAM,QAAQ,MAAM,OAAO,IAAI,QAAQ;AAAA,EACpF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,UAAU,SAAyB,KAAa,OAAc;AACrE,MAAI,CAAC,QAAS,QAAO;AAErB,MAAI;AACF,YAAQ,QAAQ,KAAK,KAAK,UAAU,KAAK,CAAC;AAC1C,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,aAAa,UAAkB,MAAc;AACpD,QAAM,OAAO,IAAI,KAAK,CAAC,IAAI,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAC1D,QAAM,MAAM,IAAI,gBAAgB,IAAI;AACpC,QAAM,SAAS,SAAS,cAAc,GAAG;AACzC,SAAO,OAAO;AACd,SAAO,WAAW;AAClB,SAAO,MAAM,UAAU;AACvB,WAAS,KAAK,YAAY,MAAM;AAChC,SAAO,MAAM;AACb,SAAO,OAAO;AACd,SAAO,WAAW,MAAM,IAAI,gBAAgB,GAAG,GAAG,GAAI;AACxD;AAEA,SAAS,WAAW,OAAO,oBAAI,KAAK,GAAG;AACrC,SAAO,KAAK,mBAAmB,CAAC,GAAG,EAAE,MAAM,WAAW,QAAQ,UAAU,CAAC;AAC3E;AAEA,SAAS,qBAAqB,QAAgB;AAC5C,WAAS,iBAA8B,OAAO,gBAAgB,EAAE,QAAQ,CAAC,OAAO;AAC9E,QAAI,CAAC,kBAAkB,IAAI,OAAO,YAAY,EAAG;AAEjD,UAAM,OAAO,GAAG,aAAa,KAAK,KAAK;AACvC,OAAG,kBAAkB;AACrB,QAAI,CAAC,GAAG,QAAQ,aAAc,IAAG,QAAQ,eAAe;AACxD,QAAI,CAAC,GAAG,QAAQ,QAAS,IAAG,QAAQ,UAAU,cAAc,EAAE;AAAA,EAChE,CAAC;AACH;AAEA,SAAS,WAAW,OAAqB,kBAA0B;AACjE,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,QAAQ,IAAI,IAAI,MAAM,QAAQ,IAAI,CAAC,WAAW,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC;AACzE,MAAI,WAAW;AAEf,WAAS,iBAA8B,gBAAgB,EAAE,QAAQ,CAAC,OAAO;AACvE,UAAM,MAAM,GAAG,QAAQ;AACvB,QAAI,CAAC,IAAK;AAEV,UAAM,QAAQ,MAAM,IAAI,GAAG;AAC3B,QAAI,CAAC,MAAO;AAGZ,QAAI,GAAG,QAAQ,iBAAiB,MAAM,SAAU;AAEhD,OAAG,cAAc,MAAM;AACvB,gBAAY;AAAA,EACd,CAAC;AAED,SAAO;AACT;AAEA,SAAS,oBAAoB,kBAAyC;AACpE,QAAM,UAAyB,CAAC;AAEhC,WAAS,iBAA8B,gBAAgB,EAAE,QAAQ,CAAC,OAAO;AACvE,QAAI,CAAC,GAAG,qBAAqB,GAAG,oBAAoB,OAAQ;AAC5D,UAAM,WAAW,GAAG,QAAQ;AAC5B,UAAM,MAAM,GAAG,QAAQ;AACvB,QAAI,aAAa,UAAa,CAAC,IAAK;AAEpC,UAAM,UAAU,GAAG,aAAa,KAAK,KAAK;AAC1C,QAAI,aAAa,SAAS;AACxB,cAAQ,KAAK;AAAA,QACX;AAAA,QACA,MAAM,YAAY,EAAE;AAAA,QACpB,KAAK,GAAG;AAAA,QACR;AAAA,QACA,KAAK;AAAA,MACP,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AAED,SAAO;AACT;AAEA,SAAS,eAAe,kBAA4C;AAClE,SAAO,oBAAoB,gBAAgB,EAAE,IAAI,CAAC,EAAE,KAAK,MAAM,GAAG,OAAO,MAAM,MAAM;AACvF;AAEO,SAAS,mBAAmB,UAA2B,CAAC,GAAqB;AAClF,MAAI,CAAC,OAAO,GAAG;AACb,WAAO,EAAE,QAAQ,OAAO,YAAY,MAAM,CAAC,GAAG,SAAS,MAAM,OAAU;AAAA,EACzE;AAEA,QAAM,SAAiB,EAAE,GAAG,UAAU,GAAG,QAAQ;AAEjD,MAAI,CAAC,aAAa,MAAM,GAAG;AACzB,WAAO,EAAE,QAAQ,OAAO,YAAY,MAAM,CAAC,GAAG,SAAS,MAAM,OAAU;AAAA,EACzE;AAEA,MAAI,OAAO,4BAA4B;AACrC,WAAO,EAAE,QAAQ,MAAM,YAAY,MAAM,eAAe,OAAO,gBAAgB,GAAG,SAAS,MAAM,OAAU;AAAA,EAC7G;AAEA,SAAO,6BAA6B;AACpC,SAAO,eAAe,QAAQ,OAAO,YAAY,GAAG;AAEpD,QAAM,UAAU,WAAW;AAC3B,QAAM,WAAW,YAAY,MAAM;AACnC,QAAM,QAAQ,UAAU,OAAO,YAAY;AAE3C,QAAM,SAAS,SAAS,cAAc,KAAK;AAC3C,SAAO,YAAY;AACnB,SAAO,YAAY,wBAAc,OAAO,SAAS;AAEjD,QAAM,aAAa,SAAS,cAAc,QAAQ;AAClD,aAAW,YAAY;AACvB,aAAW,OAAO;AAClB,aAAW,cAAc;AACzB,SAAO,YAAY,UAAU;AAC7B,WAAS,KAAK,QAAQ,MAAM;AAE5B,QAAM,QAAQ,SAAS,cAAc,KAAK;AAC1C,QAAM,YAAY;AAClB,QAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQlB,WAAS,KAAK,YAAY,KAAK;AAE/B,QAAM,aAAa,MAAM,cAAiC,eAAe;AACzE,QAAM,iBAAiB,MAAM,cAAiC,mBAAmB;AACjF,QAAM,iBAAiB,MAAM,cAAiC,oBAAoB;AAClF,QAAM,SAAS,MAAM,cAA2B,aAAa;AAC7D,MAAI,aAAuC;AAE3C,WAAS,iBAAiB,eAAe,EAAE,QAAQ,CAAC,OAAO,GAAG,UAAU,IAAI,YAAY,CAAC;AACzF,eAAa,OAAO,YAAY,OAAO,UAAU;AACjD,uBAAqB,MAAM;AAE3B,QAAM,gBAAgB,WAAW,UAAU,SAAS,QAAQ,GAAG,OAAO,gBAAgB;AAEtF,QAAM,YAAY,OAAc;AAAA,IAC9B,SAAS;AAAA,IACT,MAAM,OAAO,SAAS;AAAA,IACtB,KAAK,OAAO,SAAS;AAAA,IACrB,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC,SAAS,oBAAoB,OAAO,gBAAgB;AAAA,EACtD;AAEA,QAAM,WAAW,CAAC,YAAqB;AACrC,UAAM,QAAQ,eAAe,OAAO,gBAAgB,EAAE;AACtD,QAAI,WAAY,YAAW,cAAc,QAAQ,IAAI,2BAAoB,KAAK,MAAM;AACpF,QAAI,gBAAgB;AAClB,qBAAe,SAAS,CAAC;AACzB,qBAAe,cAAc,aAAa,QAAQ,WAAW,YAAY,MAAM,KAAK;AAAA,IACtF;AACA,QAAI,QAAQ;AACV,aAAO,cACL,WACA,cAAc,WAAW,CAAC,WAAM,KAAK,UAAU,UAAU,IAAI,KAAK,GAAG;AAAA,IACzE;AAAA,EACF;AAEA,QAAM,eAAe,MAAM;AACzB,UAAM,QAAQ,UAAU;AACxB,UAAM,QAAQ,OAAO,aAAa,SAAS,UAAU,SAAS,UAAU,KAAK;AAC7E,aAAS,SAAS,OAAO,aAAa,QAAQ,SAAY,+DAAgD;AAC1G,WAAO;AAAA,EACT;AAEA,QAAM,gBAAgB,MAAM;AAC1B,UAAM,gBAAgB,SAAS;AAC/B,QAAI,EAAE,yBAAyB,aAAc,QAAO;AACpD,QAAI,CAAC,cAAc,qBAAqB,cAAc,oBAAoB,OAAQ,QAAO;AAEzF,kBAAc,KAAK;AACnB,WAAO,aAAa,GAAG,gBAAgB;AACvC,iBAAa;AACb,WAAO;AAAA,EACT;AAEA,QAAM,wBAAwB,CAAC,UAAyB;AACtD,QAAI,MAAM,QAAQ,SAAU;AAC5B,QAAI,CAAC,cAAc,EAAG;AAEtB,UAAM,eAAe;AACrB,UAAM,gBAAgB;AAAA,EACxB;AAEA,QAAM,sBAAsB,CAAC,UAAsB;AACjD,UAAM,SAAS,MAAM;AACrB,QAAI,EAAE,kBAAkB,SAAU;AAElC,UAAM,SAAS,OAAO,QAA2B,SAAS;AAC1D,QAAI,UAAU,CAAC,OAAO,QAAQ,YAAY,KAAK,CAAC,OAAO,QAAQ,kBAAkB,GAAG;AAClF,mBAAa;AACb,eAAS,qFAA2E;AAEpF,UAAI,MAAM,WAAW,MAAM,WAAW,MAAM,QAAQ;AAClD,qBAAa;AACb;AAAA,MACF;AAEA,YAAM,eAAe;AACrB;AAAA,IACF;AAEA,UAAM,SAAS,OAAO,QAAQ,QAAQ;AACtC,QAAI,UAAU,CAAC,OAAO,QAAQ,YAAY,KAAK,CAAC,OAAO,QAAQ,kBAAkB,GAAG;AAClF,YAAM,eAAe;AACrB,YAAM,yBAAyB;AAAA,IACjC;AAAA,EACF;AAEA,QAAM,gBAAgB,CAAC,UAAsB;AAC3C,UAAM,SAAS,MAAM;AACrB,QAAI,EAAE,kBAAkB,SAAU;AAElC,iBAAa,OAAO,QAA2B,SAAS;AACxD,aAAS,aAAa,oEAA+D,MAAS;AAAA,EAChG;AAEA,QAAM,cAAc,MAAM,aAAa;AACvC,QAAM,iBAAiB,MAAM,aAAa;AAC1C,QAAM,yBAAyB,MAAM;AACnC,QAAI,SAAS,oBAAoB,SAAU,cAAa;AAAA,EAC1D;AAEA,WAAS,iBAAiB,WAAW,qBAAqB;AAC1D,WAAS,iBAAiB,SAAS,qBAAqB,IAAI;AAC5D,WAAS,iBAAiB,WAAW,aAAa;AAClD,WAAS,iBAAiB,SAAS,WAAW;AAC9C,SAAO,iBAAiB,YAAY,cAAc;AAClD,WAAS,iBAAiB,oBAAoB,sBAAsB;AAEpE,cAAY,iBAAiB,SAAS,MAAM;AAC1C,UAAM,QAAQ,aAAa;AAC3B,UAAM,UAAU,aAAa,MAAM,OAAO;AAC1C,YAAQ,SAAS,OAAO;AAExB,UAAM,cAAc,QAAQ,aAAa,QAAQ,WAAW,OAAO,IAAI;AACvE,UAAM,OAAO,KAAK,UAAU,aAAa,MAAM,CAAC;AAChD,UAAM,QAAQ,iBAAY,MAAM,QAAQ,MAAM,UAAU,MAAM,QAAQ,WAAW,IAAI,MAAM,EAAE;AAE7F,QAAI,UAAU,WAAW,WAAW;AAClC,gBAAU,UAAU,UAAU,IAAI,EAAE,KAAK,MAAM,UAAU,KAAK,CAAC,EAAE,MAAM,MAAM,aAAa,MAAM,KAAK,CAAC;AAAA,IACxG,OAAO;AACL,mBAAa,MAAM,KAAK;AAAA,IAC1B;AAAA,EACF,CAAC;AAED,kBAAgB,iBAAiB,SAAS,MAAM;AAC9C,UAAM,QAAQ,aAAa;AAC3B,UAAM,UAAU,aAAa,MAAM,OAAO;AAC1C,UAAM,QAAO,oBAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AACjD,iBAAa,aAAa,OAAO,SAAS,QAAQ,IAAI,IAAI,SAAS,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AACnG,cAAU,0BAAqB;AAAA,EACjC,CAAC;AAED,kBAAgB,iBAAiB,SAAS,MAAM;AAC9C,QAAI,CAAC,YAAY,KAAM;AAEvB,iBAAa;AACb,WAAO,SAAS,OAAO,WAAW;AAAA,EACpC,CAAC;AAED,aAAW,iBAAiB,SAAS,MAAM;AACzC,iBAAa;AACb,WAAO,eAAe,WAAW,OAAO,UAAU;AAClD,UAAM,MAAM,IAAI,IAAI,OAAO,SAAS,IAAI;AACxC,QAAI,aAAa,OAAO,OAAO,UAAU;AACzC,WAAO,SAAS,OAAO,IAAI,SAAS;AAAA,EACtC,CAAC;AAED,eAAa;AACb,MAAI,gBAAgB,EAAG,UAAS,YAAY,aAAa,cAAc,kBAAkB,IAAI,KAAK,GAAG,sBAAiB;AAEtH,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,YAAY,MAAM,eAAe,OAAO,gBAAgB;AAAA,IACxD,SAAS,MAAM;AACb,mBAAa;AACb,eAAS,oBAAoB,WAAW,qBAAqB;AAC7D,eAAS,oBAAoB,SAAS,qBAAqB,IAAI;AAC/D,eAAS,oBAAoB,WAAW,aAAa;AACrD,eAAS,oBAAoB,SAAS,WAAW;AACjD,aAAO,oBAAoB,YAAY,cAAc;AACrD,eAAS,oBAAoB,oBAAoB,sBAAsB;AACvE,aAAO,OAAO;AACd,YAAM,OAAO;AACb,YAAM,OAAO;AACb,eAAS,iBAA8B,OAAO,gBAAgB,EAAE,QAAQ,CAAC,OAAO;AAC9E,YAAI,GAAG,QAAQ,iBAAiB,QAAW;AACzC,aAAG,kBAAkB;AACrB,iBAAO,GAAG,QAAQ;AAClB,iBAAO,GAAG,QAAQ;AAAA,QACpB;AAAA,MACF,CAAC;AACD,aAAO,6BAA6B;AAAA,IACtC;AAAA,EACF;AACF;","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -21,6 +21,10 @@ type EditModeOptions = {
|
|
|
21
21
|
queryValue?: string | null;
|
|
22
22
|
/** Session storage key used to keep edit mode active across pages. */
|
|
23
23
|
sessionKey?: string;
|
|
24
|
+
/** Local/session storage key prefix used for per-page draft autosaves. */
|
|
25
|
+
storageKey?: string;
|
|
26
|
+
/** Persist drafts after every edit and before page navigation. Defaults to true. */
|
|
27
|
+
autoSave?: boolean;
|
|
24
28
|
/** User-facing label in the banner. */
|
|
25
29
|
brandName?: string;
|
|
26
30
|
/** Main UI colour. */
|
package/dist/index.d.ts
CHANGED
|
@@ -21,6 +21,10 @@ type EditModeOptions = {
|
|
|
21
21
|
queryValue?: string | null;
|
|
22
22
|
/** Session storage key used to keep edit mode active across pages. */
|
|
23
23
|
sessionKey?: string;
|
|
24
|
+
/** Local/session storage key prefix used for per-page draft autosaves. */
|
|
25
|
+
storageKey?: string;
|
|
26
|
+
/** Persist drafts after every edit and before page navigation. Defaults to true. */
|
|
27
|
+
autoSave?: boolean;
|
|
24
28
|
/** User-facing label in the banner. */
|
|
25
29
|
brandName?: string;
|
|
26
30
|
/** Main UI colour. */
|