hyperclayjs 1.27.0 → 1.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -86,7 +86,7 @@ import 'hyperclayjs/presets/standard.js';
86
86
 
87
87
  | Module | Size | Description |
88
88
  |--------|------|-------------|
89
- | dialogs | 8.2KB | ask(), consent(), tell(), snippet() dialog functions |
89
+ | dialogs | 8.5KB | ask(), consent(), tell(), snippet() dialog functions |
90
90
  | the-modal | 22.5KB | Full modal window creation system - window.theModal |
91
91
  | toast | 15.8KB | Success/error message notifications, toast(msg, msgType) |
92
92
 
@@ -123,14 +123,15 @@ import 'hyperclayjs/presets/standard.js';
123
123
  | Module | Size | Description |
124
124
  |--------|------|-------------|
125
125
  | file-upload | 11.3KB | File upload with progress |
126
- | live-sync | 15.5KB | Real-time DOM sync across browsers |
126
+ | live-sync | 25.3KB | Real-time DOM sync across browsers |
127
127
  | send-message | 1.3KB | Message sending utility |
128
128
 
129
129
  ### Vendor Libraries (Third-party libraries)
130
130
 
131
131
  | Module | Size | Description |
132
132
  |--------|------|-------------|
133
- | hyper-morph | 17.2KB | DOM morphing with content-based element matching |
133
+ | hyper-html-api | 14.6KB | Declarative extract/apply engine driven by a rules tag — foundation for CMS and upgrade flows |
134
+ | hyper-morph | 18.7KB | DOM morphing with content-based element matching |
134
135
 
135
136
  ## Presets
136
137
 
@@ -144,7 +145,7 @@ Standard feature set for most use cases
144
145
 
145
146
  **Modules:** `save-core`, `snapshot`, `save-system`, `unsaved-warning`, `edit-mode-helpers`, `persist`, `option-visibility`, `event-attrs`, `dom-helpers`, `toast`, `save-toast`, `export-to-window`, `view-mode-excludes-edit-modules`
146
147
 
147
- ### Everything (~235.8KB)
148
+ ### Everything (~262KB)
148
149
  All available features
149
150
 
150
151
  Includes all available modules across all categories.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperclayjs",
3
- "version": "1.27.0",
3
+ "version": "1.28.0",
4
4
  "description": "Modular JavaScript library for building interactive malleable HTML files with Hyperclay",
5
5
  "type": "module",
6
6
  "main": "src/hyperclay.js",
