hyperclayjs 1.29.1 → 1.31.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 +55 -27
- package/package.json +1 -1
- package/src/communication/live-sync.js +14 -3
- package/src/communication/sendMessage.js +1 -1
- package/src/communication/uploadFile.js +1 -1
- package/src/core/savePageCore.js +1 -1
- package/src/core/snapshot.js +7 -0
- package/src/data.js +10 -0
- package/src/hyperclay.js +51 -3
- package/src/ui/quickcrop.js +403 -0
- package/src/utilities/extension-noise.js +63 -0
- package/src/utilities/mutation.js +19 -0
- package/src/vendor/hyper-html-api.vendor.js +14 -0
- package/src/vendor/hyper-undo.vendor.js +1 -1
- package/src/vendor/hypercms.vendor.js +1040 -220
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
/*! quickcrop v1.0.0 | MIT | https://github.com/panphora/quickcrop
|
|
2
|
+
*
|
|
3
|
+
* const result = await quickcrop(file, { aspect: 1 });
|
|
4
|
+
* // result: { blob, dataURL, width, height } on confirm, null on cancel
|
|
5
|
+
*
|
|
6
|
+
* Uses your modal system (themodal, or any adapter) or brings its own.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const CSS = `:root{--qc-surface:#f7f2ea;--qc-surface-hover:#efe7d8;--qc-border:#cdbfa6;--qc-text:#2b241b;--qc-text-hover:#463c2e;--qc-on-dark:#efe7d8;--qc-overlay:rgba(43,36,27,.6);--qc-dim:rgba(0,0,0,.55);--qc-radius:8px;--qc-crop-line:#fff;--qc-font:ui-sans-serif,system-ui,sans-serif;}.qc-css-probe{position:absolute;}.qc-backdrop{position:fixed;inset:0;background:var(--qc-overlay);display:grid;place-items:center;z-index:1000;animation:qc-fade .15s ease;}@keyframes qc-fade{from{opacity:0}}.qc-modal{background:var(--qc-surface);border:1px solid var(--qc-border);border-radius:var(--qc-radius);padding:16px;max-width:92vw;font-family:var(--qc-font);}.qc-bar{display:flex;justify-content:flex-end;gap:10px;margin-top:12px;}.qc-btn{font:inherit;font-family:var(--qc-font);font-size:.85rem;padding:.3rem .75rem;border:1px solid var(--qc-border);border-radius:6px;background:var(--qc-surface);color:var(--qc-text);cursor:pointer;}.qc-btn:hover{background:var(--qc-surface-hover);}.qc-btn-primary{background:var(--qc-text);border-color:var(--qc-text);color:var(--qc-on-dark);font-weight:600;}.qc-btn-primary:hover{background:var(--qc-text-hover);border-color:var(--qc-text-hover);}.qc-stage{position:relative;line-height:0;user-select:none;-webkit-user-select:none;touch-action:none;}.qc-stage img{display:block;max-width:100%;pointer-events:none;}.qc-dim{position:absolute;inset:0;background:var(--qc-dim);pointer-events:none;}.qc-box{position:absolute;cursor:move;outline:1px solid var(--qc-crop-line);background:linear-gradient(var(--qc-crop-line),var(--qc-crop-line)) 0 33.33%/100% 1px no-repeat,linear-gradient(var(--qc-crop-line),var(--qc-crop-line)) 0 66.66%/100% 1px no-repeat,linear-gradient(var(--qc-crop-line),var(--qc-crop-line)) 33.33% 0/1px 100% no-repeat,linear-gradient(var(--qc-crop-line),var(--qc-crop-line)) 66.66% 0/1px 100% no-repeat;background-blend-mode:overlay;}.qc-handle{position:absolute;box-sizing:border-box;width:28px;height:28px;border:7px solid transparent;background:var(--qc-crop-line);background-clip:padding-box;border-radius:9px;}.qc-nw{left:-14px;top:-14px;cursor:nwse-resize;}.qc-ne{right:-14px;top:-14px;cursor:nesw-resize;}.qc-sw{left:-14px;bottom:-14px;cursor:nesw-resize;}.qc-se{right:-14px;bottom:-14px;cursor:nwse-resize;}.qc-mount{line-height:0;}`;
|
|
10
|
+
|
|
11
|
+
const ENCODABLE = ['image/jpeg', 'image/png', 'image/webp'];
|
|
12
|
+
|
|
13
|
+
// iOS Safari silently produces a blank bitmap above ~16.7M canvas pixels
|
|
14
|
+
// (getContext still succeeds, toBlob still returns a blob), so cap the
|
|
15
|
+
// output area and downscale instead.
|
|
16
|
+
const MAX_AREA = 16777216;
|
|
17
|
+
|
|
18
|
+
let active = false;
|
|
19
|
+
|
|
20
|
+
function el(tag, cls = '', text = '') {
|
|
21
|
+
const n = document.createElement(tag);
|
|
22
|
+
if (cls) n.className = cls;
|
|
23
|
+
if (text) n.textContent = text;
|
|
24
|
+
return n;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function escapeHtml(str) {
|
|
28
|
+
return String(str).replace(/[&<>"']/g, c => ({
|
|
29
|
+
'&': '&', '<': '<', '>': '>', '"': '"', "'": ''',
|
|
30
|
+
}[c]));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function ensureStyles() {
|
|
34
|
+
if (document.querySelector('style[data-quickcrop]')) return;
|
|
35
|
+
const probe = el('div', 'qc-css-probe');
|
|
36
|
+
probe.style.display = 'none';
|
|
37
|
+
document.body.append(probe);
|
|
38
|
+
const linked = getComputedStyle(probe).position === 'absolute';
|
|
39
|
+
probe.remove();
|
|
40
|
+
if (linked) return;
|
|
41
|
+
const style = document.createElement('style');
|
|
42
|
+
style.setAttribute('data-quickcrop', '');
|
|
43
|
+
style.setAttribute('save-remove', '');
|
|
44
|
+
style.setAttribute('snapshot-remove', '');
|
|
45
|
+
style.textContent = CSS;
|
|
46
|
+
document.head.append(style);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Modal adapters
|
|
51
|
+
//
|
|
52
|
+
// An adapter normalizes any modal system to:
|
|
53
|
+
// open({ content, confirmLabel, onConfirm, onCancel }) -> { close() }
|
|
54
|
+
// fit?() -> { width, height } optional: max content size the modal can host
|
|
55
|
+
// Dismissal affordances (Esc, backdrop, a close button) are the adapter's
|
|
56
|
+
// concern. onConfirm/onCancel must only fire from user interaction, never
|
|
57
|
+
// during open().
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
|
|
60
|
+
const builtinAdapter = {
|
|
61
|
+
open({ content, confirmLabel, onConfirm, onCancel }) {
|
|
62
|
+
ensureStyles();
|
|
63
|
+
const backdrop = el('div', 'qc-backdrop');
|
|
64
|
+
backdrop.setAttribute('save-remove', '');
|
|
65
|
+
backdrop.setAttribute('snapshot-remove', '');
|
|
66
|
+
const modal = el('div', 'qc-modal');
|
|
67
|
+
const bar = el('div', 'qc-bar');
|
|
68
|
+
const confirmBtn = el('button', 'qc-btn qc-btn-primary', confirmLabel);
|
|
69
|
+
confirmBtn.type = 'button';
|
|
70
|
+
bar.append(confirmBtn);
|
|
71
|
+
modal.append(content, bar);
|
|
72
|
+
backdrop.append(modal);
|
|
73
|
+
document.body.append(backdrop);
|
|
74
|
+
|
|
75
|
+
const onKey = e => { if (e.key === 'Escape') onCancel(); };
|
|
76
|
+
document.addEventListener('keydown', onKey);
|
|
77
|
+
confirmBtn.onclick = () => onConfirm();
|
|
78
|
+
backdrop.onclick = e => { if (e.target === backdrop) onCancel(); };
|
|
79
|
+
confirmBtn.focus();
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
close() {
|
|
83
|
+
document.removeEventListener('keydown', onKey);
|
|
84
|
+
backdrop.remove();
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
function isThemodal(m) {
|
|
91
|
+
return !!m && typeof m === 'object' &&
|
|
92
|
+
typeof m.open === 'function' &&
|
|
93
|
+
typeof m.close === 'function' &&
|
|
94
|
+
typeof m.onYes === 'function' &&
|
|
95
|
+
'html' in m;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const THEMODAL_CLOSE_X = '<svg viewBox="0 0 134 134" xmlns="http://www.w3.org/2000/svg">' +
|
|
99
|
+
'<path class="micromodal__close-bg" d="M132 132.5 1 1.5h131v131Z"/>' +
|
|
100
|
+
'<path d="M83 27l24 24M107 27L83 51" stroke="#fff" stroke-width="6"/></svg>';
|
|
101
|
+
|
|
102
|
+
function wrapThemodal(tm) {
|
|
103
|
+
// an already-open themodal dialog is a singleton collision: opening again
|
|
104
|
+
// would cross-fire that dialog's callbacks, so fall back to the built-in modal
|
|
105
|
+
const busy = () => tm.isShowing && !!document.querySelector('.micromodal-parent');
|
|
106
|
+
return {
|
|
107
|
+
// themodal's container is min(550px, 100vw - 2rem) with a 2px border and
|
|
108
|
+
// 40px/64px side padding; leave room for the button row below the stage
|
|
109
|
+
fit() {
|
|
110
|
+
if (busy()) return null;
|
|
111
|
+
const pad = window.innerWidth >= 640 ? 128 : 80;
|
|
112
|
+
return {
|
|
113
|
+
width: Math.max(120, Math.min(550, window.innerWidth - 32) - 4 - pad),
|
|
114
|
+
height: Math.max(120, window.innerHeight - 220),
|
|
115
|
+
};
|
|
116
|
+
},
|
|
117
|
+
open(opts) {
|
|
118
|
+
if (busy()) return builtinAdapter.open(opts);
|
|
119
|
+
const { content, confirmLabel, onConfirm, onCancel } = opts;
|
|
120
|
+
ensureStyles();
|
|
121
|
+
let done = false; // true once themodal is closing through its own paths
|
|
122
|
+
const finish = fn => {
|
|
123
|
+
if (done) return;
|
|
124
|
+
done = true;
|
|
125
|
+
document.removeEventListener('keydown', onKey, true);
|
|
126
|
+
fn();
|
|
127
|
+
};
|
|
128
|
+
// themodal's Esc goes straight through MicroModal without firing onNo,
|
|
129
|
+
// so intercept it in capture and run the cancel path ourselves
|
|
130
|
+
const onKey = e => {
|
|
131
|
+
if (e.key !== 'Escape') return;
|
|
132
|
+
e.stopPropagation();
|
|
133
|
+
onCancel();
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
tm.html = '<div class="qc-mount"></div>';
|
|
137
|
+
tm.yes = escapeHtml(confirmLabel);
|
|
138
|
+
tm.no = ''; // no cancel button; dismissal is the close X, backdrop, or Esc
|
|
139
|
+
tm.closeHtml = THEMODAL_CLOSE_X;
|
|
140
|
+
tm.disableFocus = true; // themodal would focus the cancel button; focus confirm instead
|
|
141
|
+
tm.onOpen(() => {
|
|
142
|
+
document.querySelector('.micromodal__content .qc-mount')?.append(content);
|
|
143
|
+
document.querySelector('.micromodal-parent .micromodal__yes')?.focus();
|
|
144
|
+
document.addEventListener('keydown', onKey, true);
|
|
145
|
+
});
|
|
146
|
+
tm.onYes(() => { finish(onConfirm); return true; });
|
|
147
|
+
tm.onNo(() => { finish(onCancel); });
|
|
148
|
+
tm.open();
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
close() {
|
|
152
|
+
if (done) return;
|
|
153
|
+
done = true;
|
|
154
|
+
document.removeEventListener('keydown', onKey, true);
|
|
155
|
+
tm.close();
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function resolveAdapter(modal) {
|
|
163
|
+
if (modal === 'builtin') return builtinAdapter;
|
|
164
|
+
if (modal && typeof modal === 'object') {
|
|
165
|
+
if (isThemodal(modal)) return wrapThemodal(modal);
|
|
166
|
+
if (typeof modal.open === 'function') return modal;
|
|
167
|
+
throw new TypeError('quickcrop: modal must be "auto", "builtin", a themodal instance, or an adapter with open()');
|
|
168
|
+
}
|
|
169
|
+
if (modal === 'auto') {
|
|
170
|
+
return isThemodal(window.themodal) ? wrapThemodal(window.themodal) : builtinAdapter;
|
|
171
|
+
}
|
|
172
|
+
throw new TypeError('quickcrop: modal must be "auto", "builtin", a themodal instance, or an adapter with open()');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
// quickcrop(file, options) -> Promise<{ blob, dataURL, width, height } | null>
|
|
177
|
+
// ---------------------------------------------------------------------------
|
|
178
|
+
|
|
179
|
+
function quickcrop(file, options = {}) {
|
|
180
|
+
const {
|
|
181
|
+
aspect = null,
|
|
182
|
+
type,
|
|
183
|
+
quality = 0.92,
|
|
184
|
+
maxWidth = null,
|
|
185
|
+
maxHeight = null,
|
|
186
|
+
minSize = 40,
|
|
187
|
+
labels = {},
|
|
188
|
+
modal = 'auto',
|
|
189
|
+
} = options;
|
|
190
|
+
const { confirm: confirmLabel = 'Crop' } = labels;
|
|
191
|
+
|
|
192
|
+
if (!(file instanceof Blob)) {
|
|
193
|
+
return Promise.reject(new TypeError('quickcrop: expected a File or Blob'));
|
|
194
|
+
}
|
|
195
|
+
if (active) {
|
|
196
|
+
return Promise.reject(new Error('quickcrop: already open'));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
let adapter;
|
|
200
|
+
try {
|
|
201
|
+
adapter = resolveAdapter(modal);
|
|
202
|
+
} catch (err) {
|
|
203
|
+
return Promise.reject(err);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
active = true;
|
|
207
|
+
const outType = type || (ENCODABLE.includes(file.type) ? file.type : 'image/png');
|
|
208
|
+
const url = URL.createObjectURL(file);
|
|
209
|
+
const img = new Image();
|
|
210
|
+
|
|
211
|
+
return new Promise((resolve, reject) => {
|
|
212
|
+
img.onerror = () => {
|
|
213
|
+
URL.revokeObjectURL(url);
|
|
214
|
+
active = false;
|
|
215
|
+
reject(new Error('quickcrop: could not decode image'));
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
function mount() {
|
|
219
|
+
// Fit the image inside the host for display. The crop math works in
|
|
220
|
+
// these *display* pixels, then scales back up to the source resolution.
|
|
221
|
+
const fit = adapter.fit ? adapter.fit() : null;
|
|
222
|
+
const fitW = Math.max(1, Math.min(fit?.width ?? window.innerWidth * 0.86, img.naturalWidth));
|
|
223
|
+
const fitH = Math.max(1, fit?.height ?? window.innerHeight * 0.72);
|
|
224
|
+
const scale = Math.min(fitW / img.naturalWidth, fitH / img.naturalHeight, 1);
|
|
225
|
+
const dispW = Math.max(1, Math.round(img.naturalWidth * scale));
|
|
226
|
+
const dispH = Math.max(1, Math.round(img.naturalHeight * scale));
|
|
227
|
+
const min = Math.min(minSize, dispW, dispH);
|
|
228
|
+
|
|
229
|
+
ensureStyles();
|
|
230
|
+
|
|
231
|
+
const stage = el('div', 'qc-stage');
|
|
232
|
+
img.style.width = dispW + 'px';
|
|
233
|
+
img.style.height = dispH + 'px';
|
|
234
|
+
const dim = el('div', 'qc-dim');
|
|
235
|
+
const box = el('div', 'qc-box');
|
|
236
|
+
for (const c of ['nw', 'ne', 'sw', 'se']) box.appendChild(el('div', 'qc-handle qc-' + c));
|
|
237
|
+
stage.append(img, dim, box);
|
|
238
|
+
|
|
239
|
+
// initial crop box: locked aspect -> largest centered rect; free -> whole image
|
|
240
|
+
let w, h;
|
|
241
|
+
if (aspect) {
|
|
242
|
+
w = Math.min(dispW, dispH * aspect);
|
|
243
|
+
h = w / aspect;
|
|
244
|
+
if (h > dispH) { h = dispH; w = h * aspect; }
|
|
245
|
+
} else {
|
|
246
|
+
w = dispW;
|
|
247
|
+
h = dispH;
|
|
248
|
+
}
|
|
249
|
+
let x = (dispW - w) / 2;
|
|
250
|
+
let y = (dispH - h) / 2;
|
|
251
|
+
render();
|
|
252
|
+
|
|
253
|
+
function render() {
|
|
254
|
+
box.style.left = x + 'px';
|
|
255
|
+
box.style.top = y + 'px';
|
|
256
|
+
box.style.width = w + 'px';
|
|
257
|
+
box.style.height = h + 'px';
|
|
258
|
+
// dim the image outside the box: one donut polygon. The outer ring winds
|
|
259
|
+
// clockwise and the inner counter-clockwise, so the default nonzero fill
|
|
260
|
+
// rule punches the hole without the (less supported) evenodd keyword.
|
|
261
|
+
dim.style.clipPath = `polygon(0 0, 100% 0, 100% 100%, 0 100%, 0 0, ` +
|
|
262
|
+
`${x}px ${y}px, ${x}px ${y + h}px, ${x + w}px ${y + h}px, ${x + w}px ${y}px, ${x}px ${y}px)`;
|
|
263
|
+
}
|
|
264
|
+
const clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
|
265
|
+
|
|
266
|
+
// one pointer handler drives both moving and resizing
|
|
267
|
+
stage.addEventListener('pointerdown', e => {
|
|
268
|
+
const handle = e.target.closest('.qc-handle');
|
|
269
|
+
const onBox = e.target === box;
|
|
270
|
+
if (!handle && !onBox) return;
|
|
271
|
+
e.preventDefault();
|
|
272
|
+
stage.setPointerCapture(e.pointerId);
|
|
273
|
+
|
|
274
|
+
const start = { px: e.clientX, py: e.clientY, x, y, w, h };
|
|
275
|
+
const corner = handle && [...handle.classList].find(c => /^qc-(nw|ne|sw|se)$/.test(c))?.slice(3);
|
|
276
|
+
|
|
277
|
+
const move = ev => {
|
|
278
|
+
if (!corner) { // --- drag to move ---
|
|
279
|
+
x = clamp(start.x + (ev.clientX - start.px), 0, dispW - w);
|
|
280
|
+
y = clamp(start.y + (ev.clientY - start.py), 0, dispH - h);
|
|
281
|
+
} else { // --- drag to resize ---
|
|
282
|
+
// Anchor = the corner diagonally opposite the one being dragged; it
|
|
283
|
+
// stays pinned while the dragged corner follows the pointer.
|
|
284
|
+
const ax = corner.includes('w') ? start.x + start.w : start.x;
|
|
285
|
+
const ay = corner.includes('n') ? start.y + start.h : start.y;
|
|
286
|
+
const goingE = corner.includes('e'), goingS = corner.includes('s');
|
|
287
|
+
const rect = stage.getBoundingClientRect();
|
|
288
|
+
const dx = Math.abs(ev.clientX - (rect.left + ax));
|
|
289
|
+
const dy = Math.abs(ev.clientY - (rect.top + ay));
|
|
290
|
+
|
|
291
|
+
if (aspect) {
|
|
292
|
+
// room before an image edge, in width units, so the lock holds in both axes
|
|
293
|
+
const roomW = Math.min(goingE ? dispW - ax : ax, (goingS ? dispH - ay : ay) * aspect);
|
|
294
|
+
const minW = Math.min(Math.max(min, min * aspect), roomW);
|
|
295
|
+
w = clamp(Math.max(dx, dy * aspect), minW, roomW);
|
|
296
|
+
h = w / aspect;
|
|
297
|
+
} else {
|
|
298
|
+
w = clamp(dx, min, goingE ? dispW - ax : ax);
|
|
299
|
+
h = clamp(dy, min, goingS ? dispH - ay : ay);
|
|
300
|
+
}
|
|
301
|
+
x = goingE ? ax : ax - w;
|
|
302
|
+
y = goingS ? ay : ay - h;
|
|
303
|
+
}
|
|
304
|
+
render();
|
|
305
|
+
};
|
|
306
|
+
const up = () => {
|
|
307
|
+
stage.removeEventListener('pointermove', move);
|
|
308
|
+
stage.removeEventListener('pointerup', up);
|
|
309
|
+
stage.removeEventListener('pointercancel', up);
|
|
310
|
+
};
|
|
311
|
+
stage.addEventListener('pointermove', move);
|
|
312
|
+
stage.addEventListener('pointerup', up);
|
|
313
|
+
stage.addEventListener('pointercancel', up);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
function renderCrop() {
|
|
317
|
+
// display -> source scale, per axis: dispW/dispH round independently,
|
|
318
|
+
// so a shared scale would distort or overrun the source on one axis
|
|
319
|
+
const sx = img.naturalWidth / dispW;
|
|
320
|
+
const sy = img.naturalHeight / dispH;
|
|
321
|
+
const outW = Math.round(w * sx);
|
|
322
|
+
const outH = Math.round(h * sy);
|
|
323
|
+
let shrink = 1;
|
|
324
|
+
if (maxWidth && outW > maxWidth) shrink = maxWidth / outW;
|
|
325
|
+
if (maxHeight && outH * shrink > maxHeight) shrink = maxHeight / outH;
|
|
326
|
+
if (outW * shrink * outH * shrink > MAX_AREA) shrink = Math.sqrt(MAX_AREA / (outW * outH));
|
|
327
|
+
const canvas = document.createElement('canvas');
|
|
328
|
+
canvas.width = Math.max(1, Math.round(outW * shrink));
|
|
329
|
+
canvas.height = Math.max(1, Math.round(outH * shrink));
|
|
330
|
+
const ctx = canvas.getContext('2d');
|
|
331
|
+
if (!ctx) throw new Error('quickcrop: crop too large for a canvas');
|
|
332
|
+
if (outType === 'image/jpeg') { // jpeg has no alpha; avoid black fill
|
|
333
|
+
ctx.fillStyle = '#fff';
|
|
334
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
335
|
+
}
|
|
336
|
+
ctx.imageSmoothingQuality = 'high';
|
|
337
|
+
ctx.drawImage(
|
|
338
|
+
img, x * sx, y * sy, w * sx, h * sy, // source rect
|
|
339
|
+
0, 0, canvas.width, canvas.height // dest rect
|
|
340
|
+
);
|
|
341
|
+
return canvas;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
let settled = false;
|
|
345
|
+
const finishOnce = () => {
|
|
346
|
+
if (settled) return false;
|
|
347
|
+
settled = true;
|
|
348
|
+
host.close();
|
|
349
|
+
URL.revokeObjectURL(url);
|
|
350
|
+
active = false;
|
|
351
|
+
return true;
|
|
352
|
+
};
|
|
353
|
+
const host = adapter.open({
|
|
354
|
+
content: stage,
|
|
355
|
+
confirmLabel,
|
|
356
|
+
onConfirm() {
|
|
357
|
+
if (settled) return;
|
|
358
|
+
let canvas, dataURL;
|
|
359
|
+
try {
|
|
360
|
+
canvas = renderCrop();
|
|
361
|
+
dataURL = canvas.toDataURL(outType, quality);
|
|
362
|
+
} catch (err) {
|
|
363
|
+
if (finishOnce()) reject(err);
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
if (!finishOnce()) return;
|
|
367
|
+
canvas.toBlob(blob => {
|
|
368
|
+
if (!blob) {
|
|
369
|
+
reject(new Error('quickcrop: image encoding failed'));
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
resolve({ blob, dataURL, width: canvas.width, height: canvas.height });
|
|
373
|
+
}, outType, quality);
|
|
374
|
+
},
|
|
375
|
+
onCancel() {
|
|
376
|
+
if (finishOnce()) resolve(null);
|
|
377
|
+
},
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// an adapter that throws during fit()/open() must not strand the cropper
|
|
382
|
+
img.onload = () => {
|
|
383
|
+
try {
|
|
384
|
+
mount();
|
|
385
|
+
} catch (err) {
|
|
386
|
+
URL.revokeObjectURL(url);
|
|
387
|
+
active = false;
|
|
388
|
+
reject(err);
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
img.src = url;
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// __QC_EXPORT_START__
|
|
396
|
+
if (!window.__hyperclayNoAutoExport) {
|
|
397
|
+
window.quickcrop = quickcrop;
|
|
398
|
+
window.hyperclay = window.hyperclay || {};
|
|
399
|
+
window.hyperclay.quickcrop = quickcrop;
|
|
400
|
+
window.h = window.hyperclay;
|
|
401
|
+
}
|
|
402
|
+
// __QC_EXPORT_END__
|
|
403
|
+
export default quickcrop;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* extension-noise.js — browser-extension DOM noise the page should never own.
|
|
3
|
+
*
|
|
4
|
+
* Extensions touch the live page in two distinct ways, handled differently so a
|
|
5
|
+
* save never loses real content:
|
|
6
|
+
* 1. Standalone elements they own (password-manager menus, Grammarly overlays,
|
|
7
|
+
* translate banners, extension iframes/scripts) — remove them entirely.
|
|
8
|
+
* 2. Marker attributes they stamp onto YOUR real inputs/forms — strip just the
|
|
9
|
+
* attribute and keep the element.
|
|
10
|
+
*
|
|
11
|
+
* The selectors are not stable public APIs; extend both lists from real captures.
|
|
12
|
+
* Keep this file byte-identical to the copy in the other package (hyperclayjs and
|
|
13
|
+
* hyper-undo each carry one) — their save/undo observers must agree, or one will
|
|
14
|
+
* record/save what the other ignores.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// Standalone elements browser extensions inject into the page — remove them entirely.
|
|
18
|
+
export const EXTENSION_NODE_SELECTORS = [
|
|
19
|
+
'[src^="chrome-extension://"]', '[href^="chrome-extension://"]',
|
|
20
|
+
'[src^="moz-extension://"]', '[href^="moz-extension://"]',
|
|
21
|
+
'[src^="safari-web-extension://"]', '[href^="safari-web-extension://"]',
|
|
22
|
+
'[id="1p-menu-live-region"]',
|
|
23
|
+
'com-1password-button', 'com-1password-menu', 'com-1password-notification',
|
|
24
|
+
'grammarly-extension', 'grammarly-desktop-integration',
|
|
25
|
+
'lt-div', 'lt-card', 'lt-toolbar', 'lt-highlighter',
|
|
26
|
+
'[data-lastpass-root]', '[data-lastpass-icon-root]', '[data-dashlane-shadowhost]',
|
|
27
|
+
'[data-klarna-extension]', '[class*="klarna-extension"]',
|
|
28
|
+
'.skiptranslate', '#goog-gt-tt', '.goog-te-banner-frame', '.goog-te-balloon-frame',
|
|
29
|
+
'style.darkreader', 'style[data-darkreader-mode]', 'style[data-darkreader-scheme]',
|
|
30
|
+
'#loom-companion-mv3', 'loom-container',
|
|
31
|
+
'[ext-id]',
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
// Attribute-name prefixes extensions stamp onto YOUR real elements — strip the attribute, keep the element.
|
|
35
|
+
export const EXTENSION_ATTR_PREFIXES = [
|
|
36
|
+
'data-grammarly', 'data-lt', 'data-1p', 'data-com-onepassword', 'data-lastpass', 'data-lp', 'data-kwimpala',
|
|
37
|
+
'data-bitwarden', 'data-bw', 'data-dashlane', 'data-np', 'data-protonpass', 'data-proton-pass',
|
|
38
|
+
'data-keeper', 'data-rf', 'data-roboform', 'data-honey', 'data-rakuten', 'data-capital-one',
|
|
39
|
+
'data-wikibuy', 'data-klarna', 'data-darkreader', 'data-evernote', 'data-pocket', 'data-pin',
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
// Pre-joined once so hot paths call matches()/closest()/querySelectorAll() without re-joining.
|
|
43
|
+
export const EXTENSION_NODE_SELECTOR = EXTENSION_NODE_SELECTORS.join(',')
|
|
44
|
+
|
|
45
|
+
// One compiled test for "is this an extension marker attribute". The (?:-|$) boundary means a
|
|
46
|
+
// prefix like `data-lt` matches `data-lt` / `data-lt-foo` but never an app attribute like `data-ltr`.
|
|
47
|
+
export const EXTENSION_ATTR_PATTERN = new RegExp('^(?:' + EXTENSION_ATTR_PREFIXES.join('|') + ')(?:-|$)')
|
|
48
|
+
|
|
49
|
+
// Remove extension-injected elements and strip extension marker attributes from a (cloned) subtree, in place.
|
|
50
|
+
// Guarded so a malformed denylist entry degrades to "noise not stripped" rather than breaking the save.
|
|
51
|
+
export function stripExtensionNoise(root) {
|
|
52
|
+
if (!root || !root.querySelectorAll) return
|
|
53
|
+
try {
|
|
54
|
+
for (const el of root.querySelectorAll(EXTENSION_NODE_SELECTOR)) el.remove()
|
|
55
|
+
for (const el of root.querySelectorAll('*')) {
|
|
56
|
+
for (const attr of [...el.attributes]) {
|
|
57
|
+
if (EXTENSION_ATTR_PATTERN.test(attr.name.toLowerCase())) el.removeAttribute(attr.name)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
} catch (e) {
|
|
61
|
+
if (typeof console !== 'undefined') console.warn('[hyperclay] stripExtensionNoise skipped:', e)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -55,6 +55,8 @@
|
|
|
55
55
|
*
|
|
56
56
|
*/
|
|
57
57
|
|
|
58
|
+
import { EXTENSION_NODE_SELECTOR, EXTENSION_ATTR_PATTERN } from './extension-noise.js';
|
|
59
|
+
|
|
58
60
|
const dummyElem = document.createElement("div");
|
|
59
61
|
|
|
60
62
|
const Mutation = {
|
|
@@ -214,6 +216,11 @@ const Mutation = {
|
|
|
214
216
|
// For non-element nodes (like text nodes), start from parent
|
|
215
217
|
let element = (node && node.nodeType !== 1) ? node.parentElement : node;
|
|
216
218
|
|
|
219
|
+
// Browser-extension injected elements (and their descendants) are not page content.
|
|
220
|
+
if (element && element.closest && element.closest(EXTENSION_NODE_SELECTOR)) {
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
|
|
217
224
|
while (element && element.nodeType === 1) {
|
|
218
225
|
if (element.hasAttribute?.('mutations-ignore') ||
|
|
219
226
|
element.hasAttribute?.('save-remove') ||
|
|
@@ -247,6 +254,12 @@ const Mutation = {
|
|
|
247
254
|
continue;
|
|
248
255
|
}
|
|
249
256
|
|
|
257
|
+
// Ignore extension marker attributes (e.g. password-manager field tags) stamped onto real elements.
|
|
258
|
+
if (mutation.type === 'attributes' && mutation.attributeName &&
|
|
259
|
+
EXTENSION_ATTR_PATTERN.test(mutation.attributeName.toLowerCase())) {
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
|
|
250
263
|
if (mutation.type === 'characterData') {
|
|
251
264
|
this._log('Processing characterData mutation', {
|
|
252
265
|
element: mutation.target.parentElement,
|
|
@@ -474,6 +487,12 @@ if (!window.__hyperclayNoAutoExport) {
|
|
|
474
487
|
window.hyperclay = window.hyperclay || {};
|
|
475
488
|
window.hyperclay.Mutation = Mutation;
|
|
476
489
|
window.h = window.hyperclay;
|
|
490
|
+
// Signal consumers (e.g. hypercms ?cms=true auto-open) that Mutation is on the
|
|
491
|
+
// window now, so they can react instead of polling. Wrapped so a dispatch
|
|
492
|
+
// failure can never break the install.
|
|
493
|
+
try {
|
|
494
|
+
document.dispatchEvent(new CustomEvent('hyperclay:mutation-ready', { detail: { Mutation } }));
|
|
495
|
+
} catch {}
|
|
477
496
|
}
|
|
478
497
|
|
|
479
498
|
export default Mutation;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
var hyperHtmlApiData=(()=>{var k=Object.defineProperty;var ce=Object.getOwnPropertyDescriptor;var fe=Object.getOwnPropertyNames;var ue=Object.prototype.hasOwnProperty;var Y=(e,t)=>{for(var n in t)k(e,n,{get:t[n],enumerable:!0})},ae=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of fe(t))!ue.call(e,r)&&r!==n&&k(e,r,{get:()=>t[r],enumerable:!(o=ce(t,r))||o.enumerable});return e};var he=e=>ae(k({},"__esModule",{value:!0}),e);var Me={};Y(Me,{applyData:()=>z,default:()=>we,engine:()=>P,extractData:()=>X});var C=["textContent","innerText","innerHTML","outerHTML","value","checked","selected","disabled","readOnly","type","tagName","nodeName","nodeType","nodeValue","childElementCount","id","className","classList","baseURI","offsetWidth","offsetHeight","clientWidth","clientHeight","scrollWidth","scrollHeight","dataset","currentSrc","duration","paused","title","documentURI","contentType"],B=new Set(C),Q=new Set(["textContent","innerText","innerHTML","value","checked","selected","disabled","readOnly","type","id","className","title"]),Z=new Set(["tagName","nodeName","nodeType","nodeValue","childElementCount","classList","baseURI","documentURI","contentType","offsetWidth","offsetHeight","clientWidth","clientHeight","scrollWidth","scrollHeight","currentSrc","duration","paused","dataset"]);var j={};Y(j,{EmptyListInsert:()=>I,MAX_RULE_DEPTH:()=>A,MaxRuleDepthExceeded:()=>E,RuleTargetReadOnly:()=>w,RulesParseError:()=>S,ShapeMismatch:()=>b,UnknownRulesVersion:()=>N,UpgradeAlreadyRegistered:()=>U});var S=class extends Error{constructor(t,n){super(t),this.name="RulesParseError",this.cause=n}},N=class extends Error{constructor(t){super(`unknown rules version: ${t}. Library supports "1".`),this.name="UnknownRulesVersion",this.version=t}},A=20,E=class extends Error{constructor(t){super(`rule depth exceeded ${A} at path: ${t.join(".")}`),this.name="MaxRuleDepthExceeded",this.path=t}},b=class extends Error{constructor(t){super(`shape mismatch: ${t.length} field(s) failed validation`),this.name="ShapeMismatch",this.mismatches=t}},I=class extends Error{constructor(t){super(`cannot add items to empty list at "${t.join(".")}" \u2014 no sibling to clone as template. Seed the list with a hidden item first.`),this.name="EmptyListInsert",this.path=t}},w=class extends Error{constructor(t){super(`cannot write to read-only DOM property "${t}"`),this.name="RuleTargetReadOnly",this.target=t}},U=class extends Error{constructor(){super("upgrade transform already registered; only one registration is allowed per page."),this.name="UpgradeAlreadyRegistered"}};function g(e,t,n,o={}){return q(e,t,n,{depth:0,path:[]},o)}function q(e,t,n,o,r){if(o.depth>A)throw new E(o.path);if(typeof n=="string")return de(e,t,n,r);if(Array.isArray(n)){let[i,c]=n;return e.find(t,i,r).map((l,a)=>q(e,l,c,{depth:o.depth+1,path:[...o.path,a]},r))}if(typeof n=="object"&&n!==null){let i={};for(let[c,s]of Object.entries(n))i[c]=q(e,t,s,{depth:o.depth+1,path:[...o.path,c]},r);return i}return null}function de(e,t,n,o){if(n.endsWith("[]")){let i=n.slice(0,-2);return e.find(t,i,o).map(c=>e.text(c))}if(n.startsWith("@"))return ee(e,t,n.slice(1));if(n.includes("@")){let i=n.lastIndexOf("@"),c=n.slice(0,i),s=n.slice(i+1),l=c?e.find(t,c,o):[t];return l.length===0?null:ee(e,l[0],s)}if(n===".")return e.text(t);let r=e.find(t,n,o);return r.length===0?null:e.text(r[0])}function ee(e,t,n){if(B.has(n)){let r=e.prop(t,n);return r==null?null:String(r)}let o=e.attr(t,n);return o||null}var pe=.5;function v(e,t,n,o,r,i,c,s={}){let l=e.find(t,n,s);if(r.length===0){l.forEach(h=>e.remove(h));return}let a=r.length>l.length,u=l[0]||null;if(a&&!u&&(u=Re(e,t,n,s),!u))throw new I(i.path);let d=l.map(h=>me(e,h,o,s)),f=null;if(u){f=e.clone(u),s.templateAttr&&e.removeAttr(f,s.templateAttr);let h=e.stripIds(f);h>0&&console.warn(`[hyper-html-api] stripped ${h} id attribute(s) from cloned template at "${i.path.join(".")||"(root)"}"`)}let H=ye(r,d,o),R=l[0]||u,L=e.parent(R),se=l.length>0?Ee(e,L,R):0,K=new Set,W=r.map((h,m)=>{let p=H[m];if(p>=0)return K.add(p),l[p];let O=e.clone(f);return e.stripIds(O),O});l.forEach((h,m)=>{K.has(m)||e.remove(h)}),W.forEach((h,m)=>{let p=se+m;e.children(L).findIndex(le=>e.sameNode(le,h))!==p&&e.insertAt(L,h,p)}),W.forEach((h,m)=>{if(o===null){let p=r[m],O=p==null?"":String(p);e.text(h)!==O&&e.text(h,O)}else{let p=c(e,h,o,r[m],{depth:i.depth+1,path:[...i.path,m]},s);p&&p!==h&&(W[m]=p)}})}function me(e,t,n,o){return n===null?e.text(t):g(e,t,n,o)}function ye(e,t,n){let o=new Array(e.length).fill(-1),r=new Set;return e.forEach((i,c)=>{let s=-1,l=-1;t.forEach((a,u)=>{if(r.has(u))return;let d=ge(i,a,n),f=d===l&&s>=0?Math.abs(u-c)<Math.abs(s-c):!1;(d>l||f)&&(l=d,s=u)}),l>=pe&&(o[c]=s,r.add(s))}),o}function ge(e,t,n){if(n===null)return e===t?1:0;let o=Object.keys(n||{});if(o.length===0)return 0;let r=0;for(let i of o)JSON.stringify(e?.[i])===JSON.stringify(t?.[i])&&r++;return r/o.length}function Ee(e,t,n){let o=e.children(t);for(let r=0;r<o.length;r++)if(e.sameNode(o[r],n))return r;return-1}function Re(e,t,n,o){if(!o.templateAttr)return null;let r=t;for(;r;){let i=e.find(r,n,{includeRulesTag:!1});for(let c of i)if(e.attr(c,o.templateAttr)!=null)return c;r=e.parent(r)}return null}var te=new Set(["checked","selected","disabled","readOnly","paused"]);function T(e,t,n,o,r={}){let i=[];if(J(n,o,[],i),i.length)throw new b(i);$(e,t,n,o,{depth:0,path:[]},r)}function $(e,t,n,o,r,i={}){if(r.depth>A)throw new E(r.path);if(o===void 0)return t;if(typeof n=="string")return Oe(e,t,n,o,r,i);if(Array.isArray(n)){let[c,s]=n;return v(e,t,c,s,o,r,$,i),t}if(typeof n=="object"&&n!==null){for(let[c,s]of Object.entries(n)){let l=$(e,t,s,o==null?o:o[c],{depth:r.depth+1,path:[...r.path,c]},i);l&&l!==t&&(t=l)}return t}return t}function Oe(e,t,n,o,r,i){if(n.endsWith("[]")){let s=n.slice(0,-2);return v(e,t,s,null,o,r,$,i),t}if(n.startsWith("@"))return ne(e,t,n.slice(1),o);if(n.includes("@")){let s=n.lastIndexOf("@"),l=n.slice(0,s),a=n.slice(s+1),u=l?e.find(t,l,i):[t];return u.length===0||ne(e,u[0],a,o),t}if(n===".")return e.text(t,o==null?"":String(o)),t;let c=e.find(t,n,i);return c.length===0||e.text(c[0],o==null?"":String(o)),t}function ne(e,t,n,o){if(Z.has(n))throw new w(n);if(n==="outerHTML"){let r=o==null?"":String(o);return e.replaceWith(t,r)}return Q.has(n)?(e.prop(t,n,Se(n,o)),t):(e.attr(t,n,o==null?"":String(o)),t)}function Se(e,t){return t==null?te.has(e)?!1:"":te.has(e)?!!t:t}function J(e,t,n,o){if(t!==void 0){if(typeof e=="string"){if(e.endsWith("[]")){Array.isArray(t)?t.forEach((r,i)=>{typeof r=="object"&&r!==null&&o.push({path:_([...n,i]),expected:"scalar",got:M(r)})}):o.push({path:_(n),expected:"array",got:M(t)});return}t!==null&&typeof t=="object"&&o.push({path:_(n),expected:"scalar",got:M(t)});return}if(Array.isArray(e)){if(!Array.isArray(t)){o.push({path:_(n),expected:"array",got:M(t)});return}let r=e[1];t.forEach((i,c)=>J(r,i,[...n,c],o));return}if(typeof e=="object"&&e!==null){if(t===null||Array.isArray(t)||typeof t!="object"){o.push({path:_(n),expected:"object",got:M(t)});return}for(let[r,i]of Object.entries(e))J(i,t[r],[...n,r],o)}}}function M(e){return e===null?"null":Array.isArray(e)?"array":typeof e}function _(e){return e.join(".")}function V(e){try{return JSON.parse(e)}catch(t){throw new S(`Invalid strict JSON: ${t.message}`,t)}}function D(e){try{return JSON.parse(e)}catch{}let t={BRACE_OPEN:"{",BRACE_CLOSE:"}",BRACKET_OPEN:"[",BRACKET_CLOSE:"]",COLON:":",COMMA:",",STRING:"STRING",SELECTOR:"SELECTOR",IDENTIFIER:"IDENTIFIER",NUMBER:"NUMBER",BOOLEAN:"BOOLEAN"};function n(r){let i=[],c=0;for(;c<r.length;){let s=r[c];if(/\s/.test(s)){c++;continue}if("{}".includes(s)){i.push({type:s,value:s}),c++;continue}if(s==="["){let d=!1,f=c+1;for(;f<r.length&&/\s/.test(r[f]);)f++;if(f<r.length&&/[a-zA-Z_]/.test(r[f])&&(d=!0),!d){i.push({type:s,value:s}),c++;continue}}if(s==="]"){i.push({type:s,value:s}),c++;continue}if(s===":"){i.push({type:t.COLON,value:s}),c++;continue}if(s===","){i.push({type:t.COMMA,value:s}),c++;continue}if(s==='"'||s==="'"){let d=s,f=c+1;for(;f<r.length&&r[f]!==d;)r[f]==="\\"&&f++,f++;i.push({type:t.STRING,value:r.substring(c+1,f),quoted:!0,sourceQuote:d}),c=f+1;continue}let l=c,a;for(;l<r.length&&!/[{},]/.test(r[l]);)if(r[l]===":"){let d=[":first",":last",":nth-child",":nth-of-type",":first-child",":last-child",":first-of-type",":last-of-type",":only-child",":only-of-type",":hover",":focus",":active",":visited",":disabled",":enabled",":checked",":empty",":root",":target",":not",":before",":after",":nth-last-child",":nth-last-of-type"],f=!1;for(let H of d){let R=H.substring(1);if(r.substring(l+1,l+1+R.length)===R){f=!0,l+=R.length;break}}if(!f)break}else if(r[l]==="["){for(l++;l<r.length&&r[l]!=="]";){if(r[l]==='"'||r[l]==="'"){let d=r[l];for(l++;l<r.length&&r[l]!==d;)r[l]==="\\"&&l++,l++}l++}l<r.length&&r[l]==="]"&&l++}else l++;a=r.substring(c,l);let u=t.IDENTIFIER;/^-?\d+(\.\d+)?$/.test(a)?u=t.NUMBER:a==="true"||a==="false"||a==="null"?u=t.BOOLEAN:/^[.#@\[]|[.#@\[]| /.test(a)&&(u=t.SELECTOR),i.push({type:u,value:a,quoted:!1}),c=l}return i}function o(r){let i="";for(let c=0;c<r.length;c++){let s=r[c];if("{}".includes(s.type)||"[]".includes(s.type)){i+=s.value;continue}if(s.type===t.COLON){i+=s.value;continue}if(s.type===t.COMMA){let l=r[c+1];if(l&&(l.type==="}"||l.type==="]"))continue;i+=s.value;continue}if(s.type===t.STRING&&s.quoted){let l=s.value;s.sourceQuote==="'"&&(l=l.replace(/\\'/g,"'"),l=l.replace(/(\\*)"/g,(a,u)=>u.length%2===0?u+'\\"':a)),i+=`"${l}"`;continue}if(s.type===t.NUMBER||s.type===t.BOOLEAN){i+=s.value;continue}if(s.type===t.SELECTOR||s.type===t.IDENTIFIER){i+=`"${s.value}"`;continue}i+=`"${s.value}"`}return i}try{let r=n(e),i=o(r);return JSON.parse(i)}catch(r){throw new S("Invalid extraction rules syntax: "+r.message,r)}}var Ae="1",re=/^[a-zA-Z0-9_-]+$/;function x(e,t,n){let o;if(n===void 0)o="script[data-rules-name]";else{if(typeof n!="string"||!re.test(n))throw new Error(`hyper-html-api: invalid rules token ${JSON.stringify(n)} (must match ${re})`);o=`script[data-rules-name~="${n}"]`}let r=e.find(t,o,{includeRulesTag:!0});if(r.length===0)return null;n!==void 0&&r.length>1&&console.warn(`hyper-html-api: ${r.length} rules tags match data-rules-name~="${n}"; using the first.`);let i=r[0],c=e.attr(i,"data-rules-version");if(c!==Ae)throw new N(c);return{rules:D(e.text(i)),tagNode:i}}function F(e,t,n){if(n&&typeof n=="object")return{rules:n,tagNode:null};if(typeof n=="string"){let o=t&&t.ownerDocument?t.ownerDocument:t;return x(e,o,n)}return null}function oe(e,t,n,o){let r=F(e,t,n);if(!r){let s=typeof n=="string"?`data-rules-name~="${n}"`:"the provided rules object";throw new Error(`hyper-html-api: could not resolve rules for ${s}`)}let{rules:i,tagNode:c}=r;return{rules:i,tagNode:c,get:()=>g(e,t,i,o),set:s=>T(e,t,i,s,o)}}function xe(e){return e&&e.nodeType===1&&e.tagName==="SCRIPT"&&e.hasAttribute&&e.hasAttribute("data-rules-name")}function Ne(e){return e?(e.nodeType===9||e.nodeType===11,e):null}var be={find(e,t,n={}){let o=Ne(e);if(!o||!o.querySelectorAll)return[];let r=Array.from(o.querySelectorAll(t));n.includeRulesTag||(r=r.filter(c=>!xe(c)));let i=[];if(n.skip&&i.push(n.skip),n.templateAttr&&i.push("["+n.templateAttr+"]"),i.length){let c=i.join(", ");r=r.filter(s=>!s.closest||!s.closest(c))}return r},parent(e){return e?e.parentElement:null},children(e){return e?Array.from(e.children):[]},text(e,t){if(t===void 0)return(e.textContent||"").trim();e.textContent=t},attr(e,t,n){if(n===void 0)return e.hasAttribute&&e.hasAttribute(t)?e.getAttribute(t):null;e.setAttribute(t,n)},removeAttr(e,t){e&&e.removeAttribute&&e.removeAttribute(t)},prop(e,t,n){if(n===void 0){let o=e?e[t]:void 0;return o!==void 0?o:null}e[t]=n},clone(e){return e.cloneNode(!0)},insertAt(e,t,n){let o=e.children[n]||null;e.insertBefore(t,o)},remove(e){e&&e.parentNode&&e.parentNode.removeChild(e)},replaceWith(e,t){if(!e||!e.parentNode)throw new Error("dom.replaceWith: node has no parent");let o=e.ownerDocument.createElement("template");o.innerHTML=t;let r=o.content.firstElementChild;if(!r)throw new Error("dom.replaceWith: html did not parse to an element");return e.parentNode.replaceChild(r,e),r},stripIds(e){let t=0;return e.id&&(e.removeAttribute("id"),t++),(e.querySelectorAll?e.querySelectorAll("[id]"):[]).forEach(o=>{o.removeAttribute("id"),t++}),t},sameNode(e,t){return e===t}},y=be;var P={extract:(e,t,n)=>g(y,e,t,n),apply:(e,t,n,o)=>T(y,e,t,n,o),findRulesIn:(e,t)=>x(y,e,t),findRules:(e,t)=>F(y,e,t),bind:(e,t,n)=>oe(y,e,t,n),parseStrict:V,parseRelaxed:D,errors:j,DOM_PROPERTIES:C},G=e=>!!e&&typeof e.nodeType=="number";function ie(e){let t=e&&e.ownerDocument?e.ownerDocument:e,n=y.find(t,"script[data-rules-name]",{includeRulesTag:!0});if(n.length===0)throw new Error('hyper-html-api: no rules tag found. Add <script type="application/json" data-rules-name="\u2026" data-rules-version="1"> or pass rules.');if(n.length>1)throw new Error('hyper-html-api: multiple rules tags found; pass a name, e.g. extractData("api").');return x(y,t).rules}function X(e,t){let n=G(e)?e:document,o=G(e)?t:e;return o===void 0?g(y,n,ie(n)):P.bind(n,o).get()}function z(e,t,n){if(!G(e))throw new Error("hyper-html-api: applyData(root, data, source?) needs a DOM root as the first argument.");return n===void 0?T(y,e,ie(e),t):P.bind(e,n).set(t),e}var Ie={engine:P,extractData:X,applyData:z},we=Ie;return he(Me);})();
|
|
2
|
+
|
|
3
|
+
// Auto-export to window unless suppressed by loader.
|
|
4
|
+
if (!window.__hyperclayNoAutoExport) {
|
|
5
|
+
window.hyperclay = window.hyperclay || {};
|
|
6
|
+
window.hyperclay.extractData = hyperHtmlApiData.extractData;
|
|
7
|
+
window.hyperclay.applyData = hyperHtmlApiData.applyData;
|
|
8
|
+
window.h = window.hyperclay;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const engine = hyperHtmlApiData.engine;
|
|
12
|
+
export const extractData = hyperHtmlApiData.extractData;
|
|
13
|
+
export const applyData = hyperHtmlApiData.applyData;
|
|
14
|
+
export default hyperHtmlApiData;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var hyperundo=(()=>{var x=Object.defineProperty;var J=Object.getOwnPropertyDescriptor;var Q=Object.getOwnPropertyNames;var X=Object.prototype.hasOwnProperty;var Y=(e,r)=>{for(var o in r)x(e,o,{get:r[o],enumerable:!0})},$=(e,r,o,t)=>{if(r&&typeof r=="object"||typeof r=="function")for(let n of Q(r))!X.call(e,n)&&n!==o&&x(e,n,{get:()=>r[n],enumerable:!(t=J(r,n))||t.enumerable});return e};var ee=e=>$(x({},"__esModule",{value:!0}),e);var ie={};Y(ie,{default:()=>oe,undo:()=>V});function te(e,r=null){let o=[];if(e.type==="attributes"){let t=e.target,n=e.attributeName,i=e.oldValue,a=t.getAttribute(n);return i==null&&a!=null?o.push({kind:"attr-add",target:t,name:n,newValue:a}):i!=null&&a==null?o.push({kind:"attr-remove",target:t,name:n,oldValue:i}):i!==a&&o.push({kind:"attr-set",target:t,name:n,oldValue:i,newValue:a}),o}if(e.type==="characterData")return o.push({kind:"text",target:e.target,oldValue:e.oldValue,newValue:e.target.data}),o;if(e.type==="childList"){let t=e.target,n=e.nextSibling,i=r?Array.from(e.addedNodes).filter(u=>!r(u)):Array.from(e.addedNodes),a=r?Array.from(e.removedNodes).filter(u=>!r(u)):Array.from(e.removedNodes);return i.length>0&&o.push({kind:"add",parent:t,nodes:i,before:n}),a.length>0&&o.push({kind:"remove",parent:t,nodes:a,before:n}),o}return o}function S(e,r=null){let o=[],t=new Map;for(let n of e){if(n.type==="attributes"){let i=n.target,a=n.attributeName,u=t.get(i);u||(u=new Map,t.set(i,u)),u.has(a)||(u.set(a,{idx:o.length,oldValue:n.oldValue}),o.push(null));continue}for(let i of te(n,r))o.push(i)}for(let[n,i]of t)for(let[a,u]of i){let l=u.oldValue,p=n.getAttribute(a),f=null;l===p?f=null:l==null?f={kind:"attr-add",target:n,name:a,newValue:p}:p==null?f={kind:"attr-remove",target:n,name:a,oldValue:l}:f={kind:"attr-set",target:n,name:a,oldValue:l,newValue:p},o[u.idx]=f}return o.filter(n=>n!=null)}function D(e){switch(e.kind){case"attr-set":case"attr-add":e.target.setAttribute(e.name,e.newValue);return;case"attr-remove":e.target.removeAttribute(e.name);return;case"text":e.target.data=e.newValue;return;case"add":for(let r of e.nodes)e.before&&e.before.parentNode===e.parent?e.parent.insertBefore(r,e.before):e.parent.appendChild(r);return;case"remove":for(let r of e.nodes)r.parentNode===e.parent&&e.parent.removeChild(r);return}}function N(e){switch(e.kind){case"attr-set":e.target.setAttribute(e.name,e.oldValue);return;case"attr-add":e.target.removeAttribute(e.name);return;case"attr-remove":e.target.setAttribute(e.name,e.oldValue);return;case"text":e.target.data=e.oldValue;return;case"add":for(let r of e.nodes)r.parentNode===e.parent&&e.parent.removeChild(r);return;case"remove":for(let r of e.nodes)e.before&&e.before.parentNode===e.parent?e.parent.insertBefore(r,e.before):e.parent.appendChild(r);return}}var re=["mutations-ignore","save-remove","save-ignore","save-freeze"];function C(e,r,o){let t=e&&e.nodeType!==1?e.parentElement:e;for(;t&&t.nodeType===1;){for(let n of re)if(t.hasAttribute&&t.hasAttribute(n))return!0;t=t.parentElement}if(r&&o&&o.type==="attributes")try{if(r(o.attributeName,o.target))return!0}catch{}return!1}function L(){let e=new Map;function r(n,i){let a=e.get(n);return a||(a=new Set,e.set(n,a)),a.add(i),()=>o(n,i)}function o(n,i){let a=e.get(n);a&&a.delete(i)}function t(n,i){let a=e.get(n);if(a)for(let u of Array.from(a))try{u(i)}catch(l){console.error("[hyper-undo] listener threw",l)}}return{on:r,off:o,emit:t}}var ne={maxHistory:100,idleWindowMs:500,idleLabel:"Edit",ignoreAttribute:null,debug:!1};function E(e){let r={...ne,...e};if(!r.scope)throw new Error("hyper-undo: scope is required");let o=L(),t=[],n=[],i=[],a=null,u=null,l=0,p=!1;function f(...d){r.debug&&console.log("[hyper-undo]",...d)}function F(){p||(p=!0,u=new MutationObserver(y),u.observe(r.scope,{childList:!0,attributes:!0,characterData:!0,subtree:!0,attributeOldValue:!0,characterDataOldValue:!0}),f("started"))}function b(){p&&(p=!1,u&&(u.disconnect(),u=null),g(),t.length=0,n.length=0,i=[],l=0,f("stopped"))}function T(){let d=r.scope;return!!(d&&d.nodeType===1&&d.isConnected===!1)}function y(d){if(l>0)return;let c=I(d);if(c.length>0){for(let m of c)i.push(m);z()}}function I(d){let c=d.filter(m=>!C(m.target,r.ignoreAttribute,m));return S(c,m=>C(m,r.ignoreAttribute))}function z(){g(),a=setTimeout(P,r.idleWindowMs)}function g(){a!=null&&(clearTimeout(a),a=null)}function P(){if(a=null,i.length===0)return;let d=i;i=[],v(r.idleLabel,d)}function v(d,c){for(t.push({label:d,timestamp:Date.now(),primitives:c}),n.length=0;t.length>r.maxHistory;)t.shift();f("commit",d,"primitives:",c.length),o.emit("
|
|
1
|
+
var hyperundo=(()=>{var x=Object.defineProperty;var J=Object.getOwnPropertyDescriptor;var Q=Object.getOwnPropertyNames;var X=Object.prototype.hasOwnProperty;var Y=(e,r)=>{for(var o in r)x(e,o,{get:r[o],enumerable:!0})},$=(e,r,o,t)=>{if(r&&typeof r=="object"||typeof r=="function")for(let n of Q(r))!X.call(e,n)&&n!==o&&x(e,n,{get:()=>r[n],enumerable:!(t=J(r,n))||t.enumerable});return e};var ee=e=>$(x({},"__esModule",{value:!0}),e);var ie={};Y(ie,{default:()=>oe,undo:()=>V});function te(e,r=null){let o=[];if(e.type==="attributes"){let t=e.target,n=e.attributeName,i=e.oldValue,a=t.getAttribute(n);return i==null&&a!=null?o.push({kind:"attr-add",target:t,name:n,newValue:a}):i!=null&&a==null?o.push({kind:"attr-remove",target:t,name:n,oldValue:i}):i!==a&&o.push({kind:"attr-set",target:t,name:n,oldValue:i,newValue:a}),o}if(e.type==="characterData")return o.push({kind:"text",target:e.target,oldValue:e.oldValue,newValue:e.target.data}),o;if(e.type==="childList"){let t=e.target,n=e.nextSibling,i=r?Array.from(e.addedNodes).filter(u=>!r(u)):Array.from(e.addedNodes),a=r?Array.from(e.removedNodes).filter(u=>!r(u)):Array.from(e.removedNodes);return i.length>0&&o.push({kind:"add",parent:t,nodes:i,before:n}),a.length>0&&o.push({kind:"remove",parent:t,nodes:a,before:n}),o}return o}function S(e,r=null){let o=[],t=new Map;for(let n of e){if(n.type==="attributes"){let i=n.target,a=n.attributeName,u=t.get(i);u||(u=new Map,t.set(i,u)),u.has(a)||(u.set(a,{idx:o.length,oldValue:n.oldValue}),o.push(null));continue}for(let i of te(n,r))o.push(i)}for(let[n,i]of t)for(let[a,u]of i){let l=u.oldValue,p=n.getAttribute(a),f=null;l===p?f=null:l==null?f={kind:"attr-add",target:n,name:a,newValue:p}:p==null?f={kind:"attr-remove",target:n,name:a,oldValue:l}:f={kind:"attr-set",target:n,name:a,oldValue:l,newValue:p},o[u.idx]=f}return o.filter(n=>n!=null)}function D(e){switch(e.kind){case"attr-set":case"attr-add":e.target.setAttribute(e.name,e.newValue);return;case"attr-remove":e.target.removeAttribute(e.name);return;case"text":e.target.data=e.newValue;return;case"add":for(let r of e.nodes)e.before&&e.before.parentNode===e.parent?e.parent.insertBefore(r,e.before):e.parent.appendChild(r);return;case"remove":for(let r of e.nodes)r.parentNode===e.parent&&e.parent.removeChild(r);return}}function N(e){switch(e.kind){case"attr-set":e.target.setAttribute(e.name,e.oldValue);return;case"attr-add":e.target.removeAttribute(e.name);return;case"attr-remove":e.target.setAttribute(e.name,e.oldValue);return;case"text":e.target.data=e.oldValue;return;case"add":for(let r of e.nodes)r.parentNode===e.parent&&e.parent.removeChild(r);return;case"remove":for(let r of e.nodes)e.before&&e.before.parentNode===e.parent?e.parent.insertBefore(r,e.before):e.parent.appendChild(r);return}}var re=["mutations-ignore","save-remove","save-ignore","save-freeze"];function C(e,r,o){let t=e&&e.nodeType!==1?e.parentElement:e;for(;t&&t.nodeType===1;){for(let n of re)if(t.hasAttribute&&t.hasAttribute(n))return!0;t=t.parentElement}if(r&&o&&o.type==="attributes")try{if(r(o.attributeName,o.target))return!0}catch{}return!1}function L(){let e=new Map;function r(n,i){let a=e.get(n);return a||(a=new Set,e.set(n,a)),a.add(i),()=>o(n,i)}function o(n,i){let a=e.get(n);a&&a.delete(i)}function t(n,i){let a=e.get(n);if(a)for(let u of Array.from(a))try{u(i)}catch(l){console.error("[hyper-undo] listener threw",l)}}return{on:r,off:o,emit:t}}var ne={maxHistory:100,idleWindowMs:500,idleLabel:"Edit",ignoreAttribute:null,debug:!1};function E(e){let r={...ne,...e};if(!r.scope)throw new Error("hyper-undo: scope is required");let o=L(),t=[],n=[],i=[],a=null,u=null,l=0,p=!1;function f(...d){r.debug&&console.log("[hyper-undo]",...d)}function F(){p||(p=!0,u=new MutationObserver(y),u.observe(r.scope,{childList:!0,attributes:!0,characterData:!0,subtree:!0,attributeOldValue:!0,characterDataOldValue:!0}),f("started"))}function b(){p&&(p=!1,u&&(u.disconnect(),u=null),g(),t.length=0,n.length=0,i=[],l=0,f("stopped"))}function T(){let d=r.scope;return!!(d&&d.nodeType===1&&d.isConnected===!1)}function y(d){if(l>0)return;let c=I(d);if(c.length>0){for(let m of c)i.push(m);z()}}function I(d){let c=d.filter(m=>!C(m.target,r.ignoreAttribute,m));return S(c,m=>C(m,r.ignoreAttribute))}function z(){g(),a=setTimeout(P,r.idleWindowMs)}function g(){a!=null&&(clearTimeout(a),a=null)}function P(){if(a=null,i.length===0)return;let d=i;i=[],v(r.idleLabel,d)}function v(d,c){for(t.push({label:d,timestamp:Date.now(),primitives:c}),n.length=0;t.length>r.maxHistory;)t.shift();f("commit",d,"primitives:",c.length),o.emit("commit")}function B(d,c){if(!p)throw new Error("hyper-undo: scope not started");y(u.takeRecords()),w();let m=c();if(m&&typeof m.then=="function")throw new Error("hyper-undo: commit() fn must be synchronous; returned a Promise");if(y(u.takeRecords()),i.length===0){o.emit("commit");return}g();let j=i;i=[],v(d,j)}function H(d){if(!p)throw new Error("hyper-undo: scope not started");if(!u)return;if(l>1){u.takeRecords();return}w();let c=I(u.takeRecords());c.length!==0&&(g(),v(d,c))}function W(){u&&u.takeRecords()}function w(){l===0&&u&&y(u.takeRecords()),i.length!==0&&(g(),P())}function k(){l===0&&u&&y(u.takeRecords()),l++,f("paused",l)}function A(){l!==0&&(l--,l===0&&u&&u.takeRecords(),f("resumed",l))}function q(){if(T()){b();return}w();let d=t.pop();if(d){k();try{for(let c=d.primitives.length-1;c>=0;c--)try{N(d.primitives[c])}catch(m){f("undo primitive failed (continuing)",m)}}finally{A()}n.push(d),f("undo",d.label),o.emit("undo")}}function G(){if(T()){b();return}w();let d=n.pop();if(d){k();try{for(let c of d.primitives)try{D(c)}catch(m){f("redo primitive failed (continuing)",m)}}finally{A()}t.push(d),f("redo",d.label),o.emit("redo")}}function Z(){u&&u.takeRecords(),t.length=0,n.length=0,i=[],g(),f("cleared"),o.emit("clear")}return{_config:r,start:F,stop:b,commit:B,commitCaptured:H,discardCaptured:W,flush:w,undo:q,redo:G,clear:Z,pause:k,resume:A,on:o.on,off:o.off,get canUndo(){return t.length>0},get canRedo(){return n.length>0},get isPaused(){return l>0},get history(){return t.map(d=>({label:d.label,timestamp:d.timestamp}))},get scope(){return r.scope}}}function _(e,r){let o=t=>{let n=e.scope;if(n&&n.nodeType===1&&n.isConnected===!1){r&&r();return}let i=t.target,a=e._config.shadowKeydownIn;if(i&&i.closest&&Array.isArray(a))for(let p of a)try{if(i.closest(p))return}catch{}if(!(t.metaKey||t.ctrlKey))return;let l=(t.key||"").toLowerCase();if(l==="z"&&!t.shiftKey){t.preventDefault(),t.stopPropagation(),e.undo();return}if(l==="z"&&t.shiftKey){t.preventDefault(),t.stopPropagation(),e.redo();return}if(l==="y"&&!t.shiftKey){t.preventDefault(),t.stopPropagation(),e.redo();return}};return window.addEventListener("keydown",o,!0),()=>window.removeEventListener("keydown",o,!0)}var K=[".CodeMirror",".cm-editor",".monaco-editor",".ace_editor",".ql-editor",".tiptap",".ProseMirror"],s=null,R=null,M=null,h=null;function U(e,r){if(h){if(h.inst===e)return h.cleanup;throw new Error("hyper-undo: another scope already owns the global Cmd+Z binding (bindKeys: true). Stop it first, or use bindKeys: false for additional scopes.")}let o=_(e,r);return h={inst:e,cleanup:o},o}function O(e){h&&h.inst===e&&(h.cleanup(),h=null)}var V={defaults:{shadowKeydownIn:K},start(e={}){let r=e.scope||(typeof document<"u"?document.body:null);if(!r)throw new Error("hyper-undo: no scope (need document.body or explicit { scope })");if(s){if(R===r)return console.warn("[hyper-undo] start() called again on existing singleton; ignoring new options"),s;throw new Error("hyper-undo: start() called with a different scope while the singleton is already started. Use undo.create({ scope }) for additional scopes, or call undo.stop() first.")}let o={...e,scope:r,shadowKeydownIn:e.shadowKeydownIn||K};return s=E(o),s.start(),R=r,e.bindKeys!==!1&&(M=U(s,()=>V.stop())),s},stop(){s&&(O(s),M=null,s.stop(),s=null,R=null)},create(e={}){let r=e.scope||(typeof document<"u"?document.body:null);if(!r)throw new Error("hyper-undo: no scope (need document.body or explicit { scope })");let o={...e,scope:r,shadowKeydownIn:e.shadowKeydownIn||K},t=E(o),n=null,i={_config:t._config,commit:t.commit,commitCaptured:t.commitCaptured,discardCaptured:t.discardCaptured,flush:t.flush,undo:t.undo,redo:t.redo,clear:t.clear,pause:t.pause,resume:t.resume,on:t.on,off:t.off,get canUndo(){return t.canUndo},get canRedo(){return t.canRedo},get isPaused(){return t.isPaused},get history(){return t.history},get scope(){return t.scope},start(){e.bindKeys&&(n=U(t,()=>i.stop())),t.start()},stop(){O(t),n=null,t.stop()}};return i},commit(e,r){return s?s.commit(e,r):void 0},commitCaptured(e){return s?s.commitCaptured(e):void 0},discardCaptured(){return s?s.discardCaptured():void 0},undo(){return s?s.undo():void 0},redo(){return s?s.redo():void 0},clear(){return s?s.clear():void 0},pause(){return s?s.pause():void 0},resume(){return s?s.resume():void 0},flush(){return s?s.flush():void 0},on(e,r){return s?s.on(e,r):void 0},off(e,r){return s?s.off(e,r):void 0},get canUndo(){return s?s.canUndo:!1},get canRedo(){return s?s.canRedo:!1},get history(){return s?s.history:[]},get isPaused(){return s?s.isPaused:!1}};var oe=V;return ee(ie);})();
|
|
2
2
|
|
|
3
3
|
// Auto-export to window unless suppressed by loader.
|
|
4
4
|
if (!window.__hyperclayNoAutoExport) {
|