@@ -50,16 +50,31 @@ class LiveSync {
50
50
  // High-water mark of server seqs we've seen on this channel (own echoes
51
51
  // count too). Server-broadcast payloads carry a monotonic seq
52
52
  // (Date.now()-based). We drop anything <= this to guard against rare
53
- // cases where a stale message lands after a newer one e.g. buffered
53
+ // cases where a stale message lands after a newer one, e.g. buffered
54
54
  // replay, alt backend after reconnect, or an own-save echo arriving
55
55
  // after a peer's newer broadcast.
56
56
  this.lastSeenSeq = 0;
57
57
 
58
- // Promise chain used to serialize overlapping applyUpdate() calls.
59
- // A morph with external scripts returns a Promise; without chaining, two
60
- // SSE events arriving back-to-back would interleave their morphs on the
61
- // same DOM.
62
- this._applyChain = Promise.resolve();
58
+ // rAF-paced single-flight queue. Incoming updates overwrite a pending
59
+ // slot; on each animation frame, if a slot is set and no morph is in
60
+ // flight, run one morph against the latest pending payload. Burst
61
+ // arrivals collapse to one morph per frame, keeping the receiver from
62
+ // falling behind under load. A morph with external scripts returns a
63
+ // Promise, so the in-flight flag prevents overlap.
64
+ this._pendingHtml = null;
65
+ this._pendingSeq = null;
66
+ this._pendingIdentityMap = null;
67
+ this._morphInFlight = false;
68
+ this._rafHandle = null;
69
+
70
+ // Identity tracking for content-based morphing across live-sync updates.
71
+ // Synthetic IDs (`<clientId>:<counter>`) live here only — never written to
72
+ // the DOM, never serialized into saved HTML. The WeakMap holds them
73
+ // against the live elements so that the next save can produce the same
74
+ // identityMap, and afterNodeMorphed transfers IDs from incoming parsed
75
+ // elements onto the live elements they morphed into.
76
+ this.idCounter = this._loadIdCounter();
77
+ this.liveWeakMap = new WeakMap();
63
78
 
64
79
  // Callbacks
65
80
  this.onConnect = null;
@@ -127,7 +142,6 @@ class LiveSync {
127
142
  // Reset state for new connection
128
143
  this.lastHtml = null;
129
144
  this.lastSeenSeq = 0;
130
- this._applyChain = Promise.resolve();
131
145
 
132
146
  console.log('[LiveSync] Starting for:', this.currentFile);
133
147
  this.connect();
@@ -150,6 +164,149 @@ class LiveSync {
150
164
  }
151
165
 
152
166
  clearTimeout(this.debounceTimer);
167
+
168
+ // Cancel any pending frame and clear the queue. A morph already in
169
+ // flight cannot be aborted; the isDestroyed check in _runPending guards
170
+ // its post-morph rescheduling so the queue stops cleanly.
171
+ if (this._rafHandle != null) {
172
+ this._cancelFrame(this._rafHandle);
173
+ this._rafHandle = null;
174
+ }
175
+ this._pendingHtml = null;
176
+ this._pendingSeq = null;
177
+ this._pendingIdentityMap = null;
178
+ }
179
+
180
+ _loadIdCounter() {
181
+ try {
182
+ const raw = sessionStorage.getItem('livesync-id-counter');
183
+ const n = parseInt(raw || '0', 10);
184
+ return Number.isFinite(n) && n > 0 ? n : 0;
185
+ } catch (e) {
186
+ return 0;
187
+ }
188
+ }
189
+
190
+ _persistIdCounter() {
191
+ try {
192
+ sessionStorage.setItem('livesync-id-counter', String(this.idCounter));
193
+ } catch (e) {
194
+ // Storage full / sandboxed — fall back to in-memory only.
195
+ }
196
+ }
197
+
198
+ _mintId() {
199
+ this.idCounter++;
200
+ this._persistIdCounter();
201
+ return `${this.clientId}:${this.idCounter}`;
202
+ }
203
+
204
+ /**
205
+ * Walk the live DOM and the snapshot clone in lockstep. Path keys come
206
+ * from the clone (= what the receiver will see, after [snapshot-remove]
207
+ * and snapshotHooks). WeakMap lookup happens against the live element so
208
+ * synthetic IDs persist across saves.
209
+ *
210
+ * The live walk filters [snapshot-remove] to mirror the clone's earlier
211
+ * strip in captureSnapshot. If child counts diverge anywhere (an
212
+ * onbeforesnapshot handler added/removed siblings on the clone), the
213
+ * subtree is skipped — better to fall back to content scoring there than
214
+ * emit misaligned IDs.
215
+ *
216
+ * @param {Element} liveRoot
217
+ * @param {Element} cloneRoot
218
+ * @returns {Object} identityMap keyed by dot-path
219
+ */
220
+ _buildIdentityMap(liveRoot, cloneRoot) {
221
+ const map = {};
222
+ if (!liveRoot || !cloneRoot) return map;
223
+
224
+ const visit = (live, clone, path) => {
225
+ let id = this.liveWeakMap.get(live);
226
+ if (!id) {
227
+ id = this._mintId();
228
+ this.liveWeakMap.set(live, id);
229
+ }
230
+ map[path] = id;
231
+
232
+ const liveKids = [];
233
+ for (const c of live.children) {
234
+ if (!c.hasAttribute('snapshot-remove')) liveKids.push(c);
235
+ }
236
+ const cloneKids = clone.children;
237
+
238
+ if (liveKids.length !== cloneKids.length) {
239
+ this._log(
240
+ `identity map: subtree skipped at "${path}" (live=${liveKids.length}, clone=${cloneKids.length})`
241
+ );
242
+ return;
243
+ }
244
+
245
+ for (let i = 0; i < liveKids.length; i++) {
246
+ visit(liveKids[i], cloneKids[i], path === '' ? String(i) : `${path}.${i}`);
247
+ }
248
+ };
249
+
250
+ visit(liveRoot, cloneRoot, '');
251
+ return map;
252
+ }
253
+
254
+ /**
255
+ * Walk a single parsed tree, invoking cb(element, path) at each Element.
256
+ * Paths use the same dot-segment scheme as _buildIdentityMap so the
257
+ * receiver can look up IDs by the path the sender emitted.
258
+ */
259
+ _walkParsedTree(root, cb) {
260
+ if (!root) return;
261
+ const visit = (el, path) => {
262
+ cb(el, path);
263
+ const kids = el.children;
264
+ for (let i = 0; i < kids.length; i++) {
265
+ visit(kids[i], path === '' ? String(i) : `${path}.${i}`);
266
+ }
267
+ };
268
+ visit(root, '');
269
+ }
270
+
271
+ /**
272
+ * Fill liveWeakMap entries for live elements that the matcher's
273
+ * afterNodeMorphed didn't reach. createNode's no-id-children
274
+ * optimization (hyper-morph importNode path) inserts a clone of the
275
+ * parsed element without invoking morphNode, so afterNodeMorphed never
276
+ * fires for those subtrees and their synthetic IDs would be lost. On
277
+ * the receiver's next save, _buildIdentityMap would mint fresh IDs for
278
+ * the same logical elements, breaking convergence for newly-added
279
+ * ambiguous siblings — exactly the case identity-map exists to fix.
280
+ *
281
+ * Walks live and parsed in lockstep using the same path scheme as
282
+ * _buildIdentityMap. Filters [snapshot-remove] from the live side to
283
+ * stay aligned with the sender's clone view. Aborts a subtree on
284
+ * child-count divergence (e.g. local save-ignore additions) — those
285
+ * elements fall through to content scoring on the next round, which
286
+ * is the same fallback as a sender-side lockstep skip.
287
+ *
288
+ * @param {Element} liveRoot - post-morph live tree root
289
+ * @param {Element} parsedRoot - parsed-tree root (still has identityMap WeakMap entries)
290
+ * @param {Object} identityMap - path → id map from the SSE payload
291
+ */
292
+ _fillInIdsAfterMorph(liveRoot, parsedRoot, identityMap) {
293
+ if (!liveRoot || !parsedRoot || !identityMap) return;
294
+ const visit = (live, parsed, path) => {
295
+ const id = identityMap[path];
296
+ if (id && !this.liveWeakMap.has(live)) {
297
+ this.liveWeakMap.set(live, id);
298
+ }
299
+ const liveKids = [];
300
+ for (const c of live.children) {
301
+ if (!c.hasAttribute('snapshot-remove')) liveKids.push(c);
302
+ }
303
+ const parsedKids = parsed.children;
304
+ if (liveKids.length !== parsedKids.length) return;
305
+ for (let i = 0; i < liveKids.length; i++) {
306
+ visit(liveKids[i], parsedKids[i], path === '' ? String(i) : `${path}.${i}`);
307
+ }
308
+ };
309
+ visit(liveRoot, parsedRoot, '');
153
310
  }
154
311
 
155
312
  /**
@@ -210,7 +367,7 @@ class LiveSync {
210
367
  return;
211
368
  }
212
369
 
213
- const { html, sender, seq } = data;
370
+ const { html, sender, seq, identityMap } = data;
214
371
 
215
372
  // Staleness check runs FIRST — compared against the high-water mark of
216
373
  // seqs we've seen (own echoes count too, see below). `seq` is optional
@@ -240,8 +397,8 @@ class LiveSync {
240
397
  }
241
398
 
242
399
  this._log(`Received update from: ${sender} (my clientId: ${this.clientId}, seq=${seq})`);
243
- this.applyUpdate(html, seq);
244
- if (this.onUpdate) this.onUpdate({ html, sender, seq });
400
+ this.applyUpdate(html, seq, identityMap);
401
+ if (this.onUpdate) this.onUpdate({ html, sender, seq, identityMap });
245
402
  };
246
403
 
247
404
  // Native EventSource auto-reconnects on transient errors
@@ -268,12 +425,17 @@ class LiveSync {
268
425
  return;
269
426
  }
270
427
 
271
- const { documentElement } = event.detail;
272
- if (!documentElement) return;
428
+ const { documentElement: clone } = event.detail;
429
+ if (!clone) return;
273
430
 
431
+ // Capture both synchronously inside the event handler — captureSnapshot's
432
+ // caller continues to mutate the clone (strip [save-remove], run hooks)
433
+ // after dispatchEvent returns, so reading outerHTML and walking children
434
+ // must happen now.
274
435
  this._log('snapshot-ready received, preparing to send');
275
- const html = documentElement.outerHTML;
276
- this.sendUpdate(html);
436
+ const html = clone.outerHTML;
437
+ const identityMap = this._buildIdentityMap(document.documentElement, clone);
438
+ this.sendUpdate(html, identityMap);
277
439
  };
278
440
 
279
441
  document.addEventListener('hyperclay:snapshot-ready', this._snapshotHandler);
@@ -283,7 +445,7 @@ class LiveSync {
283
445
  * Send full HTML to the server (debounced)
284
446
  * Only updates lastHtml after successful save
285
447
  */
286
- sendUpdate(html) {
448
+ sendUpdate(html, identityMap) {
287
449
  clearTimeout(this.debounceTimer);
288
450
 
289
451
  this.debounceTimer = setTimeout(() => {
@@ -300,7 +462,8 @@ class LiveSync {
300
462
  headers: { 'Content-Type': 'application/json', 'Page-URL': window.location.href },
301
463
  body: JSON.stringify({
302
464
  html: html,
303
- sender: this.clientId
465
+ sender: this.clientId,
466
+ identityMap: identityMap
304
467
  })
305
468
  }).then(response => {
306
469
  if (response.ok) {
@@ -316,39 +479,96 @@ class LiveSync {
316
479
  }
317
480
 
318
481
  /**
319
- * Apply an update received from the server.
320
- * Morphs the entire document (head and body).
482
+ * Apply an update received from the server. Morphs the entire document.
321
483
  *
322
- * Serialized via a promise chain so concurrent SSE events don't produce
323
- * overlapping morphs (morph can return a Promise when external scripts load).
484
+ * Updates land in a single pending slot. On each animation frame, the
485
+ * latest pending payload is morphed once; intermediate updates that
486
+ * arrived between frames are skipped because they would be replaced
487
+ * milliseconds later anyway. Burst arrivals collapse to roughly one morph
488
+ * per frame, so the receiver always shows current state instead of
489
+ * playing back a backlog of stale snapshots.
324
490
  *
325
491
  * @param {string} html - Full document HTML
326
492
  * @param {number} [seq] - Optional monotonic seq from the server
327
- * @returns {Promise<void>}
493
+ * @param {Object} [identityMap] - Optional element-identity map from sender
328
494
  */
329
- applyUpdate(html, seq) {
330
- // Serialize concurrent updates onto a single promise chain. `.catch` on
331
- // the combined result both logs the failure (so it isn't a silent
332
- // unhandled rejection) AND heals the chain so subsequent updates aren't
333
- // blocked by it. `_applyChain` and the returned promise are the same
334
- // caught promise — callers that fire-and-forget are safe.
335
- const result = this._applyChain
336
- .then(() => this._doApplyUpdate(html, seq))
337
- .catch((err) => {
338
- console.error('[LiveSync] applyUpdate failed:', err);
339
- });
340
- this._applyChain = result;
341
- return result;
495
+ applyUpdate(html, seq, identityMap) {
496
+ if (this.isDestroyed) return;
497
+ this._pendingHtml = html;
498
+ this._pendingSeq = seq;
499
+ this._pendingIdentityMap = identityMap;
500
+ this._scheduleNextFrame();
501
+ }
502
+
503
+ /**
504
+ * Schedule the next-frame morph if one isn't already pending. Skipped when
505
+ * a morph is in flight; the morph's post-completion check will reschedule
506
+ * if a newer payload arrived during it.
507
+ */
508
+ _scheduleNextFrame() {
509
+ if (this.isDestroyed) return;
510
+ if (this._rafHandle != null) return;
511
+ if (this._morphInFlight) return;
512
+ this._rafHandle = this._requestFrame(() => this._runPending());
513
+ }
514
+
515
+ /**
516
+ * Drain the pending slot once. Errors are caught and logged so a single
517
+ * failed morph does not stop the queue.
518
+ */
519
+ async _runPending() {
520
+ this._rafHandle = null;
521
+ if (this.isDestroyed) return;
522
+
523
+ const html = this._pendingHtml;
524
+ const seq = this._pendingSeq;
525
+ const identityMap = this._pendingIdentityMap;
526
+ this._pendingHtml = null;
527
+ this._pendingSeq = null;
528
+ this._pendingIdentityMap = null;
529
+ if (html == null) return;
530
+
531
+ this._morphInFlight = true;
532
+ try {
533
+ await this._doApplyUpdate(html, seq, identityMap);
534
+ } catch (err) {
535
+ console.error('[LiveSync] applyUpdate failed:', err);
536
+ } finally {
537
+ this._morphInFlight = false;
538
+ }
539
+
540
+ // A newer payload may have arrived during the morph. Schedule another
541
+ // frame to drain it. Without this, late-arriving updates would sit
542
+ // forever until the next applyUpdate call.
543
+ if (!this.isDestroyed && this._pendingHtml != null) {
544
+ this._scheduleNextFrame();
545
+ }
546
+ }
547
+
548
+ _requestFrame(cb) {
549
+ if (typeof window !== 'undefined' && typeof window.requestAnimationFrame === 'function') {
550
+ return window.requestAnimationFrame(cb);
551
+ }
552
+ return setTimeout(cb, 16);
553
+ }
554
+
555
+ _cancelFrame(handle) {
556
+ if (typeof window !== 'undefined' && typeof window.cancelAnimationFrame === 'function') {
557
+ window.cancelAnimationFrame(handle);
558
+ return;
559
+ }
560
+ clearTimeout(handle);
342
561
  }
343
562
 
344
563
  /**
345
- * Actual morph work. Do not call directly use applyUpdate() so calls
346
- * serialize.
564
+ * Actual morph work. Do not call directly. Use applyUpdate() so calls
565
+ * pass through the rAF queue and don't overlap.
347
566
  * @param {string} html
348
567
  * @param {number} [seq]
568
+ * @param {Object} [identityMap]
349
569
  * @returns {Promise<void>}
350
570
  */
351
- async _doApplyUpdate(html, seq) {
571
+ async _doApplyUpdate(html, seq, identityMap) {
352
572
  this._log('applyUpdate - pausing mutations and morphing');
353
573
  this.isPaused = true;
354
574
 
@@ -366,6 +586,32 @@ class LiveSync {
366
586
  const parser = new DOMParser();
367
587
  const newDoc = parser.parseFromString(html, 'text/html');
368
588
 
589
+ // Build the parsed-tree WeakMap from the incoming identityMap. The
590
+ // sender emitted paths off its clone, which is exactly what we just
591
+ // parsed, so the same path scheme indexes into both trees.
592
+ const parsedWeakMap = new WeakMap();
593
+ if (identityMap && typeof identityMap === 'object' && !Array.isArray(identityMap)) {
594
+ this._walkParsedTree(newDoc.documentElement, (el, path) => {
595
+ const id = identityMap[path];
596
+ if (id) parsedWeakMap.set(el, id);
597
+ });
598
+ }
599
+
600
+ const liveWeakMap = this.liveWeakMap;
601
+ // Priority: synthetic IDs win when present (they're updated after every
602
+ // morph via afterNodeMorphed). data-id / id is the durable fallback that
603
+ // covers the bootstrap window and any element that hasn't been paired yet.
604
+ const key = (el) =>
605
+ liveWeakMap.get(el) ||
606
+ parsedWeakMap.get(el) ||
607
+ (el.getAttribute && el.getAttribute('data-id')) ||
608
+ el.id ||
609
+ null;
610
+ const afterNodeMorphed = (oldEl, newEl) => {
611
+ const id = parsedWeakMap.get(newEl);
612
+ if (id) liveWeakMap.set(oldEl, id);
613
+ };
614
+
369
615
  try {
370
616
  // Morph entire document. We MUST await — HyperMorph.morph returns a
371
617
  // Promise when `scripts: { handle: true }` needs to wait for external
@@ -376,12 +622,24 @@ class LiveSync {
376
622
  morphStyle: 'outerHTML',
377
623
  ignoreActiveValue: true,
378
624
  head: { style: 'merge' },
379
- scripts: { handle: true, matchMode: 'smart' }
625
+ scripts: { handle: true, matchMode: 'smart' },
626
+ key,
627
+ callbacks: { afterNodeMorphed }
380
628
  });
381
629
 
382
630
  // Restore viewport. Done after morph so layout has settled.
383
631
  window.scrollTo(scrollX, scrollY);
384
632
 
633
+ // Fill in any IDs the matcher's afterNodeMorphed missed. Brand-new
634
+ // elements come in via hyper-morph's importNode optimization, which
635
+ // skips morphNode and thus afterNodeMorphed; their parsedWeakMap IDs
636
+ // never make it onto liveWeakMap. Without this pass, the receiver
637
+ // would mint fresh IDs on its next save for those elements,
638
+ // breaking convergence exactly for newly-added ambiguous siblings.
639
+ if (identityMap && typeof identityMap === 'object' && !Array.isArray(identityMap)) {
640
+ this._fillInIdsAfterMorph(document.documentElement, newDoc.documentElement, identityMap);
641
+ }
642
+
385
643
  // Only mark lastHtml after a successful morph so that a failed apply
386
644
  // doesn't desync our state and cause the next outbound save to be
387
645
  // mistakenly skipped as "unchanged". Note: lastSeenSeq is advanced at
@@ -449,6 +707,8 @@ if (typeof window !== 'undefined') {
449
707
  }
450
708
  }
451
709
 
452
- // Export for hyperclayjs module system
453
- export { liveSync };
710
+ // Export for hyperclayjs module system. The class itself is exported so
711
+ // tests can create fresh instances without driving the singleton's
712
+ // EventSource/snapshot wiring.
713
+ export { liveSync, LiveSync };
454
714
  export default liveSync;
package/src/hyperclay.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * DO NOT EDIT THIS FILE DIRECTLY — it is generated from build/hyperclay.template.js
3
3
  *
4
- * HyperclayJS v1.27.0 - Minimal Browser-Native Loader
4
+ * HyperclayJS v1.28.0 - Minimal Browser-Native Loader
5
5
  *
6
6
  * Modules auto-init when imported (no separate init call needed).
7
7
  * Include `export-to-window` feature to export to window.hyperclay.
@@ -61,6 +61,7 @@ const MODULE_PATHS = {
61
61
  "style-injection": "./dom-utilities/insertStyleTag.js",
62
62
  "form-data": "./dom-utilities/getDataFromForm.js",
63
63
  "hyper-morph": "./vendor/hyper-morph.vendor.js",
64
+ "hyper-html-api": "./vendor/hyper-html-api.vendor.js",
64
65
  "slugify": "./string-utilities/slugify.js",
65
66
  "copy-to-clipboard": "./string-utilities/copy-to-clipboard.js",
66
67
  "query-params": "./string-utilities/query.js",
@@ -187,6 +188,7 @@ const PRESETS = {
187
188
  "style-injection",
188
189
  "form-data",
189
190
  "hyper-morph",
191
+ "hyper-html-api",
190
192
  "slugify",
191
193
  "copy-to-clipboard",
192
194
  "query-params",
@@ -337,6 +339,7 @@ export const insertStyleTag = window.hyperclayModules['style-injection']?.insert
337
339
  export const getDataFromForm = window.hyperclayModules['form-data']?.getDataFromForm ?? window.hyperclayModules['form-data']?.default;
338
340
  export const HyperMorph = window.hyperclayModules['hyper-morph']?.HyperMorph ?? window.hyperclayModules['hyper-morph']?.default;
339
341
  export const morph = window.hyperclayModules['hyper-morph']?.morph ?? window.hyperclayModules['hyper-morph']?.default;
342
+ export const HyperHtmlApi = window.hyperclayModules['hyper-html-api']?.HyperHtmlApi ?? window.hyperclayModules['hyper-html-api']?.default;
340
343
  export const slugify = window.hyperclayModules['slugify']?.slugify ?? window.hyperclayModules['slugify']?.default;
341
344
  export const copyToClipboard = window.hyperclayModules['copy-to-clipboard']?.copyToClipboard ?? window.hyperclayModules['copy-to-clipboard']?.default;
342
345
  export const query = window.hyperclayModules['query-params']?.query ?? window.hyperclayModules['query-params']?.default;
package/src/ui/prompts.js CHANGED
@@ -26,17 +26,22 @@ function createModal(promptText, yesCallback, extraContent = "", includeInput =
26
26
  promptResult = document.querySelector(".micromodal__input").value;
27
27
  if (!promptResult) return false; // keep modal open on empty input
28
28
  }
29
- // Defer so the modal's close sequence completes before callbacks fire
30
- // chained ask()/consent() calls then land in a clean themodal instead of
31
- // piggybacking on this modal's onYes loop (themodal is a singleton).
32
- setTimeout(() => {
33
- if (yesCallback) {
34
- try { yesCallback(promptResult); }
35
- catch (err) { toast(err.message || 'An error occurred', 'error'); }
29
+ // Run the validation callback synchronously so a throw can keep the
30
+ // modal open (callers rely on this e.g. delete-site confirms by
31
+ // throwing when the typed name doesn't match).
32
+ if (yesCallback) {
33
+ try {
34
+ yesCallback(promptResult);
35
+ } catch (err) {
36
+ toast(err.message || 'An error occurred', 'error');
37
+ return false; // keep modal open, user can retry
36
38
  }
37
- resolve(promptResult);
38
- }, 0);
39
- return true; // Allow modal to close
39
+ }
40
+ // Defer resolve so downstream .then() handlers don't fire inside this
41
+ // modal's onYes loop themodal is a singleton, and chained ask()/
42
+ // consent() calls need a clean themodal to set up their state.
43
+ setTimeout(() => resolve(promptResult), 0);
44
+ return true; // allow modal to close
40
45
  });
41
46
 
42
47
  themodal.onNo = () => {
@@ -0,0 +1,13 @@
1
+ var HyperHtmlApi=(()=>{var B=Object.defineProperty;var ce=Object.getOwnPropertyDescriptor;var le=Object.getOwnPropertyNames;var ae=Object.prototype.hasOwnProperty;var ee=(e,t)=>{for(var r in t)B(e,r,{get:t[r],enumerable:!0})},ue=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of le(t))!ae.call(e,n)&&n!==r&&B(e,n,{get:()=>t[n],enumerable:!(o=ce(t,n))||o.enumerable});return e};var de=e=>ue(B({},"__esModule",{value:!0}),e);var Be={};ee(Be,{cms:()=>ie,default:()=>qe,engine:()=>se,upgrade:()=>fe});var C=["textContent","innerText","innerHTML","outerHTML","value","checked","selected","disabled","readOnly","type","tagName","nodeName","nodeType","nodeValue","childElementCount","id","className","classList","href","src","action","baseURI","offsetWidth","offsetHeight","clientWidth","clientHeight","scrollWidth","scrollHeight","dataset","currentSrc","duration","paused","title","documentURI","contentType"],O=new Set(C);var D={};ee(D,{EmptyListInsert:()=>b,MaxRuleDepthExceeded:()=>E,RulesParseError:()=>x,ShapeMismatch:()=>M,UnknownRulesVersion:()=>T,UpgradeAlreadyRegistered:()=>w});var x=class extends Error{constructor(t,r){super(t),this.name="RulesParseError",this.cause=r}},T=class extends Error{constructor(t){super(`unknown rules version: ${t}. Library supports "1".`),this.name="UnknownRulesVersion",this.version=t}},E=class extends Error{constructor(t){super(`rule depth exceeded 20 at path: ${t.join(".")}`),this.name="MaxRuleDepthExceeded",this.path=t}},M=class extends Error{constructor(t){super(`shape mismatch: ${t.length} field(s) failed validation`),this.name="ShapeMismatch",this.mismatches=t}},b=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(){super("upgrade transform already registered; only one registration is allowed per page."),this.name="UpgradeAlreadyRegistered"}};var pe=20;function A(e,t,r){return $(e,t,r,{depth:0,path:[]})}function $(e,t,r,o){if(o.depth>pe)throw new E(o.path);if(typeof r=="string")return he(e,t,r);if(Array.isArray(r)){let[n,s]=r;return e.find(t,n).map((i,f)=>$(e,i,s,{depth:o.depth+1,path:[...o.path,f]}))}if(typeof r=="object"&&r!==null){let n={};for(let[s,c]of Object.entries(r))n[s]=$(e,t,c,{depth:o.depth+1,path:[...o.path,s]});return n}return null}function he(e,t,r){if(r.endsWith("[]")){let n=r.slice(0,-2);return e.find(t,n).map(s=>e.text(s))}if(r.startsWith("@"))return te(e,t,r.slice(1));if(r.includes("@")){let[n,s]=r.split("@"),c=n?e.find(t,n):[t];return c.length===0?null:te(e,c[0],s)}if(r===".")return e.text(t);let o=e.find(t,r);return o.length===0?null:e.text(o[0])}function te(e,t,r){if(O.has(r)){let n=e.prop(t,r);return n==null?null:String(n)}let o=e.attr(t,r);return o||null}var ye=.5;function W(e,t,r,o,n,s,c){let i=e.find(t,r);if(n.length===0){i.forEach(d=>e.remove(d));return}if(n.length>i.length&&i.length===0)throw new b(s.path);let a=i.map(d=>me(e,d,o)),u=null;if(i.length>0){u=e.clone(i[0]);let d=e.stripIds(u);d>0&&console.warn(`[hyper-html-api] stripped ${d} id attribute(s) from cloned template at "${s.path.join(".")||"(root)"}"`)}let p=ge(n,a,o),l=e.parent(i[0]),R=Ae(e,l,i[0]),y=new Set,h=n.map((d,m)=>{let S=p[m];if(S>=0)return y.add(S),i[S];let v=e.clone(u);return e.stripIds(v),v});i.forEach((d,m)=>{y.has(m)||e.remove(d)}),y.forEach(d=>e.remove(i[d])),h.forEach((d,m)=>{e.insertAt(l,d,R+m)}),h.forEach((d,m)=>{if(o===null){let S=n[m];e.text(d,S==null?"":String(S))}else c(e,d,o,n[m],{depth:s.depth+1,path:[...s.path,m]})})}function me(e,t,r){return r===null?e.text(t):A(e,t,r)}function ge(e,t,r){let o=new Array(e.length).fill(-1),n=new Set;return e.forEach((s,c)=>{let i=-1,f=-1;t.forEach((a,u)=>{if(n.has(u))return;let p=Ee(s,a,r),l=p===f&&i>=0?Math.abs(u-c)<Math.abs(i-c):!1;(p>f||l)&&(f=p,i=u)}),f>=ye&&(o[c]=i,n.add(i))}),o}function Ee(e,t,r){if(r===null)return e===t?1:0;let o=Object.keys(r||{});if(o.length===0)return 0;let n=0;for(let s of o)JSON.stringify(e?.[s])===JSON.stringify(t?.[s])&&n++;return n/o.length}function Ae(e,t,r){let o=e.children(t);for(let n=0;n<o.length;n++)if(e.sameNode(o[n],r))return n;return-1}var Re=20,re=new Set(["checked","selected","disabled","readOnly","paused"]);function L(e,t,r,o){let n=[];if(F(r,o,[],n),n.length)throw new M(n);j(e,t,r,o,{depth:0,path:[]})}function j(e,t,r,o,n){if(n.depth>Re)throw new E(n.path);if(o!==void 0){if(typeof r=="string")return Se(e,t,r,o,n);if(Array.isArray(r)){let[s,c]=r;return W(e,t,s,c,o,n,j)}if(typeof r=="object"&&r!==null)for(let[s,c]of Object.entries(r))j(e,t,c,o==null?o:o[s],{depth:n.depth+1,path:[...n.path,s]})}}function Se(e,t,r,o,n){if(r.endsWith("[]")){let c=r.slice(0,-2);return W(e,t,c,null,o,n,j)}if(r.startsWith("@"))return ne(e,t,r.slice(1),o);if(r.includes("@")){let[c,i]=r.split("@"),f=c?e.find(t,c):[t];return f.length===0?void 0:ne(e,f[0],i,o)}if(r==="."){e.text(t,o==null?"":String(o));return}let s=e.find(t,r);s.length!==0&&e.text(s[0],o==null?"":String(o))}function ne(e,t,r,o){O.has(r)?e.prop(t,r,xe(r,o)):e.attr(t,r,o==null?"":String(o))}function xe(e,t){return t==null?re.has(e)?!1:"":re.has(e)?!!t:t}function F(e,t,r,o){if(!(t==null||t==="")){if(typeof e=="string"){if(e.endsWith("[]")){Array.isArray(t)?t.forEach((n,s)=>{typeof n=="object"&&n!==null&&o.push({path:N([...r,s]),expected:"scalar",got:I(n)})}):o.push({path:N(r),expected:"array",got:I(t)});return}typeof t=="object"&&o.push({path:N(r),expected:"scalar",got:I(t)});return}if(Array.isArray(e)){if(!Array.isArray(t)){o.push({path:N(r),expected:"array",got:I(t)});return}let n=e[1];t.forEach((s,c)=>F(n,s,[...r,c],o));return}if(typeof e=="object"&&e!==null){if(Array.isArray(t)||typeof t!="object"){o.push({path:N(r),expected:"object",got:I(t)});return}for(let[n,s]of Object.entries(e))F(s,t[n],[...r,n],o)}}}function I(e){return e===null?"null":Array.isArray(e)?"array":typeof e}function N(e){return e.join(".")}function P(e){try{return JSON.parse(e)}catch(t){throw new x(`hyper-html-api script tags require strict JSON. Relaxed JSON (unquoted keys, single quotes, trailing commas) is only supported in the ?data= URL parameter. Original error: ${t.message}`,t)}}function J(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 r(n){let s=[],c=0;for(;c<n.length;){let i=n[c];if(/\s/.test(i)){c++;continue}if("{}".includes(i)){s.push({type:i,value:i}),c++;continue}if(i==="["){let p=!1,l=c+1;for(;l<n.length&&/\s/.test(n[l]);)l++;if(l<n.length&&/[a-zA-Z_]/.test(n[l])&&(p=!0),!p){s.push({type:i,value:i}),c++;continue}}if(i==="]"){s.push({type:i,value:i}),c++;continue}if(i===":"){s.push({type:t.COLON,value:i}),c++;continue}if(i===","){s.push({type:t.COMMA,value:i}),c++;continue}if(i==='"'||i==="'"){let p=i,l=c+1;for(;l<n.length&&n[l]!==p;)n[l]==="\\"&&l++,l++;s.push({type:t.STRING,value:n.substring(c+1,l),quoted:!0}),c=l+1;continue}let f=c,a;for(;f<n.length&&!/[{},]/.test(n[f]);)if(n[f]===":"){let p=[":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"],l=!1;for(let R of p){let y=R.substring(1);if(n.substring(f+1,f+1+y.length)===y){l=!0,f+=y.length;break}}if(!l)break}else if(n[f]==="["){for(f++;f<n.length&&n[f]!=="]";){if(n[f]==='"'||n[f]==="'"){let p=n[f];for(f++;f<n.length&&n[f]!==p;)n[f]==="\\"&&f++,f++}f++}f<n.length&&n[f]==="]"&&f++}else f++;a=n.substring(c,f);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),s.push({type:u,value:a,quoted:!1}),c=f}return s}function o(n){let s="";for(let c=0;c<n.length;c++){let i=n[c];if("{}".includes(i.type)||"[]".includes(i.type)){s+=i.value;continue}if(i.type===t.COLON){s+=i.value;continue}if(i.type===t.COMMA){s+=i.value;continue}if(i.type===t.STRING&&i.quoted){s+=`"${i.value}"`;continue}if(i.type===t.NUMBER||i.type===t.BOOLEAN){s+=i.value;continue}if(i.type===t.SELECTOR||i.type===t.IDENTIFIER){s+=`"${i.value}"`;continue}s+=`"${i.value}"`}return s}try{let n=r(e),s=o(n);return JSON.parse(s)}catch(n){throw new x("Invalid extraction rules syntax: "+n.message,n)}}var Oe="hyper-html-api",Te="1";function _(e,t){let r=e.find(t,`script#${Oe}`,{includeRulesTag:!0});if(r.length===0)return null;let o=r[0],n=e.attr(o,"data-rules-version");if(n!==Te)throw new T(n);let s=e.text(o);return{rules:P(s),tagNode:o}}var G=null;function V(e){if(typeof e!="function")throw new TypeError("registerUpgrade expects a function (v1Data) => v2Data");if(G)throw new w;G=e}function H(){return G}function U(e,t){let r={carriedOver:0,discarded:0,listItems:0},o=Y(t,e,r);return X(t,e,r),{data:o,summary:r}}function Y(e,t,r){if(typeof e=="string")return e.endsWith("[]")?Array.isArray(t)?(r.listItems+=t.length,r.carriedOver+=t.length,t):void 0:t==null?void 0:(r.carriedOver++,t);if(Array.isArray(e)){let[,o]=e;return Array.isArray(t)?(r.listItems+=t.length,t.map(n=>Y(o,n,r))):void 0}if(typeof e=="object"&&e!==null){let o={};for(let[n,s]of Object.entries(e)){let c=Y(s,t?.[n],r);c!==void 0&&(o[n]=c)}return o}}function X(e,t,r){if(t!=null){if(typeof e=="object"&&e!==null&&!Array.isArray(e)){if(typeof t!="object"||Array.isArray(t))return;let o=new Set(Object.keys(e));for(let n of Object.keys(t))o.has(n)?X(e[n],t[n],r):r.discarded+=k(t[n]);return}if(Array.isArray(e)&&Array.isArray(t)){let o=e[1];t.forEach(n=>X(o,n,r));return}typeof e=="string"&&!e.endsWith("[]")&&typeof t=="object"&&t!==null&&(r.discarded+=k(t))}}function k(e){return e==null?0:Array.isArray(e)?e.reduce((t,r)=>t+k(r),0):typeof e=="object"?Object.values(e).reduce((t,r)=>t+k(r),0):1}async function z({sourceUrl:e,v1Data:t,v1Version:r=null,parent:o=typeof window<"u"?window:null,timeoutMs:n=15e3,hidden:s=!0}={}){if(!o)throw new Error("upgrade.run requires a window context");if(!e)throw new Error("upgrade.run requires sourceUrl");let c=be(e,o.location.origin),i=new URL(c,o.location.href).origin,f=o.document.createElement("iframe");s&&(f.style.cssText="position:absolute;left:-9999px;top:-9999px;width:1px;height:1px;border:0;"),f.setAttribute("aria-hidden","true"),f.src=c,o.document.body.appendChild(f);let a=()=>{};try{return await new Promise((u,p)=>{let l=setTimeout(()=>{p(new Error(`upgrade.run: helper iframe timed out after ${n}ms`))},n),R=y=>{if(y.source!==f.contentWindow||y.origin!==i)return;let h=y.data;if(!(!h||typeof h!="object")){if(h.type==="hha:upgrade-ready"){f.contentWindow.postMessage({type:"hha:upgrade-data",v1Data:t,v1Version:r},i);return}if(h.type==="hha:upgrade-result"){clearTimeout(l),u({html:h.html,summary:h.summary});return}if(h.type==="hha:upgrade-error"){clearTimeout(l);let d=new Error(h.message||"helper error");d.name=h.name||"UpgradeHelperError",p(d);return}}};o.addEventListener("message",R),a=()=>{o.removeEventListener("message",R),clearTimeout(l),f.parentNode&&f.parentNode.removeChild(f)}})}finally{a()}}function be(e,t){let r=new URL(e,typeof location<"u"?location.href:void 0);return r.searchParams.set("_hyperHtmlApi","upgrade-helper"),r.searchParams.set("parentOrigin",t),r.toString()}var we="hyper-html-api";function Ie(e){return e&&e.nodeType===1&&e.id===we&&e.tagName==="SCRIPT"}function Ne(e){return e?(e.nodeType===9||e.nodeType===11,e):null}var Le={find(e,t,r={}){let o=Ne(e);if(!o||!o.querySelectorAll)return[];let n=Array.from(o.querySelectorAll(t));return r.includeRulesTag?n:n.filter(s=>!Ie(s))},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,r){if(r===void 0)return e.hasAttribute&&e.hasAttribute(t)?e.getAttribute(t):null;e.setAttribute(t,r)},prop(e,t,r){if(r===void 0){let o=e?e[t]:void 0;return o!==void 0?o:null}e[t]=r},clone(e){return e.cloneNode(!0)},insertAt(e,t,r){let o=e.children[r]||null;e.insertBefore(t,o)},remove(e){e&&e.parentNode&&e.parentNode.removeChild(e)},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}},g=Le;var Pe="_hyperHtmlApi",_e="upgrade-helper",Ue="parentOrigin";function q(e=typeof location<"u"?location:null){if(!e)return!1;try{return new URLSearchParams(e.search).get(Pe)===_e}catch{return!1}}function oe(e=typeof location<"u"?location:null){return e?new URLSearchParams(e.search).get(Ue):null}function Z({win:e,doc:t,parentOrigin:r}={}){if(e=e||(typeof window<"u"?window:null),t=t||(typeof document<"u"?document:null),!e||!t||(r=r||oe(e.location),!r))return;let o=()=>Ce({win:e,doc:t,parentOrigin:r});t.readyState==="loading"?t.addEventListener("DOMContentLoaded",o,{once:!0}):o()}function Ce({win:e,doc:t,parentOrigin:r}){let o;try{o=_(g,t.body)}catch(f){return K(e,r,f)}if(!o)return K(e,r,new Error("helper-mode: no rules tag in v2 document"));let n=o.rules,s=je(t,"hyper-version"),c=!!H(),i=f=>{if(f.source!==e.parent||f.origin!==r)return;let a=f.data;if(!(!a||a.type!=="hha:upgrade-data")){e.removeEventListener("message",i);try{let u=De({doc:t,rules:n,v1Data:a.v1Data});e.parent.postMessage({type:"hha:upgrade-result",html:u.html,summary:u.summary},r)}catch(u){K(e,r,u)}}};e.addEventListener("message",i),e.parent.postMessage({type:"hha:upgrade-ready",rules:n,version:s,hasTransform:c},r)}function De({doc:e,rules:t,v1Data:r}){let o=H(),n=r,s=!1;o&&(n=o(r),s=!0);let{data:c,summary:i}=U(n,t);L(g,e.body,t,c);let f=A(g,e.body,t);return{html:`<!DOCTYPE html>
2
+ `+e.documentElement.outerHTML,summary:{...i,transformApplied:s,appliedFieldCount:Q(f)}}}function K(e,t,r){e.parent.postMessage({type:"hha:upgrade-error",name:r?.name||"Error",message:r?.message||String(r)},t)}function je(e,t){let r=e.querySelector(`meta[name="${t}"]`);return r?r.getAttribute("content"):null}function Q(e){return e==null?0:Array.isArray(e)?e.reduce((t,r)=>t+Q(r),0):typeof e=="object"?Object.values(e).reduce((t,r)=>t+Q(r),0):1}var se={extract:(e,t)=>A(g,e,t),apply:(e,t,r)=>L(g,e,t,r),findRulesIn:e=>_(g,e),parseStrict:P,parseRelaxed:J,errors:D,DOM_PROPERTIES:C},ie={},fe={registerUpgrade:V,run:z,shapeMatch:U,isHelperMode:q};typeof window<"u"&&q(window.location)&&Z({win:window,doc:document});var ke={engine:se,cms:ie,upgrade:fe},qe=ke;return de(Be);})();
3
+
4
+ // Auto-export to window unless suppressed by loader
5
+ if (!window.__hyperclayNoAutoExport) {
6
+ window.hyperclay = window.hyperclay || {};
7
+ window.hyperclay.htmlApi = HyperHtmlApi;
8
+ window.HyperHtmlApi = HyperHtmlApi;
9
+ window.h = window.hyperclay;
10
+ }
11
+
12
+ export { HyperHtmlApi };
13
+ export default HyperHtmlApi;
@@ -1,9 +1,9 @@
1
- var HyperMorph=(()=>{var V=Object.defineProperty;var K=Object.getOwnPropertyDescriptor;var G=Object.getOwnPropertyNames;var Y=Object.prototype.hasOwnProperty;var J=(s,o)=>{for(var u in o)V(s,u,{get:o[u],enumerable:!0})},Q=(s,o,u,d)=>{if(o&&typeof o=="object"||typeof o=="function")for(let h of G(o))!Y.call(s,h)&&h!==u&&V(s,h,{get:()=>o[h],enumerable:!(d=K(o,h))||d.enumerable});return s};var X=s=>Q(V({},"__esModule",{value:!0}),s);var pe={};J(pe,{HyperMorph:()=>$,default:()=>he,defaults:()=>fe,morph:()=>de});var F={includeClasses:!0,includeAttributes:["href","src","name","type","role","aria-label","alt","title"],excludeAttributePrefixes:["data-morph-","data-hyper-","data-im-"],textHintLength:64,excludeIds:!0,maxPathDepth:4,landmarks:["HEADER","NAV","MAIN","ASIDE","FOOTER","SECTION","ARTICLE"],weights:{signature:100,pathSegment:10,textMatch:20,textMismatch:25,uniqueCandidate:50,positionPenalty:1},minConfidence:101};function Z(s){let o=5381;for(let u=0;u<s.length;u++)o=(o<<5)+o^s.charCodeAt(u);return Math.abs(o).toString(36)}function ee(s){if(s.classList&&s.classList.length>0)return Array.from(s.classList).sort().join(" ");let o=s.getAttribute?.("class");return o?o.split(/\s+/).filter(Boolean).sort().join(" "):""}function te(s,o){let u=[];for(let d of s.attributes||[]){let h=d.name;h==="id"||h==="class"||o.excludeAttributePrefixes.some(y=>h.startsWith(y))||o.includeAttributes.includes(h)&&u.push(`${h}=${d.value}`)}return u.sort().join("|")}function ne(s,o){return(s.textContent||"").replace(/\s+/g," ").trim().slice(0,o.textHintLength)}function re(s,o){let u=[s.tagName];return o.includeClasses&&u.push(ee(s)),u.push(te(s,o)),Z(u.join("|"))}function se(s){let o=s.tagName,u=1,d=s.previousElementSibling;for(;d;)d.tagName===o&&u++,d=d.previousElementSibling;return u}function ie(s,o){return s.id||s.getAttribute?.("role")?!0:o.landmarks.includes(s.tagName)}function ae(s){if(s.id)return`#${s.id}`;let o=s.getAttribute?.("role");return o?`@${o}`:s.tagName}function oe(s,o){let u=[],d=s;for(;d&&d.tagName&&u.length<o.maxPathDepth;){let h=`${d.tagName}:${se(d)}`;if(u.unshift(h),d!==s&&ie(d,o)){u.unshift(ae(d));break}d=d.parentElement}return u}function ue(s,o){let u=0,d=s.length-1,h=o.length-1;for(;d>=0&&h>=0&&s[d]===o[h];)u++,d--,h--;return u}function C(s,o,u){if(u.has(s))return u.get(s);let d={signature:re(s,o),path:oe(s,o),textHint:ne(s,o)};return u.set(s,d),d}function z(s,o,u,d){if(d.has(s))return d.get(s);let h=new Map,y=s.querySelectorAll("*"),H=0;for(let S of y){let I=C(S,o,u);I.domIndex=H++,h.has(I.signature)||h.set(I.signature,[]),h.get(I.signature).push(S)}return d.set(s,h),h}function ce(s,o,u){u.delete(s),o.delete(s);let d=s.querySelectorAll("*");for(let h of d)o.delete(h)}function q(s,o,u,d,h){let y=C(s,u,d),H=C(o,u,d),S=u.weights,I={},E=0;if(y.signature!==H.signature)return{score:0,breakdown:{rejected:"signature mismatch"}};E+=S.signature,I.signature=S.signature;let T=ue(y.path,H.path)*S.pathSegment;E+=T,I.path=T;let k=!0;if(y.textHint&&H.textHint?y.textHint===H.textHint?(E+=S.textMatch,I.text=S.textMatch):(E-=S.textMismatch,I.text=-S.textMismatch,k=!1):y.textHint!==H.textHint&&(E-=S.textMismatch,I.text=-S.textMismatch,k=!1),h.candidateCount===1&&k&&(E+=S.uniqueCandidate,I.unique=S.uniqueCandidate),typeof y.domIndex=="number"&&typeof H.domIndex=="number"){let N=Math.abs(y.domIndex-H.domIndex),R=Math.min(N*S.positionPenalty,20);E-=R,I.drift=-R}return{score:E,breakdown:I}}function D(s,o,u,d,h){if(u.excludeIds&&s.id)return null;let y=z(o,u,d,h),H=C(s,u,d);if(typeof H.domIndex!="number"){let k=0,N=s.previousElementSibling;for(;N;)k++,N=N.previousElementSibling;H.domIndex=k}let S=y.get(H.signature)||[],I=u.excludeIds?S.filter(k=>!k.id):S;if(I.length===0)return null;let E=null,x=0,T=null;for(let k of I){let{score:N,breakdown:R}=q(s,k,u,d,{candidateCount:I.length});N>x&&(x=N,E=k,T=R)}return x<u.minConfidence?null:{element:E,confidence:x,breakdown:T}}function W(s,o,u,d,h){let y=o.querySelectorAll("*"),H=z(s,u,d,h),S=0;for(let T of y){let k=C(T,u,d);k.domIndex=S++}let I=[];for(let T of y){if(u.excludeIds&&T.id)continue;let k=C(T,u,d),N=H.get(k.signature)||[],R=u.excludeIds?N.filter(B=>!B.id):N;for(let B of R){let{score:l,breakdown:A}=q(T,B,u,d,{candidateCount:R.length});l>=u.minConfidence&&I.push({newEl:T,oldEl:B,score:l,breakdown:A})}}I.sort((T,k)=>k.score-T.score);let E=new Map,x=new Set;for(let{newEl:T,oldEl:k}of I)E.has(T)||x.has(k)||(E.set(T,k),x.add(k));return E}function P(s,o,u,d){let h=C(s,u,d),y=C(o,u,d),{score:H,breakdown:S}=q(s,o,u,d,{candidateCount:1});return{matches:H>=u.minConfidence,score:H,breakdown:S,newMeta:{signature:h.signature,path:h.path,textHint:h.textHint},oldMeta:{signature:y.signature,path:y.path,textHint:y.textHint}}}function U(s={}){let o={...F,...s,weights:{...F.weights,...s.weights}},u=new WeakMap,d=new WeakMap;return{findMatch:(h,y)=>D(h,y,o,u,d),computeMatches:(h,y)=>W(h,y,o,u,d),explain:(h,y)=>P(h,y,o,u),invalidate:h=>ce(h,u,d),session:()=>{let h=new WeakMap,y=new WeakMap;return{findMatch:(H,S)=>D(H,S,o,h,y),computeMatches:(H,S)=>W(H,S,o,h,y),explain:(H,S)=>P(H,S,o,h)}},getConfig:()=>({...o})}}var le=U(),$=(function(){"use strict";let s=()=>{};function o(l){if(!(l instanceof Element))return!1;if(l.hasAttribute("save-ignore"))return!0;if(l.tagName==="LINK"||l.tagName==="SCRIPT"){let A=l.getAttribute("src")||l.getAttribute("href")||"";if(A.startsWith("chrome-extension://")||A.startsWith("moz-extension://")||A.startsWith("safari-web-extension://"))return!0}return!1}function u(l,A){if(A!=="smart")return l.outerHTML;let p=l.getAttribute("src"),v=l.getAttribute("type")||"text/javascript";if(p)try{let b=new URL(p,window.location.href);return`ext:${v}:${b.origin}${b.pathname}${b.search}`}catch{return`ext:${v}:${p}`}else{let b=l.textContent.trim(),g=5381;for(let r=0;r<b.length;r++)g=(g<<5)+g^b.charCodeAt(r);return`inline:${v}:${Math.abs(g).toString(36)}`}}let d={morphStyle:"outerHTML",callbacks:{beforeNodeAdded:s,afterNodeAdded:s,beforeNodeMorphed:s,afterNodeMorphed:s,beforeNodeRemoved:s,afterNodeRemoved:s,beforeAttributeUpdated:s},head:{style:"merge",shouldPreserve:l=>l.getAttribute("im-preserve")==="true",shouldReAppend:l=>l.getAttribute("im-re-append")==="true",shouldRemove:s,afterHeadMorphed:s},scripts:{handle:!1,matchMode:"outerHTML",shouldPreserve:l=>l.getAttribute("im-preserve")==="true",shouldReAppend:l=>l.getAttribute("im-re-append")==="true",shouldRemove:s,afterScriptsHandled:s},restoreFocus:!0},h={computeMatches(l,A){let{computeMatches:p}=le.session();return p(l,A)}};function y(l,A,p={}){l=R(l);let v=B(A),b=N(l,v,p),g=b.scripts.matchMode,r=new Set(Array.from(l.querySelectorAll("script")).map(n=>u(n,g))),c=S(b,()=>x(b,l,v,n=>n.morphStyle==="innerHTML"?(I(n,l,v),Array.from(l.childNodes)):H(n,l,v)));b.pantry.remove();let e=k(l,r,b);return e.length>0?c instanceof Promise?c.then(n=>Promise.all(e).then(()=>n)):Promise.all(e).then(()=>c):c}function H(l,A,p){let v=B(A);return I(l,v,p,A,A.nextSibling),Array.from(v.childNodes)}function S(l,A){if(!l.config.restoreFocus)return A();let p=document.activeElement;if(!(p instanceof HTMLInputElement||p instanceof HTMLTextAreaElement))return A();let{id:v,selectionStart:b,selectionEnd:g}=p,r=A();return v&&v!==document.activeElement?.getAttribute("id")&&(p=l.target.querySelector(`[id="${v}"]`),p?.focus()),p&&!p.selectionEnd&&g!=null&&p.setSelectionRange(b,g),r}let I=(function(){function l(e,n,i,t=null,a=null){n instanceof HTMLTemplateElement&&i instanceof HTMLTemplateElement&&(n=n.content,i=i.content),t||=n.firstChild;for(let f of i.childNodes){if(o(f))continue;if(t&&t!=a){let M=p(e,f,t,a);if(M){M!==t&&b(e,t,M),E(M,f,e),t=M.nextSibling;continue}}if(f instanceof Element){let M=f.getAttribute("id");if(e.persistentIds.has(M)){let w=g(n,M,t,e);E(w,f,e),t=w.nextSibling;continue}if(!e.idMap.has(f)){let w=e.hyperMatches.get(f);if(w&&!e.idMap.has(w)){c(n,w,t),E(w,f,e),t=w.nextSibling;continue}}}let m=A(n,f,t,e);m&&(t=m.nextSibling)}for(;t&&t!=a;){let f=t;t=t.nextSibling,o(f)||v(e,f)}}function A(e,n,i,t){if(t.callbacks.beforeNodeAdded(n)===!1)return null;if(t.idMap.has(n)){let a=document.createElement(n.tagName);return e.insertBefore(a,i),E(a,n,t),t.callbacks.afterNodeAdded(a),a}else{let a=document.importNode(n,!0);return e.insertBefore(a,i),t.callbacks.afterNodeAdded(a),a}}let p=(function(){function e(t,a,f,m){let M=a instanceof Element&&!t.idMap.has(a)?t.hyperMatches.get(a):null,w=null,O=a.nextSibling,j=0,L=f;for(;L&&L!=m;){if(i(L,a)){if(n(t,L,a)||L===M&&!t.idMap.has(L))return L;if(w===null){let _=L instanceof Element&&t.hyperMatchedOldElements.has(L);!t.idMap.has(L)&&!_&&(w=L)}}if(w===null&&O&&i(L,O)&&(j++,O=O.nextSibling,j>=2&&(w=void 0)),t.activeElementAndParents.includes(L))break;L=L.nextSibling}return w||null}function n(t,a,f){let m=t.idMap.get(a),M=t.idMap.get(f);if(!M||!m)return!1;for(let w of m)if(M.has(w))return!0;return!1}function i(t,a){let f=t,m=a;return f.nodeType===m.nodeType&&f.tagName===m.tagName&&(!f.getAttribute?.("id")||f.getAttribute?.("id")===m.getAttribute?.("id"))}return e})();function v(e,n){let i=n instanceof Element&&e.hyperMatchedOldElements.has(n)&&!e.idMap.has(n);if(e.idMap.has(n)||i)c(e.pantry,n,null);else{if(e.callbacks.beforeNodeRemoved(n)===!1)return;n.parentNode?.removeChild(n),e.callbacks.afterNodeRemoved(n)}}function b(e,n,i){let t=n;for(;t&&t!==i;){let a=t;t=t.nextSibling,o(a)||v(e,a)}return t}function g(e,n,i,t){let a=t.target.getAttribute?.("id")===n&&t.target||t.target.querySelector(`[id="${n}"]`)||t.pantry.querySelector(`[id="${n}"]`);return r(a,t),c(e,a,i),a}function r(e,n){let i=e.getAttribute("id");for(;e=e.parentNode;){let t=n.idMap.get(e);t&&(t.delete(i),t.size||n.idMap.delete(e))}}function c(e,n,i){if(e.moveBefore)try{e.moveBefore(n,i)}catch{e.insertBefore(n,i)}else e.insertBefore(n,i)}return l})(),E=(function(){function l(r,c,e){return e.ignoreActive&&r===document.activeElement?null:(e.callbacks.beforeNodeMorphed(r,c)===!1||(r instanceof HTMLHeadElement&&e.head.ignore||(r instanceof HTMLHeadElement&&e.head.style!=="morph"?T(r,c,e):(A(r,c,e),g(r,e)||I(e,r,c))),e.callbacks.afterNodeMorphed(r,c)),r)}function A(r,c,e){let n=c.nodeType;if(n===1){let i=r,t=c,a=i.attributes,f=t.attributes;for(let m of f)b(m.name,i,"update",e)||i.getAttribute(m.name)!==m.value&&i.setAttribute(m.name,m.value);for(let m=a.length-1;0<=m;m--){let M=a[m];if(M&&!t.hasAttribute(M.name)){if(b(M.name,i,"remove",e))continue;i.removeAttribute(M.name)}}g(i,e)||p(i,t,e)}(n===8||n===3)&&r.nodeValue!==c.nodeValue&&(r.nodeValue=c.nodeValue)}function p(r,c,e){if(r instanceof HTMLInputElement&&c instanceof HTMLInputElement&&c.type!=="file"){let n=c.value,i=r.value;v(r,c,"checked",e),v(r,c,"disabled",e),c.hasAttribute("value")?i!==n&&(b("value",r,"update",e)||(r.setAttribute("value",n),r.value=n)):b("value",r,"remove",e)||(r.value="",r.removeAttribute("value"))}else if(r instanceof HTMLOptionElement&&c instanceof HTMLOptionElement)v(r,c,"selected",e);else if(r instanceof HTMLTextAreaElement&&c instanceof HTMLTextAreaElement){let n=c.value,i=r.value;if(b("value",r,"update",e))return;n!==i&&(r.value=n),r.firstChild&&r.firstChild.nodeValue!==n&&(r.firstChild.nodeValue=n)}}function v(r,c,e,n){let i=c[e],t=r[e];if(i!==t){let a=b(e,r,"update",n);a||(r[e]=c[e]),i?a||r.setAttribute(e,""):b(e,r,"remove",n)||r.removeAttribute(e)}}function b(r,c,e,n){return r==="value"&&n.ignoreActiveValue&&c===document.activeElement?!0:n.callbacks.beforeAttributeUpdated(r,c,e)===!1}function g(r,c){return!!c.ignoreActiveValue&&r===document.activeElement&&r!==document.body}return l})();function x(l,A,p,v){if(l.head.block){let b=A.querySelector("head"),g=p.querySelector("head");if(b&&g){let r=T(b,g,l);return Promise.all(r).then(()=>{let c=Object.assign(l,{head:{block:!1,ignore:!0}});return v(c)})}}return v(l)}function T(l,A,p){let v=[],b=[],g=[],r=[],c=p.scripts.matchMode,e=t=>{if(t.tagName==="SCRIPT")return u(t,c);if(t.tagName==="LINK"&&c==="smart"){let a=t.getAttribute("href");if(a)try{let f=new URL(a,window.location.href);return`link:${t.getAttribute("rel")||""}:${f.origin}${f.pathname}${f.search}`}catch{}}return t.outerHTML},n=new Map;for(let t of A.children)o(t)||n.set(e(t),t);for(let t of l.children){let a=e(t),f=n.has(a),m=p.head.shouldReAppend(t),M=p.head.shouldPreserve(t);f||M?m?b.push(t):(n.delete(a),g.push(t)):p.head.style==="append"?m&&(b.push(t),r.push(t)):p.head.shouldRemove(t)!==!1&&!o(t)&&b.push(t)}r.push(...n.values());let i=[];for(let t of r){let a=document.createRange().createContextualFragment(t.outerHTML).firstChild;if(p.callbacks.beforeNodeAdded(a)!==!1){if("href"in a&&a.href||"src"in a&&a.src){let f,m=new Promise(function(M){f=M});a.addEventListener("load",function(){f()}),i.push(m)}l.appendChild(a),p.callbacks.afterNodeAdded(a),v.push(a)}}for(let t of b)p.callbacks.beforeNodeRemoved(t)!==!1&&(l.removeChild(t),p.callbacks.afterNodeRemoved(t));return p.head.afterHeadMorphed(l,{added:v,kept:g,removed:b}),i}function k(l,A,p){if(!p.scripts.handle)return[];let v=[],b=[],g=[],r=[],c=p.scripts.matchMode,e=Array.from(l.querySelectorAll("script"));for(let i of e){let t=u(i,c),a=A.has(t),f=p.scripts.shouldPreserve(i),m=p.scripts.shouldReAppend(i);a||f?m?(b.push(i),r.push(i)):g.push(i):r.push(i)}for(let i of A){let t=e.some(a=>a.outerHTML===i)}let n=[];for(let i of r){if(p.callbacks.beforeNodeAdded(i)===!1)continue;let t=document.createRange().createContextualFragment(i.outerHTML).firstChild;if(t.src){let a,f=new Promise(function(m){a=m});t.addEventListener("load",function(){a()}),t.addEventListener("error",function(){a()}),n.push(f)}i.replaceWith(t),p.callbacks.afterNodeAdded(t),v.push(t)}return p.scripts.afterScriptsHandled(l,{added:v,kept:g,removed:b}),n}let N=(function(){function l(e,n,i){let{persistentIds:t,idMap:a}=r(e,n),f=h.computeMatches(e,n),m=new Set;for(let O of f.values())m.add(O);let M=A(i),w=M.morphStyle||"outerHTML";if(!["innerHTML","outerHTML"].includes(w))throw`Do not understand how to morph style ${w}`;return{target:e,newContent:n,config:M,morphStyle:w,ignoreActive:M.ignoreActive,ignoreActiveValue:M.ignoreActiveValue,restoreFocus:M.restoreFocus,idMap:a,persistentIds:t,hyperMatches:f,hyperMatchedOldElements:m,pantry:p(),activeElementAndParents:v(e),callbacks:M.callbacks,head:M.head,scripts:M.scripts}}function A(e){let n=Object.assign({},d);return Object.assign(n,e),n.callbacks=Object.assign({},d.callbacks,e.callbacks),n.head=Object.assign({},d.head,e.head),n.scripts=Object.assign({},d.scripts,e.scripts),n}function p(){let e=document.createElement("div");return e.hidden=!0,document.body.insertAdjacentElement("afterend",e),e}function v(e){let n=[],i=document.activeElement;if(i?.tagName!=="BODY"&&e.contains(i))for(;i&&(n.push(i),i!==e);)i=i.parentElement;return n}function b(e){let n=Array.from(e.querySelectorAll("[id]"));return e.getAttribute?.("id")&&n.push(e),n}function g(e,n,i,t){for(let a of t){let f=a.getAttribute("id");if(n.has(f)){let m=a;for(;m;){let M=e.get(m);if(M==null&&(M=new Set,e.set(m,M)),M.add(f),m===i)break;m=m.parentElement}}}}function r(e,n){let i=b(e),t=b(n),a=c(i,t),f=new Map;g(f,a,e,i);let m=n.__hyperMorphRoot||n;return g(f,a,m,t),{persistentIds:a,idMap:f}}function c(e,n){let i=new Set,t=new Map;for(let{id:f,tagName:m}of e)t.has(f)?i.add(f):t.set(f,m);let a=new Set;for(let{id:f,tagName:m}of n)a.has(f)?i.add(f):t.get(f)===m&&a.add(f);for(let f of i)a.delete(f);return a}return l})(),{normalizeElement:R,normalizeParent:B}=(function(){let l=new WeakSet;function A(g){return g instanceof Document?g.documentElement:g}function p(g){if(g==null)return document.createElement("div");if(typeof g=="string")return p(b(g));if(l.has(g))return g;if(g instanceof Node){if(g.parentNode)return new v(g);{let r=document.createElement("div");return r.append(g),r}}else{let r=document.createElement("div");for(let c of[...g])r.append(c);return r}}class v{constructor(r){this.originalNode=r,this.realParentNode=r.parentNode,this.previousSibling=r.previousSibling,this.nextSibling=r.nextSibling}get childNodes(){let r=[],c=this.previousSibling?this.previousSibling.nextSibling:this.realParentNode.firstChild;for(;c&&c!=this.nextSibling;)r.push(c),c=c.nextSibling;return r}querySelectorAll(r){return this.childNodes.reduce((c,e)=>{if(e instanceof Element){e.matches(r)&&c.push(e);let n=e.querySelectorAll(r);for(let i=0;i<n.length;i++)c.push(n[i])}return c},[])}insertBefore(r,c){return this.realParentNode.insertBefore(r,c)}moveBefore(r,c){return this.realParentNode.moveBefore(r,c)}get __hyperMorphRoot(){return this.originalNode}}function b(g){let r=new DOMParser,c=g.replace(/<svg(\s[^>]*>|>)([\s\S]*?)<\/svg>/gim,"");if(c.match(/<\/html>/)||c.match(/<\/head>/)||c.match(/<\/body>/)){let e=r.parseFromString(g,"text/html");if(c.match(/<\/html>/))return l.add(e),e;{let n=e.firstChild;return n&&l.add(n),n}}else{let n=r.parseFromString("<body><template>"+g+"</template></body>","text/html").body.querySelector("template").content;return l.add(n),n}}return{normalizeElement:A,normalizeParent:p}})();return{morph:y,defaults:d}})();var de=$.morph,fe=$.defaults,he=$;return X(pe);})();
1
+ var HyperMorph=(()=>{var z=Object.defineProperty;var ne=Object.getOwnPropertyDescriptor;var re=Object.getOwnPropertyNames;var se=Object.prototype.hasOwnProperty;var ie=(s,c)=>{for(var a in c)z(s,a,{get:c[a],enumerable:!0})},ae=(s,c,a,d)=>{if(c&&typeof c=="object"||typeof c=="function")for(let h of re(c))!se.call(s,h)&&h!==a&&z(s,h,{get:()=>c[h],enumerable:!(d=ne(c,h))||d.enumerable});return s};var oe=s=>ae(z({},"__esModule",{value:!0}),s);var we={};ie(we,{HyperMorph:()=>W,default:()=>ke,defaults:()=>Se,morph:()=>ye});var J={includeClasses:!0,includeAttributes:["href","src","name","type","role","aria-label","alt","title"],excludeAttributePrefixes:["data-morph-","data-hyper-","data-im-"],textHintLength:64,excludeIds:!0,maxPathDepth:4,landmarks:["HEADER","NAV","MAIN","ASIDE","FOOTER","SECTION","ARTICLE"],weights:{signature:100,pathSegment:10,textMatch:20,textMismatch:25,uniqueCandidate:50,positionPenalty:1,slotMatch:30},minConfidence:101};function ce(s){let c=5381;for(let a=0;a<s.length;a++)c=(c<<5)+c^s.charCodeAt(a);return Math.abs(c).toString(36)}function ue(s){if(s.classList&&s.classList.length>0)return Array.from(s.classList).sort().join(" ");let c=s.getAttribute?.("class");return c?c.split(/\s+/).filter(Boolean).sort().join(" "):""}function le(s,c){let a=[];for(let d of s.attributes||[]){let h=d.name;h==="id"||h==="class"||c.excludeAttributePrefixes.some(w=>h.startsWith(w))||c.includeAttributes.includes(h)&&a.push(`${h}=${d.value}`)}return a.sort().join("|")}function de(s,c){return(s.textContent||"").replace(/\s+/g," ").trim().slice(0,c.textHintLength)}function fe(s,c){let a=[s.tagName];return c.includeClasses&&a.push(ue(s)),a.push(le(s,c)),ce(a.join("|"))}function he(s){let c=s.tagName,a=1,d=s.previousElementSibling;for(;d;)d.tagName===c&&a++,d=d.previousElementSibling;return a}function pe(s,c){return s.id||s.getAttribute?.("role")?!0:c.landmarks.includes(s.tagName)}function me(s){if(s.id)return`#${s.id}`;let c=s.getAttribute?.("role");return c?`@${c}`:s.tagName}function ge(s,c){let a=[],d=s;for(;d&&d.tagName&&a.length<c.maxPathDepth;){let h=`${d.tagName}:${he(d)}`;if(a.unshift(h),d!==s&&pe(d,c)){a.unshift(me(d));break}d=d.parentElement}return a}function be(s,c){let a=0,d=s.length-1,h=c.length-1;for(;d>=0&&h>=0&&s[d]===c[h];)a++,d--,h--;return a}function q(s,c,a){if(a.has(s))return a.get(s);let d={signature:fe(s,c),path:ge(s,c),textHint:de(s,c)};return a.set(s,d),d}function ee(s,c,a,d){if(d.has(s))return d.get(s);let h=new Map,w=s.querySelectorAll("*"),E=0;for(let S of w){let H=q(S,c,a);H.domIndex=E++,h.has(H.signature)||h.set(H.signature,[]),h.get(H.signature).push(S)}return d.set(s,h),h}function Me(s,c,a){a.delete(s),c.delete(s);let d=s.querySelectorAll("*");for(let h of d)c.delete(h)}function U(s,c,a,d,h){let w=q(s,a,d),E=q(c,a,d),S=a.weights,H={},T=0;if(w.signature!==E.signature)return{score:0,breakdown:{rejected:"signature mismatch"}};T+=S.signature,H.signature=S.signature;let k=be(w.path,E.path)*S.pathSegment;T+=k,H.path=k;let v=!0;if(w.textHint&&E.textHint?w.textHint===E.textHint?(T+=S.textMatch,H.text=S.textMatch):(T-=S.textMismatch,H.text=-S.textMismatch,v=!1):w.textHint!==E.textHint&&(T-=S.textMismatch,H.text=-S.textMismatch,v=!1),h.candidateCount===1&&v&&(T+=S.uniqueCandidate,H.unique=S.uniqueCandidate),typeof w.domIndex=="number"&&typeof E.domIndex=="number"){let I=Math.abs(w.domIndex-E.domIndex),L=Math.min(I*S.positionPenalty,20);T-=L,H.drift=-L}return{score:T,breakdown:H}}function Q(s,c,a,d,h){if(a.excludeIds&&s.id)return null;let w=ee(c,a,d,h),E=q(s,a,d);if(typeof E.domIndex!="number"){let v=0,I=s.previousElementSibling;for(;I;)v++,I=I.previousElementSibling;E.domIndex=v}let S=w.get(E.signature)||[],H=a.excludeIds?S.filter(v=>!v.id):S;if(H.length===0)return null;let T=null,O=0,k=null;for(let v of H){let{score:I,breakdown:L}=U(s,v,a,d,{candidateCount:H.length});I>O&&(O=I,T=v,k=L)}return O<a.minConfidence?null:{element:T,confidence:O,breakdown:k}}function ve(s,c,a,d){let h=[],w=a.weights.signature+a.weights.slotMatch,E={slot:w};function S(k){if(k.children)return k.children;let v=k.childNodes;if(!v)return[];let I=[];for(let L=0;L<v.length;L++)v[L].nodeType===1&&I.push(v[L]);return I}function H(k,v){let I=S(k),L=S(v);if(I.length===L.length)for(let C=0;C<I.length;C++){let u=I[C],M=L[C];if(a.excludeIds&&(u.id||M.id)||u.tagName!==M.tagName)continue;let p=q(u,a,d).signature,A=q(M,a,d).signature;p!==A&&h.push({newEl:u,oldEl:M,score:w,breakdown:E}),H(u,M)}}function T(k,v){for(;;){if(k.tagName===v.tagName)return[k,v];let I=S(k);if(!k.tagName&&I.length===1){k=I[0];continue}let L=S(v);if(L.length===1&&L[0].tagName===k.tagName){v=L[0];continue}return null}}let O=T(s,c);return O&&H(O[0],O[1]),h}function X(s,c,a,d,h){let w=c.querySelectorAll("*"),E=ee(s,a,d,h),S=0;for(let k of w){let v=q(k,a,d);v.domIndex=S++}let H=[];for(let k of w){if(a.excludeIds&&k.id)continue;let v=q(k,a,d),I=E.get(v.signature)||[],L=a.excludeIds?I.filter(C=>!C.id):I;for(let C of L){let{score:u,breakdown:M}=U(k,C,a,d,{candidateCount:L.length});u>=a.minConfidence&&H.push({newEl:k,oldEl:C,score:u,breakdown:M})}}if(a.weights.slotMatch>0){let k=ve(c,s,a,d);for(let v of k)H.push(v)}H.sort((k,v)=>v.score-k.score);let T=new Map,O=new Set;for(let{newEl:k,oldEl:v}of H)T.has(k)||O.has(v)||(T.set(k,v),O.add(v));return T}function Z(s,c,a,d){let h=q(s,a,d),w=q(c,a,d),{score:E,breakdown:S}=U(s,c,a,d,{candidateCount:1});return{matches:E>=a.minConfidence,score:E,breakdown:S,newMeta:{signature:h.signature,path:h.path,textHint:h.textHint},oldMeta:{signature:w.signature,path:w.path,textHint:w.textHint}}}function te(s={}){let c={...J,...s,weights:{...J.weights,...s.weights}},a=new WeakMap,d=new WeakMap;return{findMatch:(h,w)=>Q(h,w,c,a,d),computeMatches:(h,w)=>X(h,w,c,a,d),explain:(h,w)=>Z(h,w,c,a),invalidate:h=>Me(h,a,d),session:()=>{let h=new WeakMap,w=new WeakMap;return{findMatch:(E,S)=>Q(E,S,c,h,w),computeMatches:(E,S)=>X(E,S,c,h,w),explain:(E,S)=>Z(E,S,c,h)}},getConfig:()=>({...c})}}var Ae=te(),W=(function(){"use strict";let s=()=>{};function c(u){if(!(u instanceof Element))return!1;if(u.hasAttribute("save-ignore"))return!0;if(u.tagName==="LINK"||u.tagName==="SCRIPT"){let M=u.getAttribute("src")||u.getAttribute("href")||"";if(M.startsWith("chrome-extension://")||M.startsWith("moz-extension://")||M.startsWith("safari-web-extension://"))return!0}return!1}function a(u,M){if(M!=="smart")return u.outerHTML;let p=u.getAttribute("src"),A=u.getAttribute("type")||"text/javascript";if(p)try{let b=new URL(p,window.location.href);return`ext:${A}:${b.origin}${b.pathname}${b.search}`}catch{return`ext:${A}:${p}`}else{let b=u.textContent.trim(),g=5381;for(let r=0;r<b.length;r++)g=(g<<5)+g^b.charCodeAt(r);return`inline:${A}:${Math.abs(g).toString(36)}`}}let d={morphStyle:"outerHTML",callbacks:{beforeNodeAdded:s,afterNodeAdded:s,beforeNodeMorphed:s,afterNodeMorphed:s,beforeNodeRemoved:s,afterNodeRemoved:s,beforeAttributeUpdated:s},head:{style:"merge",shouldPreserve:u=>u.getAttribute("im-preserve")==="true",shouldReAppend:u=>u.getAttribute("im-re-append")==="true",shouldRemove:s,afterHeadMorphed:s},scripts:{handle:!1,matchMode:"outerHTML",shouldPreserve:u=>u.getAttribute("im-preserve")==="true",shouldReAppend:u=>u.getAttribute("im-re-append")==="true",shouldRemove:s,afterScriptsHandled:s},restoreFocus:!0},h={computeMatches(u,M){let{computeMatches:p}=Ae.session();return p(u,M)}};function w(u,M,p={}){u=L(u);let A=C(M),b=I(u,A,p),g=b.scripts.matchMode,r=new Set(Array.from(u.querySelectorAll("script")).map(n=>a(n,g))),l=S(b,()=>O(b,u,A,n=>n.morphStyle==="innerHTML"?(H(n,u,A),Array.from(u.childNodes)):E(n,u,A)));b.pantry.remove();let e=v(u,r,b);return e.length>0?l instanceof Promise?l.then(n=>Promise.all(e).then(()=>n)):Promise.all(e).then(()=>l):l}function E(u,M,p){let A=C(M);return H(u,A,p,M,M.nextSibling),Array.from(A.childNodes)}function S(u,M){if(!u.config.restoreFocus)return M();let p=document.activeElement;if(!(p instanceof HTMLInputElement||p instanceof HTMLTextAreaElement))return M();let{id:A,selectionStart:b,selectionEnd:g}=p,r=M();return A&&A!==document.activeElement?.getAttribute("id")&&(p=u.target.querySelector(`[id="${A}"]`),p?.focus()),p&&!p.selectionEnd&&g!=null&&p.setSelectionRange(b,g),r}let H=(function(){function u(e,n,i,t=null,o=null){n instanceof HTMLTemplateElement&&i instanceof HTMLTemplateElement&&(n=n.content,i=i.content),t||=n.firstChild;for(let f of i.childNodes){if(c(f))continue;if(t&&t!=o){let y=p(e,f,t,o);if(y){y!==t&&b(e,t,y),T(y,f,e),t=y.nextSibling;continue}}if(f instanceof Element){let y=f.getAttribute("id");if(e.persistentIds.has(y)){let N=g(n,y,t,e);T(N,f,e),t=N.nextSibling;continue}if(!e.idMap.has(f)){let N=e.hyperMatches.get(f);if(N&&!e.idMap.has(N)){l(n,N,t),T(N,f,e),t=N.nextSibling;continue}}}let m=M(n,f,t,e);m&&(t=m.nextSibling)}for(;t&&t!=o;){let f=t;t=t.nextSibling,c(f)||A(e,f)}}function M(e,n,i,t){if(t.callbacks.beforeNodeAdded(n)===!1)return null;if(t.idMap.has(n)){let o=document.createElement(n.tagName);return e.insertBefore(o,i),T(o,n,t),t.callbacks.afterNodeAdded(o),o}else{let o=document.importNode(n,!0);return e.insertBefore(o,i),t.callbacks.afterNodeAdded(o),o}}let p=(function(){function e(t,o,f,m){let y=o instanceof Element&&!t.idMap.has(o)?t.hyperMatches.get(o):null,N=null,$=o.nextSibling,D=0,x=f;for(;x&&x!=m;){if(i(x,o)){if(n(t,x,o)||x===y&&!t.idMap.has(x))return x;if(N===null){let V=x instanceof Element&&t.hyperMatchedOldElements.has(x);!t.idMap.has(x)&&!V&&(N=x)}}if(N===null&&$&&i(x,$)&&(D++,$=$.nextSibling,D>=2&&(N=void 0)),t.activeElementAndParents.includes(x))break;x=x.nextSibling}return N||null}function n(t,o,f){let m=t.idMap.get(o),y=t.idMap.get(f);if(!y||!m)return!1;for(let N of m)if(y.has(N))return!0;return!1}function i(t,o){let f=t,m=o;return f.nodeType===m.nodeType&&f.tagName===m.tagName&&(!f.getAttribute?.("id")||f.getAttribute?.("id")===m.getAttribute?.("id"))}return e})();function A(e,n){let i=n instanceof Element&&e.hyperMatchedOldElements.has(n)&&!e.idMap.has(n);if(e.idMap.has(n)||i)l(e.pantry,n,null);else{if(e.callbacks.beforeNodeRemoved(n)===!1)return;n.parentNode?.removeChild(n),e.callbacks.afterNodeRemoved(n)}}function b(e,n,i){let t=n;for(;t&&t!==i;){let o=t;t=t.nextSibling,c(o)||A(e,o)}return t}function g(e,n,i,t){let o=t.target.getAttribute?.("id")===n&&t.target||t.target.querySelector(`[id="${n}"]`)||t.pantry.querySelector(`[id="${n}"]`);return r(o,t),l(e,o,i),o}function r(e,n){let i=e.getAttribute("id");for(;e=e.parentNode;){let t=n.idMap.get(e);t&&(t.delete(i),t.size||n.idMap.delete(e))}}function l(e,n,i){if(e.moveBefore)try{e.moveBefore(n,i)}catch{e.insertBefore(n,i)}else e.insertBefore(n,i)}return u})(),T=(function(){function u(r,l,e){return e.ignoreActive&&r===document.activeElement?null:(e.callbacks.beforeNodeMorphed(r,l)===!1||(r instanceof HTMLHeadElement&&e.head.ignore||(r instanceof HTMLHeadElement&&e.head.style!=="morph"?k(r,l,e):(M(r,l,e),g(r,e)||H(e,r,l))),e.callbacks.afterNodeMorphed(r,l)),r)}function M(r,l,e){let n=l.nodeType;if(n===1){let i=r,t=l,o=i.attributes,f=t.attributes;for(let m of f)b(m.name,i,"update",e)||i.getAttribute(m.name)!==m.value&&i.setAttribute(m.name,m.value);for(let m=o.length-1;0<=m;m--){let y=o[m];if(y&&!t.hasAttribute(y.name)){if(b(y.name,i,"remove",e))continue;i.removeAttribute(y.name)}}g(i,e)||p(i,t,e)}(n===8||n===3)&&r.nodeValue!==l.nodeValue&&(r.nodeValue=l.nodeValue)}function p(r,l,e){if(r instanceof HTMLInputElement&&l instanceof HTMLInputElement&&l.type!=="file"){let n=l.value,i=r.value;A(r,l,"checked",e),A(r,l,"disabled",e),l.hasAttribute("value")?i!==n&&(b("value",r,"update",e)||(r.setAttribute("value",n),r.value=n)):b("value",r,"remove",e)||(r.value="",r.removeAttribute("value"))}else if(r instanceof HTMLOptionElement&&l instanceof HTMLOptionElement)A(r,l,"selected",e);else if(r instanceof HTMLTextAreaElement&&l instanceof HTMLTextAreaElement){let n=l.value,i=r.value;if(b("value",r,"update",e))return;n!==i&&(r.value=n),r.firstChild&&r.firstChild.nodeValue!==n&&(r.firstChild.nodeValue=n)}}function A(r,l,e,n){let i=l[e],t=r[e];if(i!==t){let o=b(e,r,"update",n);o||(r[e]=l[e]),i?o||r.setAttribute(e,""):b(e,r,"remove",n)||r.removeAttribute(e)}}function b(r,l,e,n){return r==="value"&&n.ignoreActiveValue&&l===document.activeElement?!0:n.callbacks.beforeAttributeUpdated(r,l,e)===!1}function g(r,l){return!!l.ignoreActiveValue&&r===document.activeElement&&r!==document.body}return u})();function O(u,M,p,A){if(u.head.block){let b=M.querySelector("head"),g=p.querySelector("head");if(b&&g){let r=k(b,g,u);return Promise.all(r).then(()=>{let l=Object.assign(u,{head:{block:!1,ignore:!0}});return A(l)})}}return A(u)}function k(u,M,p){let A=[],b=[],g=[],r=[],l=p.scripts.matchMode,e=t=>{if(t.tagName==="SCRIPT")return a(t,l);if(t.tagName==="LINK"&&l==="smart"){let o=t.getAttribute("href");if(o)try{let f=new URL(o,window.location.href);return`link:${t.getAttribute("rel")||""}:${f.origin}${f.pathname}${f.search}`}catch{}}return t.outerHTML},n=new Map;for(let t of M.children)c(t)||n.set(e(t),t);for(let t of u.children){let o=e(t),f=n.has(o),m=p.head.shouldReAppend(t),y=p.head.shouldPreserve(t);f||y?m?b.push(t):(n.delete(o),g.push(t)):p.head.style==="append"?m&&(b.push(t),r.push(t)):p.head.shouldRemove(t)!==!1&&!c(t)&&b.push(t)}r.push(...n.values());let i=[];for(let t of r){let o=document.createRange().createContextualFragment(t.outerHTML).firstChild;if(p.callbacks.beforeNodeAdded(o)!==!1){if("href"in o&&o.href||"src"in o&&o.src){let f,m=new Promise(function(y){f=y});o.addEventListener("load",function(){f()}),i.push(m)}u.appendChild(o),p.callbacks.afterNodeAdded(o),A.push(o)}}for(let t of b)p.callbacks.beforeNodeRemoved(t)!==!1&&(u.removeChild(t),p.callbacks.afterNodeRemoved(t));return p.head.afterHeadMorphed(u,{added:A,kept:g,removed:b}),i}function v(u,M,p){if(!p.scripts.handle)return[];let A=[],b=[],g=[],r=[],l=p.scripts.matchMode,e=Array.from(u.querySelectorAll("script"));for(let i of e){let t=a(i,l),o=M.has(t),f=p.scripts.shouldPreserve(i),m=p.scripts.shouldReAppend(i);o||f?m?(b.push(i),r.push(i)):g.push(i):r.push(i)}for(let i of M){let t=e.some(o=>o.outerHTML===i)}let n=[];for(let i of r){if(p.callbacks.beforeNodeAdded(i)===!1)continue;let t=document.createRange().createContextualFragment(i.outerHTML).firstChild;if(t.src){let o,f=new Promise(function(m){o=m});t.addEventListener("load",function(){o()}),t.addEventListener("error",function(){o()}),n.push(f)}i.replaceWith(t),p.callbacks.afterNodeAdded(t),A.push(t)}return p.scripts.afterScriptsHandled(u,{added:A,kept:g,removed:b}),n}let I=(function(){function u(e,n,i){let{persistentIds:t,idMap:o}=r(e,n),f=h.computeMatches(e,n);if(typeof i.key=="function"){let $=new Map,D=new Set,x=R=>{let B=i.key(R);B!=null&&($.has(B)?D.add(B):$.set(B,R))};e instanceof Element&&x(e);for(let R of e.querySelectorAll("*"))x(R);for(let R of D)$.delete(R);let V=new Map;for(let[R,B]of f)V.set(B,R);let P=n.__hyperMorphRoot||n,F=new Map,G=new Set,Y=R=>{let B=i.key(R);B!=null&&(F.has(B)?G.add(B):F.set(B,R))};P instanceof Element&&Y(P);for(let R of P.querySelectorAll("*"))Y(R);for(let R of G)F.delete(R);for(let[R,B]of F){let j=$.get(R);if(!j||j.tagName!==B.tagName)continue;let K=V.get(j);K&&K!==B&&f.delete(K);let _=f.get(B);_&&_!==j&&V.delete(_),f.set(B,j),V.set(j,B)}}let m=new Set;for(let $ of f.values())m.add($);let y=M(i),N=y.morphStyle||"outerHTML";if(!["innerHTML","outerHTML"].includes(N))throw`Do not understand how to morph style ${N}`;return{target:e,newContent:n,config:y,morphStyle:N,ignoreActive:y.ignoreActive,ignoreActiveValue:y.ignoreActiveValue,restoreFocus:y.restoreFocus,idMap:o,persistentIds:t,hyperMatches:f,hyperMatchedOldElements:m,pantry:p(),activeElementAndParents:A(e),callbacks:y.callbacks,head:y.head,scripts:y.scripts}}function M(e){let n=Object.assign({},d);return Object.assign(n,e),n.callbacks=Object.assign({},d.callbacks,e.callbacks),n.head=Object.assign({},d.head,e.head),n.scripts=Object.assign({},d.scripts,e.scripts),n}function p(){let e=document.createElement("div");return e.hidden=!0,document.body.insertAdjacentElement("afterend",e),e}function A(e){let n=[],i=document.activeElement;if(i?.tagName!=="BODY"&&e.contains(i))for(;i&&(n.push(i),i!==e);)i=i.parentElement;return n}function b(e){let n=Array.from(e.querySelectorAll("[id]"));return e.getAttribute?.("id")&&n.push(e),n}function g(e,n,i,t){for(let o of t){let f=o.getAttribute("id");if(n.has(f)){let m=o;for(;m;){let y=e.get(m);if(y==null&&(y=new Set,e.set(m,y)),y.add(f),m===i)break;m=m.parentElement}}}}function r(e,n){let i=b(e),t=b(n),o=l(i,t),f=new Map;g(f,o,e,i);let m=n.__hyperMorphRoot||n;return g(f,o,m,t),{persistentIds:o,idMap:f}}function l(e,n){let i=new Set,t=new Map;for(let{id:f,tagName:m}of e)t.has(f)?i.add(f):t.set(f,m);let o=new Set;for(let{id:f,tagName:m}of n)o.has(f)?i.add(f):t.get(f)===m&&o.add(f);for(let f of i)o.delete(f);return o}return u})(),{normalizeElement:L,normalizeParent:C}=(function(){let u=new WeakSet;function M(g){return g instanceof Document?g.documentElement:g}function p(g){if(g==null)return document.createElement("div");if(typeof g=="string")return p(b(g));if(u.has(g))return g;if(g instanceof Node){if(g.parentNode)return new A(g);{let r=document.createElement("div");return r.append(g),r}}else{let r=document.createElement("div");for(let l of[...g])r.append(l);return r}}class A{constructor(r){this.originalNode=r,this.realParentNode=r.parentNode,this.previousSibling=r.previousSibling,this.nextSibling=r.nextSibling}get childNodes(){let r=[],l=this.previousSibling?this.previousSibling.nextSibling:this.realParentNode.firstChild;for(;l&&l!=this.nextSibling;)r.push(l),l=l.nextSibling;return r}querySelectorAll(r){return this.childNodes.reduce((l,e)=>{if(e instanceof Element){e.matches(r)&&l.push(e);let n=e.querySelectorAll(r);for(let i=0;i<n.length;i++)l.push(n[i])}return l},[])}insertBefore(r,l){return this.realParentNode.insertBefore(r,l)}moveBefore(r,l){return this.realParentNode.moveBefore(r,l)}get __hyperMorphRoot(){return this.originalNode}}function b(g){let r=new DOMParser,l=g.replace(/<svg(\s[^>]*>|>)([\s\S]*?)<\/svg>/gim,"");if(l.match(/<\/html>/)||l.match(/<\/head>/)||l.match(/<\/body>/)){let e=r.parseFromString(g,"text/html");if(l.match(/<\/html>/))return u.add(e),e;{let n=e.firstChild;return n&&u.add(n),n}}else{let n=r.parseFromString("<body><template>"+g+"</template></body>","text/html").body.querySelector("template").content;return u.add(n),n}}return{normalizeElement:M,normalizeParent:p}})();return{morph:w,defaults:d}})();var ye=W.morph,Se=W.defaults,ke=W;return oe(we);})();
2
2
 
3
3
  // Convenience morph wrapper with data-id support
4
4
  var morph = function(oldEl, newEl, options = {}) {
5
5
  return HyperMorph.morph(oldEl, newEl, {
6
- key: (el) => el.getAttribute('data-id') || el.id,
6
+ key: (el) => (el.getAttribute && el.getAttribute('data-id')) || el.id || null,
7
7
  ...options
8
8
  });
9
9
  };