hyper-undo 0.7.0 → 0.8.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/CHANGELOG.md +23 -0
- package/README.md +68 -2
- package/dist/hyper-undo.min.js +1 -1
- package/package.json +1 -1
- package/src/index.js +12 -1
- package/src/keys.js +55 -17
- package/src/primitives.js +2 -1
- package/src/replay-plan.js +65 -20
- package/src/scope.js +228 -47
- package/src/typing.js +142 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# hyper-undo changelog
|
|
2
2
|
|
|
3
|
+
## [Unreleased]
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `refused: 'skip'` scope option: one `undo()` or `redo()` press removes each entry that preflight or apply refuses (every code), emits a `skipped` event carrying the entry's plain-data description, and goes on to the next entry until one applies or the stack is empty. The receipt gains a `skipped` list, and the default (`null`) keeps a refused entry on top as before.
|
|
7
|
+
- `skipped` event: fired once per entry a `refused: 'skip'` press removes.
|
|
8
|
+
- `trackContent(root, { read, write, prop })`: a field's content is recorded as one guarded value step, its saved HTML before and after, sampled from the changes inside the root instead of the raw text and node steps those changes would otherwise leave. Returns a release function, and `stop()` forgets every tracked root.
|
|
9
|
+
- `durableForm` scope option: a host gives a node a stable saved form, used instead of its live markup when certifying and replaying steps
|
|
10
|
+
- `editableKeys: 'page'` scope option: in editable content, Cmd+Z, Cmd+Shift+Z, Ctrl+Y and the browser's own `beforeinput` `historyUndo`/`historyRedo` go to the page history instead of the browser's per-field undo. Form controls, IME compositions, the `shadowKeydownIn` editors and regions the scope does not record keep their own undo, and the default leaves every key with the browser.
|
|
11
|
+
- `ownKeysIn` option: selectors whose history keys belong to the page even inside a `shadowKeydownIn` editor.
|
|
12
|
+
- `holdReplay` option: a consumer can hold undo and redo, which then return `{ ok: false, code: 'held' }` without refusing the entry, touching a stack or changing the document.
|
|
13
|
+
- `typingBreak: 'sentence'` scope option: an undo press undoes one sentence of typing. A step closes after the space or line break that follows `.`, `?` or `!`, at a field switch, at a switch between typing and deleting, after a paste, a drop, a cut or a format command, when a selection is replaced, and at a caret jump made with the mouse or the navigation keys. An open IME composition never lets the idle timer close a step, on every scope.
|
|
14
|
+
- `endsSentence(textBefore)`: the sentence rule as a predicate, for a host that groups typing the same way.
|
|
15
|
+
- A successful `undo()` or `redo()` receipt gains `caret`, where the applied entry changed text: `{ node, offset }` for a text node or an added or removed one, and `{ root, textOffset }` for a tracked content root. The key is present only when the applied entry changed text; a refused press carries no `caret`. A press that skips entries and then applies one reports that entry's `caret`. The engine never touches the selection.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- Changes inside a subtree removed from a `no-undo` region no longer leave a history entry
|
|
19
|
+
- An entry description shows a node's durable form as text, not as an internal token
|
|
20
|
+
|
|
3
21
|
## [0.6.4] - 2026-10-15
|
|
4
22
|
|
|
5
23
|
### Changed
|
|
@@ -19,6 +37,11 @@
|
|
|
19
37
|
|
|
20
38
|
## [Unreleased]
|
|
21
39
|
|
|
40
|
+
## [0.7.1] - 2026-10-30
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
43
|
+
- Undo and redo keep changes made inside a subtree that an edit removes: a child moved out of a column that is then removed returns to the column on undo and leaves again on redo.
|
|
44
|
+
|
|
22
45
|
## [0.7.0] - 2026-10-30
|
|
23
46
|
|
|
24
47
|
### Added
|
package/README.md
CHANGED
|
@@ -66,12 +66,13 @@ the box.
|
|
|
66
66
|
| `undo.discardCaptured()` | drain and throw away the captured records (failure path companion) |
|
|
67
67
|
| `undo.flush()` | force-close the current idle batch as its own commit |
|
|
68
68
|
| `undo.command(label, fn)` | run a synchronous `fn` and push only its own writes as one labelled entry; returns `{ id, label }`, or `null` when it wrote nothing |
|
|
69
|
+
| `undo.trackContent(root, { read, write, prop })` | record `root`'s content as one guarded value step: `read(root)` is sampled when a mutation lands inside the root and `write(root, html)` replays a step, so the inside of a tracked root is never recorded as raw text or node steps. Returns a release function; `stop()` forgets every tracked root. `prop` defaults to `'content'` |
|
|
69
70
|
| `undo.setRecording(mode)` | `'automatic'` (default) or `'commands'`; see [Recording modes](#recording-modes) |
|
|
70
|
-
| `undo.undo()` / `undo.redo()` | navigate history; each returns a receipt, `{ ok: true, entryId, label, direction }` or `{ ok: false, code, entryId, label, direction }`. `undo({ entryId })` and `redo({ entryId })` replay only while that entry is the next one,
|
|
71
|
+
| `undo.undo()` / `undo.redo()` | navigate history; each returns a receipt, `{ ok: true, entryId, label, direction, skipped, caret }` or `{ ok: false, code, entryId, label, direction }`. `skipped` is the list of skip records from this call and is present only when something was skipped, and a press that only skipped returns `{ ok: false, code: 'empty', direction, skipped }`. `caret` says where the applied entry changed text (see [Where the text changed](#where-the-text-changed)); it is present only when the applied entry changed text. `undo({ entryId })` and `redo({ entryId })` replay only while that entry is the next one, refuse `stale-entry` otherwise, and never skip |
|
|
71
72
|
| `undo.discard(entryId)` | drop the refused top entry, and only that one: `{ ok: true, entryId, direction }`, or `{ ok: false, code: 'not-refused' }` |
|
|
72
73
|
| `undo.clear()` | clear both stacks |
|
|
73
74
|
| `undo.pause()` / `undo.resume()` | recorder skips while paused |
|
|
74
|
-
| `undo.on('undo'\|'redo'\|'commit'\|'refused'\|'discard'\|'clear', fn)` / `undo.off(...)` | subscribe to lifecycle events; `undo`/`redo` fire after a navigate with the receipt, `refused` with `{ entryId, label, direction, code, targets }`, `commit` after a new commit, `discard` with `{ entryId, label, direction }`, `clear` after a reset. (No generic `change` event.) |
|
|
75
|
+
| `undo.on('undo'\|'redo'\|'commit'\|'refused'\|'skipped'\|'discard'\|'clear', fn)` / `undo.off(...)` | subscribe to lifecycle events; `undo`/`redo` fire after a navigate with the receipt, `refused` with `{ entryId, label, direction, code, targets }`, `skipped` with `{ entryId, label, direction, code, targets, description }`, `commit` after a new commit, `discard` with `{ entryId, label, direction }`, `clear` after a reset. (No generic `change` event.) |
|
|
75
76
|
| `undo.canUndo` / `undo.canRedo` | booleans (getters) |
|
|
76
77
|
| `undo.history` | `[{ id, label, timestamp }, ...]`, oldest first; `timestamp` is `Date.now()` millis. The entry that was just refused also carries `refused: { direction, code, targets }` |
|
|
77
78
|
| `undo.future` | the redo stack in the same shape, next redo first |
|
|
@@ -90,9 +91,15 @@ the box.
|
|
|
90
91
|
| `idleLabel` | `'Edit'` | label for auto-closed batches |
|
|
91
92
|
| `bindKeys` | `true` (singleton), `false` (`create`) | install the global Cmd+Z handler |
|
|
92
93
|
| `shadowKeydownIn` | code-editor selectors (see below) | when `event.target.closest(selector)` matches, the global handler bails without `preventDefault` |
|
|
94
|
+
| `editableKeys` | `'native'` | how the history keys and the browser's own history input event are routed inside editable content. `'native'` leaves every `contenteditable` (and the `shadowKeydownIn` list) its own undo. `'page'` sends Cmd+Z, Cmd+Shift+Z, Ctrl+Y and a `beforeinput` `historyUndo`/`historyRedo` to the page history when the target sits inside a tracked content root or matches `ownKeysIn`; a form control, the `shadowKeydownIn` list and an editing host the scope does not record keep their own undo. Only then is `beforeinput` listened to at all |
|
|
95
|
+
| `ownKeysIn` | `null` | selectors whose history keys and history input events belong to the page stack under `editableKeys: 'page'`, even when the element also matches `shadowKeydownIn` (a block editor sharing its element with `.ProseMirror`) |
|
|
96
|
+
| `typingBreak` | `null` | `'sentence'` closes the open typing step after the space or line break that follows `.`, `?` or `!` (the delimiter belongs to the finished sentence), at a switch to another field, at a switch between typing and deleting, after a paste, a drop, a cut or a format command, when a selection is replaced, and at a caret jump made with the mouse or a navigation key. So one undo press undoes one sentence of typing. On every scope an open IME composition holds the idle step open |
|
|
97
|
+
| `holdReplay` | `null` | function `() => boolean`; while it answers true, `undo()` and `redo()` return `{ ok: false, code: 'held' }` without flushing, moving either stack, changing the document or emitting `refused` |
|
|
98
|
+
| `refused` | `null` | `'skip'` lets one `undo()` or `redo()` press get past a step it can no longer replay: the refused entry is removed from its stack (it goes to no other stack), announced with a `skipped` event, and the same call goes on to the next entry until one applies or the stack is empty. Every refusal code is skipped, `replay-error` included, but `undo({ entryId })` never skips. The default keeps a refused entry on top. See [Refusals](#refusals) |
|
|
93
99
|
| `ignoreAttribute` | `null` | predicate `(attrName, element) => boolean`; return true to skip recording that attribute mutation, and the certificate's projection skips it too, so such a change never refuses a later undo or redo. |
|
|
94
100
|
| `isProtected` | `null` | predicate `(node) => boolean`; return true for content that no undo or redo may erase, and an operation that would remove or replace a subtree containing such a node is refused with code `"protected"`. |
|
|
95
101
|
| `ignoreNode` | `null` | predicate `(node) => boolean`; in automatic mode a `childList` record whose every added and removed node it accepts is not recorded, so a gesture's own repeated moves leave no generic entry beside the one the consumer records with `command`, and such a node is left out of a bracketed entry's added and removed lists too. |
|
|
102
|
+
| `durableForm` | `null` | function `(node, host) => string \| undefined`; return a string for a node whose live markup is not its saved markup (a mounted rich text editor), and the certificate uses that string as the node's whole form instead of looking inside it, at record time and at every replay check. Return `undefined` to keep the default form. A throw or a non-string also keeps the default. |
|
|
96
103
|
| `debug` | `false` | console.log internal state transitions |
|
|
97
104
|
|
|
98
105
|
## Batching
|
|
@@ -141,6 +148,39 @@ undo.undo() // { ok: true, entryId, label, direction }
|
|
|
141
148
|
|
|
142
149
|
A refused entry keeps its place on top, and `undo.history` (or `undo.future`) shows `refused: { direction, code, targets }` on it, so nothing is lost and nothing is silently skipped. `undo.discard(entryId)` drops exactly that entry and leaves the document and every other entry as they are.
|
|
143
150
|
|
|
151
|
+
### Skipping what can no longer be replayed
|
|
152
|
+
|
|
153
|
+
By default a refused entry keeps its place on top, so every later press refuses it again until it is dismissed. With `refused: 'skip'` one press removes it instead and goes on:
|
|
154
|
+
|
|
155
|
+
```js
|
|
156
|
+
const undo = hyperundo.create({ refused: 'skip' })
|
|
157
|
+
undo.on('skipped', ({ entryId, label, direction, code, targets, description }) => keepCopy(description))
|
|
158
|
+
|
|
159
|
+
undo.undo()
|
|
160
|
+
// { ok: true, entryId: 'A', label: 'Edit A', direction: 'undo',
|
|
161
|
+
// skipped: [{ entryId: 'C', label: 'Edit C', direction: 'undo', code: 'content-changed',
|
|
162
|
+
// targets: [...], description: { id: 'C', label: 'Edit C', ... } }] }
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
The press pops each refused entry, emits one `skipped` event for it and continues to the next entry in the same call, until one applies or the stack is empty (`{ ok: false, code: 'empty', direction, skipped }`). A skipped entry goes to no other stack and emits no `refused` event. Its `description` is the plain-data `describe(entryId)` of the entry, built before it is removed, so a consumer can keep a recovery copy after the fact. `undo({ entryId })` never skips: a named entry is refused as it is today.
|
|
166
|
+
|
|
167
|
+
### Where the text changed
|
|
168
|
+
|
|
169
|
+
A successful receipt carries `caret`, taken from the last operation of the replay that changed text, so a host can put the caret where the removed or restored text was. The key is present only when the applied entry changed text:
|
|
170
|
+
|
|
171
|
+
| The entry changed | `caret` |
|
|
172
|
+
|---|---|
|
|
173
|
+
| a text node | `{ node, offset }`, at the end of the changed run of its data |
|
|
174
|
+
| a tracked content root (`trackContent`) | `{ root, textOffset }`, at the end of the changed run of `root.textContent` |
|
|
175
|
+
| added or removed a text node | `{ node, offset }`: the parent at the node's index, or the node at its length |
|
|
176
|
+
| a value step with a `write` and no `content` flag, an attribute, a node that is not text | no `caret` key |
|
|
177
|
+
|
|
178
|
+
```js
|
|
179
|
+
undo.undo() // { ok: true, entryId, label, direction, skipped, caret: { node, offset: 5 } }
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
The engine never reads or writes the selection; placing the caret is the host's job. A refused or skipped press reports no `caret`, and when one press skips entries and then applies one, the `caret` is the applied entry's.
|
|
183
|
+
|
|
144
184
|
`targets` is one `{ id, kind }` per node the refused step addresses. `id` is the nearest `[data-id]` ancestor, so a text step with no id of its own names the record it was typed in, and `null` when nothing in the ancestor chain carries one; `kind` says what the target was to the step (`added`, `removed`, `move`, `protected`, or the attribute or property name of a value step). The receipt's own `targets` are on the `refused` event and the history entry, so a consumer can name the record a refusal is about without asking the engine again.
|
|
145
185
|
|
|
146
186
|
## Describing an entry
|
|
@@ -198,6 +238,32 @@ records nothing: the page's Cmd+Z history starts at the write a commit or a
|
|
|
198
238
|
`command` makes of that text. Pass `bindKeys: false` to opt out entirely and
|
|
199
239
|
bind your own handler.
|
|
200
240
|
|
|
241
|
+
### One undo press, one sentence of typing
|
|
242
|
+
|
|
243
|
+
`typingBreak: 'sentence'` groups typing into sentences instead of into half
|
|
244
|
+
second batches. A step closes after the space or line break that follows `.`,
|
|
245
|
+
`?` or `!`, so the space belongs to the finished sentence; the rule is simple on
|
|
246
|
+
purpose, so `e.g. ` ends a step early and `3.5` does not. It also closes at a
|
|
247
|
+
switch to another field, at a switch between typing and deleting, after a paste,
|
|
248
|
+
a drop, a cut or a format command (each is a step of its own), when a selection
|
|
249
|
+
is replaced, and at a caret jump made with the mouse or with the navigation
|
|
250
|
+
keys. An open IME composition never lets the idle timer close a step, with or
|
|
251
|
+
without the option.
|
|
252
|
+
|
|
253
|
+
`endsSentence(textBefore)` is exported for a host that needs the same rule.
|
|
254
|
+
|
|
255
|
+
### Editable content can share the page history
|
|
256
|
+
|
|
257
|
+
`editableKeys: 'page'` gives `contenteditable` fields the page's timeline. A key
|
|
258
|
+
or the browser's own Undo (`beforeinput` `historyUndo`) inside a plain field, a
|
|
259
|
+
tracked rich field, or an element named by `ownKeysIn` calls `undo()` or
|
|
260
|
+
`redo()`; the event is prevented, so the browser's per-field undo never runs and
|
|
261
|
+
the two histories cannot drift. An `input`, a `textarea`, a `select`, an open
|
|
262
|
+
IME composition, the `shadowKeydownIn` list, and an editing host inside a
|
|
263
|
+
region the scope does not record (an authored `no-undo` field) keep their own
|
|
264
|
+
undo. An empty page history still prevents the key: the field's own undo would
|
|
265
|
+
change text the page history does not know about.
|
|
266
|
+
|
|
201
267
|
## Filter attributes
|
|
202
268
|
|
|
203
269
|
A mutation is excluded from recording when its target's ancestor chain contains
|
package/dist/hyper-undo.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var hyperundo=(()=>{var J=Object.defineProperty;var ct=Object.getOwnPropertyDescriptor;var ft=Object.getOwnPropertyNames;var mt=Object.prototype.hasOwnProperty;var pt=(e,n)=>{for(var t in n)J(e,t,{get:n[t],enumerable:!0})},gt=(e,n,t,r)=>{if(n&&typeof n=="object"||typeof n=="function")for(let i of ft(n))!mt.call(e,i)&&i!==t&&J(e,i,{get:()=>n[i],enumerable:!(r=ct(n,i))||r.enumerable});return e};var ht=e=>gt(J({},"__esModule",{value:!0}),e);var Yt={};pt(Yt,{default:()=>Gt,undo:()=>ne});function yt(e,n=null){let t=[];if(e.type==="attributes"){let r=e.target,i=e.attributeName,a=e.oldValue,l=r.getAttribute(i);return a==null&&l!=null?t.push({kind:"attr-add",target:r,name:i,newValue:l}):a!=null&&l==null?t.push({kind:"attr-remove",target:r,name:i,oldValue:a}):a!==l&&t.push({kind:"attr-set",target:r,name:i,oldValue:a,newValue:l}),t}if(e.type==="characterData")return t.push({kind:"text",target:e.target,oldValue:e.oldValue,newValue:e.target.data}),t;if(e.type==="childList"){let r=e.target,i=e.nextSibling,a=e.nextSibling;for(;a&&n?.(a);)a=a.nextSibling;let l=e.previousSibling;for(;l&&n?.(l);)l=l.previousSibling;let s=n?Array.from(e.addedNodes).filter(g=>!n(g)):Array.from(e.addedNodes),d=n?Array.from(e.removedNodes).filter(g=>!n(g)):Array.from(e.removedNodes);return s.length>0&&t.push({kind:"add",parent:r,nodes:s,before:i,afterAuthored:a,beforeAuthored:l,record:e}),d.length>0&&t.push({kind:"remove",parent:r,nodes:d,before:i,afterAuthored:a,beforeAuthored:l,record:e}),t}return t}function ve(e,n=null){let t=[],r=new Map;for(let i of e){if(i.type==="attributes"){let a=i.target,l=i.attributeName,s=r.get(a);s||(s=new Map,r.set(a,s)),s.has(l)||(s.set(l,{idx:t.length,oldValue:i.oldValue}),t.push(null));continue}for(let a of yt(i,n))t.push(a)}for(let[i,a]of r)for(let[l,s]of a){let d=s.oldValue,g=i.getAttribute(l),p=null;d===g?p=null:d==null?p={kind:"attr-add",target:i,name:l,newValue:g}:g==null?p={kind:"attr-remove",target:i,name:l,oldValue:d}:p={kind:"attr-set",target:i,name:l,oldValue:d,newValue:g},t[s.idx]=p}return t.filter(i=>i!=null)}function Ae(e){let n=[],t=new Map;for(let i of e){let a=we(i);if(a==null){n.push(i);continue}let l=t.get(i.target);l||(l=new Map,t.set(i.target,l));let s=l.get(a);if(s==null){let d=ke(i);if(d.oldValue===d.newValue)continue;l.set(a,n.length),n.push({...i,...d})}else n[s]={...n[s],newValue:ke(i).newValue}}let r=[];for(let i of n){if(we(i)==null){r.push(i);continue}i.oldValue!==i.newValue&&r.push(wt(i))}return r}var bt=new Set(["attr-set","attr-add","attr-remove","text","value"]);function we(e){return bt.has(e.kind)?e.kind==="text"?"#text":e.kind==="value"?`#${e.prop||"value"}`:e.name==null?null:`@${e.name}`:null}function ke(e){return e.kind==="attr-add"?{oldValue:null,newValue:e.newValue}:e.kind==="attr-remove"?{oldValue:e.oldValue,newValue:null}:{oldValue:e.oldValue,newValue:e.newValue}}function wt(e){return e.kind!=="attr-add"&&e.kind!=="attr-remove"?e:e.oldValue==null&&e.newValue!=null?{...e,kind:"attr-add"}:e.oldValue!=null&&e.newValue==null?{...e,kind:"attr-remove"}:{...e,kind:"attr-set"}}var Ne=new WeakMap,kt=0;function T(e){let n=Ne.get(e);return n||(n=`n${++kt}`,Ne.set(e,n)),n}function v(e){return!!e&&typeof e=="object"&&typeof e.nodeType=="number"}function vt(e){return e.kind==="add"||e.kind==="remove"}function Se(e,n){let t=[],r=e.childNodes;if(!r)return t;for(let i of r)n&&n(i)||t.push(T(i));return t}function z(e,n,t=null,r=null){if(!v(e))return null;if(e.nodeType===3)return`#text:${JSON.stringify(e.data)}`;if(e.nodeType===8)return`#comment:${JSON.stringify(e.data)}`;if(e.nodeType!==1)return`#node:${e.nodeType}`;let i=[];if(e.attributes){for(let s of e.attributes)At(s.name,e,n,t)||i.push(`${s.name}=${JSON.stringify(s.value)}`);i.sort()}let a=[];if(e.childNodes)for(let s of e.childNodes)Nt(s,r)||a.push(z(s,n,t,r));return`<${String(e.tagName||"").toLowerCase()}${i.length?` ${i.join(" ")}`:""}>${a.join("")}</>`}function G(e,n=[]){if(!v(e))return n;if(e.nodeType===1){let t=e.getAttribute&&e.getAttribute("data-id");t&&n.push(t)}if(e.childNodes)for(let t of e.childNodes)G(t,n);return n}function At(e,n,t,r=null){if(typeof t!="function")return!1;try{return!!t(e,n,r)}catch{return!1}}function Nt(e,n){if(typeof n!="function")return!1;try{return!!n(e)}catch{return!1}}function Te(e,n){if(typeof n!="function")return[];let t=[],r=i=>{if(!v(i))return;let a=!1;try{a=!!n(i)}catch{a=!1}if(a&&t.push(E(i,"protected")),i.childNodes)for(let l of i.childNodes)r(l)};for(let i of e)r(i);return t}function E(e,n,t=null){let r=e;r&&r.nodeType!==1&&(r=r.parentElement);let i=r&&typeof r.closest=="function"?r.closest("[data-id]"):null;return!i&&t&&typeof t.closest=="function"&&(i=t.closest("[data-id]")),{id:(i?i.getAttribute("data-id"):null)||null,kind:n||null}}function Et(e,n,t=null){return(e||[]).filter(v).map(r=>E(r,n,t))}function q(e){return{replayable:!1,reason:e,steps:[]}}function D(e,n){let t=[[n.afterAuthored,i=>i],[n.beforeAuthored,i=>i+1],[n.before,i=>i]],r=!1;for(let[i,a]of t){if(!v(i))continue;r=!0;let l=e.indexOf(T(i));if(l>=0)return a(l)}return r?null:e.length}function Ve(e,n={}){let t=n.isIgnored||null,r=n.ignoreAttribute||null,i=Ae(e),a=[];for(let p of i){if(!vt(p)){if(!v(p.target))return q("non-dom-target");continue}if(!v(p.parent))return q("non-dom-parent");let h=Array.from(p.nodes||[]).filter(v);if(h.length!==0){if(h.length!==Array.from(p.nodes||[]).length)return q("non-dom-node");a.push({primitive:p,nodes:h,parent:p.parent})}}if(St(a))return q("in-record-move");let l=Tt(a,t);if(!l)return q("ambiguous-structure");let s=Vt(a,l),d=new Map(a.map(p=>[p.primitive,p])),g=[];for(let p of i){let h=d.get(p);if(!h){g.push(Pt(p));continue}let w=s.get(h);if(w){w.at===h&&g.push(w.step);continue}g.push(It(h,l.get(h),r,t))}return{replayable:!0,reason:null,steps:g}}function St(e){let n=new Map;for(let t of e){let r=t.primitive.record;if(!r)continue;let i=n.get(r);i||(i=new Map,n.set(r,i));for(let a of t.nodes){let l=i.get(a);l||(l=new Set,i.set(a,l)),l.add(t.primitive.kind)}}for(let t of n.values())for(let r of t.values())if(r.size>1)return!0;return!1}function Tt(e,n){let t=new Map;for(let i of e)t.has(i.parent)||t.set(i.parent,Se(i.parent,n));let r=new Map;for(let i=e.length-1;i>=0;i--){let a=e[i],l=t.get(a.parent);if(!l)return null;let s=l.slice(),d;if(a.primitive.kind==="add"){let g=a.nodes.map(T);if(!g.every(p=>l.includes(p)))return null;d=l.filter(p=>!g.includes(p))}else{let g=D(s,a.primitive);if(g==null)return null;d=s.slice(),d.splice(g,0,...a.nodes.map(T))}r.set(a,{before:d,after:s}),t.set(a.parent,d)}return r}function Vt(e,n){let t=new Map,r=new Map;e.forEach((a,l)=>{for(let s of a.nodes){let d=T(s);a.primitive.kind==="remove"?t.set(d,{item:a,index:l,node:s}):r.set(d,{item:a,index:l,node:s})}});let i=new Map;for(let[a,l]of t){let s=r.get(a);if(!s||s.index<l.index||l.item.nodes.length!==1||s.item.nodes.length!==1)continue;let d={type:"move",node:l.node,sourceParent:l.item.parent,destParent:s.item.parent,source:n.get(l.item),dest:n.get(s.item),sourceSlot:l.item.primitive,destSlot:s.item.primitive,targets:[E(l.node,"move")]};i.set(l.item,{step:d,at:s.item}),i.set(s.item,{step:d,at:s.item})}return i}function It(e,n,t,r){let i=e.primitive.kind;return{type:i==="add"?"add":"restore",parent:e.parent,nodes:e.nodes,seqBefore:n.before,seqAfter:n.after,slot:e.primitive,content:e.nodes.map(a=>z(a,t,e.parent,r)),ids:i==="remove"?e.nodes.flatMap(a=>G(a)):[],targets:Et(e.nodes,i==="add"?"added":"removed",e.parent)}}function Pt(e){return{type:e.kind,primitive:e,targets:[E(e.target,e.name||e.prop||"text")]}}function Ie(e,n,t={}){if(!e||!e.replayable)return{ok:!1,code:"unsupported",targets:[]};let r={isIgnored:t.isIgnored||null,ignoreAttribute:t.ignoreAttribute||null,isProtected:t.isProtected||null,inScope:t.inScope||(()=>!0),liveNodesWithIds:t.liveNodesWithIds||(()=>[]),sim:new Map,planned:new Map,operations:[]};for(let i=0;i<e.steps.length;i++){let a=e.steps[n==="undo"?e.steps.length-1-i:i],l=Rt(a,n,r);if(l)return{ok:!1,code:l.code,targets:l.targets}}return{ok:!0,operations:r.operations}}function M(e,n){return e.sim.has(n)||e.sim.set(n,Se(n,e.isIgnored)),e.sim.get(n)}function Pe(e,n){for(let t=e;t;t=t.parentNode)if(n.planned.has(t))return n.planned.get(t);return n.inScope(e)}function Ce(e,n){return n.liveNodesWithIds(e).filter(t=>Pe(t,n)).length>0}function b(e,n){return{code:e,targets:n||[]}}function Ct(e){return e.kind==="text"?e.target.data:e.kind==="value"?typeof e.read=="function"?e.read(e.target):e.target[e.prop||"value"]:e.target.getAttribute(e.name)}function Rt(e,n,t){let r=n==="undo";return e.type==="move"?Lt(e,r,t):e.type==="add"?Ot(e,r,t):e.type==="restore"?_t(e,r,t):xt(e,r,t)}function xt(e,n,t){let r=e.primitive,i=n?r.newValue:r.oldValue,a=n?r.oldValue:r.newValue;return v(r.target)?Pe(r.target,t)?r.kind==="value"&&typeof r.write=="function"&&typeof r.read!="function"?b("unsupported",e.targets):Ct(r)!==i?b("content-changed",e.targets):(r.kind==="value"?t.operations.push({type:"value",target:r.target,prop:r.prop||"value",value:a,read:r.read||null,write:r.write||null}):r.kind==="text"?t.operations.push({type:"text",target:r.target,data:a}):t.operations.push({type:"attr",target:r.target,name:r.name,value:a??null}),null):b("target-detached",e.targets):b("unsupported",e.targets)}function Ot(e,n,t){if(!v(e.parent)||!t.inScope(e.parent))return b("target-detached",e.targets);if(n){if(!L(M(t,e.parent),e.seqAfter))return b("structure-changed",e.targets);let s=Te(e.nodes,t.isProtected);if(s.length>0)return b("protected",s);for(let d=0;d<e.nodes.length;d++)if(z(e.nodes[d],t.ignoreAttribute,e.parent,t.isIgnored)!==e.content[d])return b("content-changed",e.targets);for(let d of e.nodes)t.operations.push({type:"remove",node:d}),t.planned.set(d,!1);return t.sim.set(e.parent,e.seqBefore.slice()),null}if(!L(M(t,e.parent),e.seqBefore))return b("structure-changed",e.targets);for(let s of e.nodes)if(s.parentNode)return b("structure-changed",e.targets);let r=e.nodes.flatMap(s=>G(s));if(r.length>0&&Ce(r,t))return b("duplicate-id",e.targets);let i=D(e.seqBefore,e.slot);if(i==null)return b("unsupported",e.targets);let a=e.seqBefore.slice(),l=i;for(let s of e.nodes)t.operations.push({type:"insert",parent:e.parent,node:s,beforeId:a[l]??null}),a=a.slice(0,l).concat(T(s),a.slice(l)),l+=1,t.planned.set(s,!0);return t.sim.set(e.parent,e.seqAfter.slice()),null}function _t(e,n,t){if(!v(e.parent)||!t.inScope(e.parent))return b("target-detached",e.targets);if(n){if(!L(M(t,e.parent),e.seqAfter))return b("structure-changed",e.targets);for(let s=0;s<e.nodes.length;s++){let d=e.nodes[s];if(d.parentNode||z(d,t.ignoreAttribute,e.parent,t.isIgnored)!==e.content[s])return b("content-changed",e.targets)}if(e.ids.length>0&&Ce(e.ids,t))return b("duplicate-id",e.targets);let i=D(e.seqAfter,e.slot);if(i==null)return b("unsupported",e.targets);let a=e.seqAfter.slice(),l=i;for(let s of e.nodes)t.operations.push({type:"insert",parent:e.parent,node:s,beforeId:a[l]??null}),a=a.slice(0,l).concat(T(s),a.slice(l)),l+=1,t.planned.set(s,!0);return t.sim.set(e.parent,e.seqBefore.slice()),null}if(!L(M(t,e.parent),e.seqBefore))return b("structure-changed",e.targets);let r=Te(e.nodes,t.isProtected);if(r.length>0)return b("protected",r);for(let i=0;i<e.nodes.length;i++){let a=e.nodes[i];if(a.parentNode!==e.parent||z(a,t.ignoreAttribute,e.parent,t.isIgnored)!==e.content[i])return b("content-changed",e.targets)}for(let i of e.nodes)t.operations.push({type:"remove",node:i}),t.planned.set(i,!1);return t.sim.set(e.parent,e.seqAfter.slice()),null}function Mt(e,n){let t=e.indexOf(n);return t<0?null:e.slice(0,t).concat(e.slice(t+1))}function Lt(e,n,t){let r=T(e.node),i=n?e.destParent:e.sourceParent,a=n?e.sourceParent:e.destParent,l=M(t,i),s=M(t,a),d=n?e.dest.after:e.source.before,g=n?e.source.after:e.dest.before,p=n?e.sourceSlot:e.destSlot;if(!v(i)||!v(a)||!t.inScope(i)||!t.inScope(a))return b("target-detached",e.targets);if(!L(l,d))return b("structure-changed",e.targets);let h=i===a?Mt(s,r):s;if(!L(h,g))return b("structure-changed",e.targets);let w=D(g,p);return w==null?b("unsupported",e.targets):(t.operations.push({type:"insert",parent:a,node:e.node,beforeId:g[w]??null}),t.planned.set(e.node,!0),n?(t.sim.set(a,e.source.before.slice()),i!==a&&t.sim.set(i,e.dest.before.slice())):(t.sim.set(a,e.dest.after.slice()),i!==a&&t.sim.set(i,e.source.after.slice())),null)}function L(e,n){if(!e||!n||e.length!==n.length)return!1;for(let t=0;t<e.length;t++)if(e[t]!==n[t])return!1;return!0}function qt(e,n){for(let t of e.childNodes||[])if(T(t)===n)return t;return null}function Ee(e,n){typeof e.write=="function"?e.write(e.target,n):e.target[e.prop]=n}function Re(e){let n=[];try{for(let t of e)zt(t,n);return{ok:!0}}catch(t){for(let r=n.length-1;r>=0;r--)try{n[r]()}catch{}return{ok:!1,error:t}}}function zt(e,n){if(e.type==="attr"){let t=e.target.getAttribute(e.name);e.value==null?e.target.removeAttribute(e.name):e.target.setAttribute(e.name,e.value),n.push(()=>{t==null?e.target.removeAttribute(e.name):e.target.setAttribute(e.name,t)});return}if(e.type==="text"){let t=e.target.data;e.target.data=e.data,n.push(()=>{e.target.data=t});return}if(e.type==="value"){let t=typeof e.read=="function"?e.read(e.target):e.target[e.prop];Ee(e,e.value),n.push(()=>Ee(e,t));return}if(e.type==="insert"){let t=e.beforeId==null?null:qt(e.parent,e.beforeId);if(e.beforeId!=null&&!t)throw new Error("hyper-undo: replay anchor vanished");let r=e.node.parentNode,i=e.node.nextSibling;e.parent.insertBefore(e.node,t),n.push(()=>{r?r.insertBefore(e.node,i&&i.parentNode===r?i:null):e.node.parentNode===e.parent&&e.parent.removeChild(e.node)});return}if(e.type==="remove"){let t=e.node.parentNode;if(!t)throw new Error("hyper-undo: replay target is no longer attached");let r=e.node.nextSibling;t.removeChild(e.node),n.push(()=>t.insertBefore(e.node,r))}}var Kt=['[src^="chrome-extension://"]','[href^="chrome-extension://"]','[src^="moz-extension://"]','[href^="moz-extension://"]','[src^="safari-web-extension://"]','[href^="safari-web-extension://"]','[id="1p-menu-live-region"]',"com-1password-button","com-1password-menu","com-1password-notification","grammarly-extension","grammarly-desktop-integration","lt-div","lt-card","lt-toolbar","lt-highlighter","[data-lastpass-root]","[data-lastpass-icon-root]","[data-dashlane-shadowhost]","[data-klarna-extension]",'[class*="klarna-extension"]',".skiptranslate","#goog-gt-tt",".goog-te-banner-frame",".goog-te-balloon-frame","style.darkreader","style[data-darkreader-mode]","style[data-darkreader-scheme]","#loom-companion-mv3","loom-container","[ext-id]"],$t=["data-grammarly","data-lt","data-1p","data-com-onepassword","data-lastpass","data-lp","data-kwimpala","data-bitwarden","data-bw","data-dashlane","data-np","data-protonpass","data-proton-pass","data-keeper","data-rf","data-roboform","data-honey","data-rakuten","data-capital-one","data-wikibuy","data-klarna","data-darkreader","data-evernote","data-pocket","data-pin"],xe=Kt.join(","),Oe=new RegExp("^(?:"+$t.join("|")+")(?:-|$)");var rn=Object.freeze({data:Object.freeze({tokens:["no-data"],bundles:["editor-ui"]}),save:Object.freeze({tokens:["no-save"],bundles:["editor-ui"]}),snapshot:Object.freeze({tokens:["no-snapshot"],bundles:["editor-ui"]}),watch:Object.freeze({tokens:["no-watch"],bundles:["editor-ui"]}),undo:Object.freeze({tokens:["no-undo"],bundles:["editor-ui"]}),history:Object.freeze({tokens:[],bundles:["editor-ui"]})}),Bt=Object.freeze({"editor-ui":Object.freeze(["no-data","no-save","no-snapshot","no-watch","no-undo"])}),Dt=Object.freeze(["no-save","no-snapshot","no-trigger-autosave","no-dirty","no-watch","no-undo","no-data","freeze","editor-ui","live"]);function U(e,n){if(!e||e.nodeType!==1)return!1;let t=e.getAttribute?.("clay");return!!(t&&t.split(/\s+/).includes(n)||e.hasAttribute?.(n))}function Y(e,n){if(U(e,n))return!0;for(let[t,r]of Object.entries(Bt))if(r.includes(n)&&U(e,t))return!0;return!1}function _e(e){return e==="clay"||Dt.includes(e)}var Ut=["mutations-ignore","save-remove","save-ignore","save-freeze","no-undo","no-watch"];function jt(){if(typeof window>"u")return null;let e=window.clay?.region||window.hyperclay?.region;return e&&typeof e.resolveRegionPolicy=="function"?e.resolveRegionPolicy:null}function Z(e){let n=jt();if(n)try{if(!n(e).undoable)return!0}catch{}let t=e&&e.nodeType!==1?e.parentElement:e;if(t&&t.closest&&t.closest(xe))return!0;for(;t&&t.nodeType===1;){if(Y(t,"no-undo")||Y(t,"no-watch"))return!0;for(let r of Ut)if(U(t,r))return!0;t=t.parentElement}return!1}function Wt(e){let n=e.target,t=n.cloneNode(!1),r=t;for(;n.parentElement;){n=n.parentElement;let i=n.cloneNode(!1);i.appendChild(t),t=i}return e.oldValue==null?r.removeAttribute(e.attributeName):r.setAttribute(e.attributeName,e.oldValue),Z(r)}function j(e,n,t){if(t?.type==="attributes"&&_e(t.attributeName)){if(Z(e)&&Wt(t))return!0}else if(Z(e))return!0;if(t&&t.type==="attributes"){if(t.attributeName&&Oe.test(t.attributeName.toLowerCase()))return!0;if(n)try{if(n(t.attributeName,t.target))return!0}catch{}}return!1}function Me(){let e=new Map;function n(i,a){let l=e.get(i);return l||(l=new Set,e.set(i,l)),l.add(a),()=>t(i,a)}function t(i,a){let l=e.get(i);l&&l.delete(a)}function r(i,a){let l=e.get(i);if(l)for(let s of Array.from(l))try{s(a)}catch(d){console.error("[hyper-undo] listener threw",d)}}return{on:n,off:t,emit:r}}var Ft={maxHistory:100,idleWindowMs:500,idleLabel:"Edit",recording:"automatic",ignoreAttribute:null,isProtected:null,ignoreNode:null,debug:!1};function Q(e){let n={...Ft,...e};if(!n.scope)throw new Error("hyper-undo: scope is required");let t=Me(),r=[],i=[],a=`u${Math.random().toString(36).slice(2,8)}`,l=0,s=null,d=[],g=null,p=null,h=0,w=!1,P=n.recording==="commands"?"commands":"automatic",A=null,C=!1;function k(...o){n.debug&&console.log("[hyper-undo]",...o)}function $e(){w||(w=!0,p=Be(R),p.observe(n.scope,{childList:!0,attributes:!0,characterData:!0,subtree:!0,attributeOldValue:!0,characterDataOldValue:!0}),k("started"))}function Be(o){let u=typeof document<"u"&&n.scope===document.body,c=typeof window<"u"&&(window.clay?.Mutation||window.hyperclay?.Mutation)||null;return u&&c&&typeof c.createObserver=="function"?(k("sourcing records from the platform Mutation hub (shared observer)"),c.createObserver(o)):new MutationObserver(o)}function re(){w&&(w=!1,p&&(p.disconnect(),p=null),x(),r.length=0,i.length=0,d=[],s=null,A=null,C=!1,h=0,k("stopped"))}function De(){let o=n.scope;return!!(o&&o.nodeType===1&&o.isConnected===!1)}function R(o){oe(o,!1)}function oe(o,u){if(A){for(let m of o)A.records.push(m);return}if(P==="commands"||h>0)return;let c=W(Ue(o),u);if(c.length>0){for(let m of c)d.push(m);ie()}}function Ue(o){return typeof n.ignoreNode!="function"?o:o.filter(u=>{if(u.type!=="childList")return!0;let c=[...u.addedNodes,...u.removedNodes];return c.length===0?!0:!c.every(m=>je(m))})}function je(o){try{return!!n.ignoreNode(o)}catch{return!1}}function W(o,u=!1){let c=o.filter(m=>(ut(m.target)||u&&ye(m.target))&&!j(m.target,n.ignoreAttribute,m));return ve(c,m=>j(m,n.ignoreAttribute))}function ie(){x(),g=setTimeout(ae,n.idleWindowMs)}function x(){g!=null&&(clearTimeout(g),g=null)}function ae(){if(g=null,d.length===0)return;let o=d;d=[],K(n.idleLabel,o)}function K(o,u){let c=Ve(u,{isIgnored:F,ignoreAttribute:ue});if(c.steps.length===0)return k("commit",o,"with nothing to record"),t.emit("commit"),null;let m={id:`${a}-${++l}`,label:o,timestamp:Date.now(),primitives:u,certificate:c};for(r.push(m),i.length=0,s=null;r.length>n.maxHistory;)r.shift();return k("commit",o,"primitives:",u.length),t.emit("commit"),m}function F(o){return j(o,n.ignoreAttribute)}function ue(o,u,c=null){if(typeof n.ignoreAttribute!="function")return!1;try{return!!n.ignoreAttribute(o,u,c)}catch{return!1}}function We(o){if(typeof n.isProtected!="function")return!1;try{return!!n.isProtected(o)}catch{return!1}}function Fe(o,u){if(!w)throw new Error("hyper-undo: scope not started");if(P==="commands")return de(o,u,{honorPause:!0,receipt:!1});R(p.takeRecords()),O();let c=h===0?se():null,m=u();if(m&&typeof m.then=="function")throw new Error("hyper-undo: commit() fn must be synchronous; returned a Promise");if(oe(p.takeRecords(),!0),c!=null&&le(c),d.length===0){t.emit("commit");return}x();let y=d;d=[],K(o,y)}function Xe(o){if(!w)throw new Error("hyper-undo: scope not started");if(!p)return;if(h>1){p.takeRecords();return}O();let u=W(p.takeRecords(),!0);u.length!==0&&(x(),K(o,u))}function He(){p&&p.takeRecords()}function X(){let o=n.scope;return o&&o.ownerDocument||(typeof document<"u"?document:null)}function $(){let o=X();return!o||!o.head||typeof o.head.querySelector!="function"?null:o.head.querySelector("title")}function se(){let o=$();return o?o.textContent:null}function le(o){let u=$();if(!u||o==null)return;let c=u.textContent;c!==o&&fe({kind:"value",target:u,prop:"textContent",oldValue:o,newValue:c})}function Je(o){if(!o)return null;let u=X();if(!u||o.target!==u||o.prop!=="title")return o;let c=$();return c?{kind:"value",target:c,prop:"textContent",oldValue:o.oldValue,newValue:o.newValue}:null}function Ge(){if(typeof window>"u")return!1;let o=window.clay?.effect||window.hyperclay?.effect;return!!(o&&o.running)}function Ye(){p&&R(p.takeRecords()),O(),A={records:[],primitives:[]}}function Ze(){let o=A;if(A=null,!o)return[];let u=W(o.records,!0);for(let c of o.primitives)u.push(c);return u}function de(o,u,{honorPause:c,receipt:m}){if(typeof u!="function")throw new Error("hyper-undo: command() needs a synchronous function");if(A)throw new Error("hyper-undo: command() cannot nest inside another command");if(C)throw new Error("hyper-undo: command() cannot run during undo or redo");if(Ge())throw new Error("hyper-undo: command() cannot run inside a derived-effect context");if(u.constructor&&u.constructor.name==="AsyncFunction")throw new Error("hyper-undo: command() fn must be synchronous; it was declared async");let y=c&&h>0,N=y?null:se();Ye();let S=[],V=null,_;try{try{_=u()}catch(be){V=be}if(p&&R(p.takeRecords()),_&&typeof _.then=="function"){try{_.then(void 0,()=>{})}catch{}V||(V=new Error("hyper-undo: command() fn must be synchronous; returned a Promise"))}y||le(N)}finally{S=Ze()}let B=y||S.length===0?null:K(o,S);if(V)throw V;return m?B?{id:B.id,label:B.label}:null:B}function Qe(o,u){if(!w)throw new Error("hyper-undo: scope not started");return de(o,u,{honorPause:!1,receipt:!0})}function et(o){if(o!=="automatic"&&o!=="commands")throw new Error("hyper-undo: setRecording() takes 'automatic' or 'commands'");if(A)throw new Error("hyper-undo: setRecording() cannot change while a command is running");return P===o||(O(),P=o,k("recording",o)),P}function ce(o){if(!w||!o||!A&&(h>0||P==="commands"))return;let u=Je(o);u&&fe(u)}function fe(o){if(A){A.primitives.push(o);return}d.push(o),ie()}function tt(o,{prop:u="value",oldValue:c,newValue:m,read:y,write:N}={}){!o||c===m||ce({kind:"value",target:o,prop:u,oldValue:c,newValue:m,read:y,write:N})}function O(){h===0&&p&&R(p.takeRecords()),d.length!==0&&(x(),ae())}function me(){h===0&&p&&R(p.takeRecords()),h++,k("paused",h)}function pe(){h!==0&&(h--,h===0&&p&&p.takeRecords(),k("resumed",h))}function ge(o,u){if(De())return re(),{ok:!1,code:"disconnected"};O();let c=o==="undo"?r:i,m=o==="undo"?i:r,y=c.at(-1);if(!y)return{ok:!1,code:"empty"};if(u&&u!==y.id){let _=he(u)||y;return H(_,o,"stale-entry")}let N;C=!0;try{N=Ie(y.certificate,o,{isIgnored:F,ignoreAttribute:ue,isProtected:We,inScope:st,liveNodesWithIds:lt})}finally{C=!1}if(!N.ok)return H(y,o,N.code,N.targets);me();let S;C=!0;try{S=Re(N.operations)}finally{C=!1,pe()}if(!S.ok)return k(`${o} failed and was rolled back`,S.error),H(y,o,"replay-error");c.pop(),m.push(y),s=null;let V={ok:!0,entryId:y.id,label:y.label,direction:o};return k(o,y.label),t.emit(o,V),V}function nt(o){return ge("undo",o&&o.entryId)}function rt(o){return ge("redo",o&&o.entryId)}function he(o){return r.find(u=>u.id===o)||i.find(u=>u.id===o)||null}function H(o,u,c,m=[]){return s={entryId:o.id,direction:u,code:c,targets:m},k("refused",u,c),t.emit("refused",{entryId:o.id,label:o.label,direction:u,code:c,targets:m}),{ok:!1,code:c,entryId:o.id,label:o.label,direction:u}}function ot(o){if(!s||s.entryId!==o)return{ok:!1,code:"not-refused",entryId:o};let u=s.direction,c=u==="undo"?r:i,m=c.at(-1);return!m||m.id!==o?{ok:!1,code:"stale-entry",entryId:o}:(c.pop(),s=null,k("discarded",m.label),t.emit("discard",{entryId:o,label:m.label,direction:u}),{ok:!0,entryId:o,direction:u})}function it(o){let u=he(o);if(!u||!u.certificate||!u.certificate.replayable)return null;let c=s&&s.entryId===u.id?{direction:s.direction,code:s.code,targets:(s.targets||[]).map(m=>({id:m.id??null,kind:m.kind??null}))}:null;return{id:u.id,label:u.label,timestamp:u.timestamp,refused:c,steps:u.certificate.steps.map(at)}}function at(o){if(o.type==="move")return{kind:"move",node:{id:E(o.node).id},from:{id:E(o.sourceParent).id},to:{id:E(o.destParent).id}};if(o.type==="add"||o.type==="restore")return{kind:o.type==="add"?"add":"remove",parent:{id:E(o.parent).id},nodes:o.nodes.map((N,S)=>({id:E(N).id,html:o.content[S]??null}))};let u=o.primitive,c={id:E(u.target).id},m=u.oldValue??null,y=u.newValue??null;return o.type==="value"?{kind:"value",target:c,prop:u.prop??"value",before:m,after:y}:o.type==="text"?{kind:"text",target:c,before:m,after:y}:o.type==="attr-set"||o.type==="attr-add"||o.type==="attr-remove"?{kind:"attribute",target:c,name:u.name??null,before:m,after:y}:{kind:o.type,target:c,before:m,after:y}}function ut(o){let u=n.scope;return!u||!o?!1:o===u||typeof u.contains=="function"&&u.contains(o)}function ye(o){let u=X(),c=u&&u.head;if(!c||!o)return!1;let m=o.nodeType===1?o:o.parentElement;for(;m&&m!==c;m=m.parentElement)if(m.localName==="title")return!1;return m===c}function st(o){let u=n.scope;return!o||!u?!1:o===u||$()===o||ye(o)?!0:typeof u.contains=="function"?u.contains(o):!1}function lt(o){let u=n.scope;if(!o||o.length===0||typeof u?.querySelectorAll!="function")return[];let c=new Set(o),m=[];for(let y of u.querySelectorAll("[data-id]"))c.has(y.getAttribute("data-id"))&&(F(y)||m.push(y));return m}function dt(){p&&p.takeRecords(),r.length=0,i.length=0,d=[],s=null,x(),k("cleared"),t.emit("clear")}return{_config:n,start:$e,stop:re,commit:Fe,commitCaptured:Xe,discardCaptured:He,flush:O,command:Qe,setRecording:et,record:ce,recordValue:tt,undo:nt,redo:rt,clear:dt,discard:ot,describe:it,pause:me,resume:pe,on:t.on,off:t.off,get canUndo(){return r.length>0},get canRedo(){return i.length>0},get isPaused(){return h>0},get history(){return r.map(o=>{let u={id:o.id,label:o.label,timestamp:o.timestamp};return s&&s.entryId===o.id&&(u.refused={direction:s.direction,code:s.code,targets:s.targets}),u})},get future(){return i.map(o=>{let u={id:o.id,label:o.label,timestamp:o.timestamp};return s&&s.entryId===o.id&&(u.refused={direction:s.direction,code:s.code,targets:s.targets}),u}).reverse()},get scope(){return n.scope}}}var Xt=new Set(["checkbox","radio","button","submit","reset","image","file","range","color"]);function Ht(e){let n=typeof e.composedPath=="function"?e.composedPath():[],t=n.length>0?n:Jt(e.target);for(let r of t){if(!r||r.nodeType!==1)continue;let i=String(r.tagName||"").toUpperCase();if(i==="TEXTAREA"||i==="SELECT"||i==="INPUT"&&!Xt.has(String(r.type||r.getAttribute?.("type")||"text").toLowerCase())||r.isContentEditable===!0)return!0;let a=r.getAttribute&&r.getAttribute("contenteditable");if(a!=null&&String(a).toLowerCase()!=="false")return!0}return!1}function Jt(e){let n=[];for(let t=e;t;t=t.parentNode||(t.host??null))n.push(t);return n}function Le(e,n){let t=r=>{let i=e.scope;if(i&&i.nodeType===1&&i.isConnected===!1){n&&n();return}if(r.defaultPrevented||r.isComposing||r.keyCode===229||Ht(r))return;let a=r.target,l=e._config.shadowKeydownIn;if(a&&a.closest&&Array.isArray(l))for(let g of l)try{if(a.closest(g))return}catch{}if(!(r.metaKey||r.ctrlKey))return;let d=(r.key||"").toLowerCase();if(d==="z"&&!r.shiftKey){r.preventDefault(),r.stopPropagation(),e.undo();return}if(d==="z"&&r.shiftKey){r.preventDefault(),r.stopPropagation(),e.redo();return}if(d==="y"&&!r.shiftKey){r.preventDefault(),r.stopPropagation(),e.redo();return}};return window.addEventListener("keydown",t,!0),()=>window.removeEventListener("keydown",t,!0)}var ee=[".CodeMirror",".cm-editor",".monaco-editor",".ace_editor",".ql-editor",".tiptap",".ProseMirror",".overtype-input","[data-richclay-active]"],f=null,te=null,qe=null,I=null;function ze(e,n){if(I){if(I.inst===e)return I.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 t=Le(e,n);return I={inst:e,cleanup:t},t}function Ke(e){I&&I.inst===e&&(I.cleanup(),I=null)}var ne={defaults:{shadowKeydownIn:ee},start(e={}){let n=e.scope||(typeof document<"u"?document.body:null);if(!n)throw new Error("hyper-undo: no scope (need document.body or explicit { scope })");if(f){if(te===n)return console.warn("[hyper-undo] start() called again on existing singleton; ignoring new options"),f;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 t={...e,scope:n,shadowKeydownIn:e.shadowKeydownIn||ee};return f=Q(t),f.start(),te=n,e.bindKeys!==!1&&(qe=ze(f,()=>ne.stop())),f},stop(){f&&(Ke(f),qe=null,f.stop(),f=null,te=null)},create(e={}){let n=e.scope||(typeof document<"u"?document.body:null);if(!n)throw new Error("hyper-undo: no scope (need document.body or explicit { scope })");let t={...e,scope:n,shadowKeydownIn:e.shadowKeydownIn||ee},r=Q(t),i=null,a={_config:r._config,commit:r.commit,command:r.command,setRecording:r.setRecording,commitCaptured:r.commitCaptured,discardCaptured:r.discardCaptured,flush:r.flush,record:r.record,recordValue:r.recordValue,undo:r.undo,redo:r.redo,clear:r.clear,discard:r.discard,describe:r.describe,pause:r.pause,resume:r.resume,on:r.on,off:r.off,get canUndo(){return r.canUndo},get canRedo(){return r.canRedo},get isPaused(){return r.isPaused},get history(){return r.history},get future(){return r.future},get scope(){return r.scope},start(){e.bindKeys&&(i=ze(r,()=>a.stop())),r.start()},stop(){Ke(r),i=null,r.stop()}};return a},commit(e,n){return f?f.commit(e,n):void 0},command(e,n){return f?f.command(e,n):void 0},setRecording(e){return f?f.setRecording(e):void 0},commitCaptured(e){return f?f.commitCaptured(e):void 0},discardCaptured(){return f?f.discardCaptured():void 0},record(e){return f?f.record(e):void 0},recordValue(e,n){return f?f.recordValue(e,n):void 0},undo(e){return f?f.undo(e):void 0},redo(e){return f?f.redo(e):void 0},clear(){return f?f.clear():void 0},discard(e){return f?f.discard(e):void 0},describe(e){return f?f.describe(e):void 0},pause(){return f?f.pause():void 0},resume(){return f?f.resume():void 0},flush(){return f?f.flush():void 0},on(e,n){return f?f.on(e,n):void 0},off(e,n){return f?f.off(e,n):void 0},get canUndo(){return f?f.canUndo:!1},get canRedo(){return f?f.canRedo:!1},get history(){return f?f.history:[]},get future(){return f?f.future:[]},get isPaused(){return f?f.isPaused:!1}};var Gt=ne;return ht(Yt);})();
|
|
1
|
+
var hyperundo=(()=>{var he=Object.defineProperty;var Kt=Object.getOwnPropertyDescriptor;var Bt=Object.getOwnPropertyNames;var Ft=Object.prototype.hasOwnProperty;var qt=(e,n)=>{for(var t in n)he(e,t,{get:n[t],enumerable:!0})},zt=(e,n,t,o)=>{if(n&&typeof n=="object"||typeof n=="function")for(let a of Bt(n))!Ft.call(e,a)&&a!==t&&he(e,a,{get:()=>n[a],enumerable:!(o=Kt(n,a))||o.enumerable});return e};var $t=e=>zt(he({},"__esModule",{value:!0}),e);var Rn={};qt(Rn,{default:()=>Pn,endsSentence:()=>ke,undo:()=>Ne});function Ut(e,n=null){let t=[];if(e.type==="attributes"){let o=e.target,a=e.attributeName,i=e.oldValue,s=o.getAttribute(a);return i==null&&s!=null?t.push({kind:"attr-add",target:o,name:a,newValue:s}):i!=null&&s==null?t.push({kind:"attr-remove",target:o,name:a,oldValue:i}):i!==s&&t.push({kind:"attr-set",target:o,name:a,oldValue:i,newValue:s}),t}if(e.type==="characterData")return t.push({kind:"text",target:e.target,oldValue:e.oldValue,newValue:e.target.data}),t;if(e.type==="childList"){let o=e.target,a=e.nextSibling,i=e.nextSibling;for(;i&&n?.(i);)i=i.nextSibling;let s=e.previousSibling;for(;s&&n?.(s);)s=s.previousSibling;let d=n?Array.from(e.addedNodes).filter(y=>!n(y)):Array.from(e.addedNodes),f=n?Array.from(e.removedNodes).filter(y=>!n(y)):Array.from(e.removedNodes);return d.length>0&&t.push({kind:"add",parent:o,nodes:d,before:a,afterAuthored:i,beforeAuthored:s,record:e}),f.length>0&&t.push({kind:"remove",parent:o,nodes:f,before:a,afterAuthored:i,beforeAuthored:s,record:e}),t}return t}function We(e,n=null){let t=[],o=new Map;for(let a of e){if(a.type==="attributes"){let i=a.target,s=a.attributeName,d=o.get(i);d||(d=new Map,o.set(i,d)),d.has(s)||(d.set(s,{idx:t.length,oldValue:a.oldValue}),t.push(null));continue}for(let i of Ut(a,n))t.push(i)}for(let[a,i]of o)for(let[s,d]of i){let f=d.oldValue,y=a.getAttribute(s),p=null;f===y?p=null:f==null?p={kind:"attr-add",target:a,name:s,newValue:y}:y==null?p={kind:"attr-remove",target:a,name:s,oldValue:f}:p={kind:"attr-set",target:a,name:s,oldValue:f,newValue:y},t[d.idx]=p}return t.filter(a=>a!=null)}function je(e,{sameContent:n=(t,o)=>t===o}={}){let t=[],o=new Map;for(let i of e){let s=$e(i);if(s==null){t.push(i);continue}let d=o.get(i.target);d||(d=new Map,o.set(i.target,d));let f=d.get(s);if(f==null){let y=Ue(i);if(y.oldValue===y.newValue)continue;d.set(s,t.length),t.push({...i,...y})}else t[f]={...t[f],newValue:Ue(i).newValue}}let a=[];for(let i of t){if($e(i)==null){a.push(i);continue}i.oldValue!==i.newValue&&(i.kind==="value"&&i.content===!0&&n(i.oldValue,i.newValue)||a.push(jt(i)))}return a}var Wt=new Set(["attr-set","attr-add","attr-remove","text","value"]);function $e(e){return Wt.has(e.kind)?e.kind==="text"?"#text":e.kind==="value"?`#${e.prop||"value"}`:e.name==null?null:`@${e.name}`:null}function Ue(e){return e.kind==="attr-add"?{oldValue:null,newValue:e.newValue}:e.kind==="attr-remove"?{oldValue:e.oldValue,newValue:null}:{oldValue:e.oldValue,newValue:e.newValue}}function jt(e){return e.kind!=="attr-add"&&e.kind!=="attr-remove"?e:e.oldValue==null&&e.newValue!=null?{...e,kind:"attr-add"}:e.oldValue!=null&&e.newValue==null?{...e,kind:"attr-remove"}:{...e,kind:"attr-set"}}var Xe=new WeakMap,Xt=0;function _(e){let n=Xe.get(e);return n||(n=`n${++Xt}`,Xe.set(e,n)),n}function N(e){return!!e&&typeof e=="object"&&typeof e.nodeType=="number"}function Ht(e){return e.kind==="add"||e.kind==="remove"}function Ge(e,n){let t=[],o=e.childNodes;if(!o)return t;for(let a of o)n&&n(a)||t.push(_(a));return t}function He(e,n,t){if(typeof n!="function"||!N(e)||e.nodeType!==1)return null;let o=null;try{o=n(e,t)}catch{o=null}return typeof o=="string"?`#form:${JSON.stringify(o)}`:null}function Q(e,n,t=null,o=null,a=null,i=null){if(!N(e))return null;let s=He(e,a,t);if(s!=null)return s;if(e.nodeType===3)return`#text:${JSON.stringify(i&&i.has(e)?i.get(e):e.data)}`;if(e.nodeType===8)return`#comment:${JSON.stringify(e.data)}`;if(e.nodeType!==1)return`#node:${e.nodeType}`;let d=[];if(e.attributes){for(let p of e.attributes)Jt(p.name,e,n,t)||d.push(`${p.name}=${JSON.stringify(p.value)}`);d.sort()}let f=[];if(e.childNodes)for(let p of e.childNodes){let w=He(p,a,t);if(w!=null){f.push(w);continue}Yt(p,o)||f.push(Q(p,n,t,o,a,i))}return`<${String(e.tagName||"").toLowerCase()}${d.length?` ${d.join(" ")}`:""}>${f.join("")}</>`}function ye(e,n=[]){if(!N(e))return n;if(e.nodeType===1){let t=e.getAttribute&&e.getAttribute("data-id");t&&n.push(t)}if(e.childNodes)for(let t of e.childNodes)ye(t,n);return n}function Jt(e,n,t,o=null){if(typeof t!="function")return!1;try{return!!t(e,n,o)}catch{return!1}}function Yt(e,n){if(typeof n!="function")return!1;try{return!!n(e)}catch{return!1}}function Ze(e,n){if(typeof n!="function")return[];let t=[],o=a=>{if(!N(a))return;let i=!1;try{i=!!n(a)}catch{i=!1}if(i&&t.push(I(a,"protected")),a.childNodes)for(let s of a.childNodes)o(s)};for(let a of e)o(a);return t}function I(e,n,t=null){let o=e;o&&o.nodeType!==1&&(o=o.parentElement);let a=o&&typeof o.closest=="function"?o.closest("[data-id]"):null;return!a&&t&&typeof t.closest=="function"&&(a=t.closest("[data-id]")),{id:(a?a.getAttribute("data-id"):null)||null,kind:n||null}}function Gt(e,n,t=null){return(e||[]).filter(N).map(o=>I(o,n,t))}function Z(e){return{replayable:!1,reason:e,steps:[]}}function ie(e,n){let t=[[n.afterAuthored,a=>a],[n.beforeAuthored,a=>a+1],[n.before,a=>a]],o=!1;for(let[a,i]of t){if(!N(a))continue;o=!0;let s=e.indexOf(_(a));if(s>=0)return i(s)}return o?null:e.length}function Qe(e,n={}){let t=n.isIgnored||null,o=n.ignoreAttribute||null,a=n.durableForm||null,i=je(e,{sameContent:ae}),s=[];for(let w of i){if(!Ht(w)){if(!N(w.target))return Z("non-dom-target");continue}if(!N(w.parent))return Z("non-dom-parent");let k=Array.from(w.nodes||[]).filter(N);if(k.length!==0){if(k.length!==Array.from(w.nodes||[]).length)return Z("non-dom-node");s.push({primitive:w,nodes:k,parent:w.parent})}}if(Zt(s))return Z("in-record-move");let d=Qt(s,t);if(!d)return Z("ambiguous-structure");let f=en(s,d),y=new Map(s.map(w=>[w.primitive,w])),p=[];for(let w of i){let k=y.get(w);if(!k){p.push(nn(w));continue}let A=f.get(k);if(A){A.at===k&&p.push(A.step);continue}p.push(tn(k,d.get(k),o,t,a))}return{replayable:!0,reason:null,steps:p}}function Zt(e){let n=new Map;for(let t of e){let o=t.primitive.record;if(!o)continue;let a=n.get(o);a||(a=new Map,n.set(o,a));for(let i of t.nodes){let s=a.get(i);s||(s=new Set,a.set(i,s)),s.add(t.primitive.kind)}}for(let t of n.values())for(let o of t.values())if(o.size>1)return!0;return!1}function Qt(e,n){let t=new Map;for(let a of e)t.has(a.parent)||t.set(a.parent,Ge(a.parent,n));let o=new Map;for(let a=e.length-1;a>=0;a--){let i=e[a],s=t.get(i.parent);if(!s)return null;let d=s.slice(),f;if(i.primitive.kind==="add"){let y=i.nodes.map(_);if(!y.every(p=>s.includes(p)))return null;f=s.filter(p=>!y.includes(p))}else{let y=ie(d,i.primitive);if(y==null)return null;f=d.slice(),f.splice(y,0,...i.nodes.map(_))}o.set(i,{before:f,after:d}),t.set(i.parent,f)}return o}function en(e,n){let t=new Map,o=new Map;e.forEach((i,s)=>{for(let d of i.nodes){let f=_(d);i.primitive.kind==="remove"?t.set(f,{item:i,index:s,node:d}):o.set(f,{item:i,index:s,node:d})}});let a=new Map;for(let[i,s]of t){let d=o.get(i);if(!d||d.index<s.index||s.item.nodes.length!==1||d.item.nodes.length!==1)continue;let f={type:"move",node:s.node,sourceParent:s.item.parent,destParent:d.item.parent,source:n.get(s.item),dest:n.get(d.item),sourceSlot:s.item.primitive,destSlot:d.item.primitive,targets:[I(s.node,"move")]};a.set(s.item,{step:f,at:d.item}),a.set(d.item,{step:f,at:d.item})}return a}function tn(e,n,t,o,a=null){let i=e.primitive.kind;return{type:i==="add"?"add":"restore",parent:e.parent,nodes:e.nodes,seqBefore:n.before,seqAfter:n.after,slot:e.primitive,content:e.nodes.map(s=>Q(s,t,e.parent,o,a)),ids:i==="remove"?e.nodes.flatMap(s=>ye(s)):[],targets:Gt(e.nodes,i==="add"?"added":"removed",e.parent)}}function nn(e){return{type:e.kind,primitive:e,targets:[I(e.target,e.name||e.prop||"text")]}}function et(e,n,t={}){if(!e||!e.replayable)return{ok:!1,code:"unsupported",targets:[]};let o={isIgnored:t.isIgnored||null,ignoreAttribute:t.ignoreAttribute||null,durableForm:t.durableForm||null,isProtected:t.isProtected||null,inScope:t.inScope||(()=>!0),liveNodesWithIds:t.liveNodesWithIds||(()=>[]),sim:new Map,planned:new Map,texts:new Map,operations:[]};for(let a=0;a<e.steps.length;a++){let i=e.steps[n==="undo"?e.steps.length-1-a:a],s=on(i,n,o);if(s)return{ok:!1,code:s.code,targets:s.targets}}return{ok:!0,operations:o.operations}}function X(e,n){return e.sim.has(n)||e.sim.set(n,Ge(n,e.isIgnored)),e.sim.get(n)}function H(e,n){for(let t=e;t;t=t.parentNode)if(n.planned.has(t))return n.planned.get(t);return n.inScope(e)}function tt(e,n){return n.liveNodesWithIds(e).filter(t=>H(t,n)).length>0}function b(e,n){return{code:e,targets:n||[]}}function ae(e,n){return e===n?!0:typeof e!="string"||typeof n!="string"?!1:e.replace(/\u00A0| /g," ")===n.replace(/\u00A0| /g," ")}function rn(e){return e.kind==="text"?e.target.data:e.kind==="value"?typeof e.read=="function"?e.read(e.target):e.target[e.prop||"value"]:e.target.getAttribute(e.name)}function on(e,n,t){let o=n==="undo";return e.type==="move"?dn(e,o,t):e.type==="add"?un(e,o,t):e.type==="restore"?sn(e,o,t):an(e,o,t)}function an(e,n,t){let o=e.primitive,a=n?o.newValue:o.oldValue,i=n?o.oldValue:o.newValue;if(!N(o.target))return b("unsupported",e.targets);if(!H(o.target,t))return b("target-detached",e.targets);if(o.kind==="value"&&typeof o.write=="function"&&typeof o.read!="function")return b("unsupported",e.targets);let s=rn(o);return(o.content===!0?!ae(s,a):s!==a)?b("content-changed",e.targets):(o.kind==="value"?t.operations.push({type:"value",target:o.target,prop:o.prop||"value",value:i,read:o.read||null,write:o.write||null,content:o.content===!0}):o.kind==="text"?(t.operations.push({type:"text",target:o.target,data:i}),t.texts.set(o.target,i)):t.operations.push({type:"attr",target:o.target,name:o.name,value:i??null}),null)}function un(e,n,t){if(!N(e.parent)||!H(e.parent,t))return b("target-detached",e.targets);if(n){if(!J(X(t,e.parent),e.seqAfter))return b("structure-changed",e.targets);let d=Ze(e.nodes,t.isProtected);if(d.length>0)return b("protected",d);for(let f=0;f<e.nodes.length;f++)if(Q(e.nodes[f],t.ignoreAttribute,e.parent,t.isIgnored,t.durableForm)!==e.content[f])return b("content-changed",e.targets);for(let f of e.nodes)t.operations.push({type:"remove",node:f}),t.planned.set(f,!1);return t.sim.set(e.parent,e.seqBefore.slice()),null}if(!J(X(t,e.parent),e.seqBefore))return b("structure-changed",e.targets);for(let d of e.nodes)if(d.parentNode)return b("structure-changed",e.targets);let o=e.nodes.flatMap(d=>ye(d));if(o.length>0&&tt(o,t))return b("duplicate-id",e.targets);let a=ie(e.seqBefore,e.slot);if(a==null)return b("unsupported",e.targets);let i=e.seqBefore.slice(),s=a;for(let d of e.nodes)t.operations.push({type:"insert",parent:e.parent,node:d,beforeId:i[s]??null}),i=i.slice(0,s).concat(_(d),i.slice(s)),s+=1,t.planned.set(d,!0);return t.sim.set(e.parent,e.seqAfter.slice()),null}function sn(e,n,t){if(!N(e.parent)||!H(e.parent,t))return b("target-detached",e.targets);if(n){if(!J(X(t,e.parent),e.seqAfter))return b("structure-changed",e.targets);for(let f=0;f<e.nodes.length;f++){let y=e.nodes[f];if(y.parentNode||Q(y,t.ignoreAttribute,e.parent,t.isIgnored,t.durableForm)!==e.content[f])return b("content-changed",e.targets)}if(e.ids.length>0&&tt(e.ids,t))return b("duplicate-id",e.targets);let i=ie(e.seqAfter,e.slot);if(i==null)return b("unsupported",e.targets);let s=e.seqAfter.slice(),d=i;for(let f of e.nodes)t.operations.push({type:"insert",parent:e.parent,node:f,beforeId:s[d]??null}),s=s.slice(0,d).concat(_(f),s.slice(d)),d+=1,t.planned.set(f,!0);return t.sim.set(e.parent,e.seqBefore.slice()),null}if(!J(X(t,e.parent),e.seqBefore))return b("structure-changed",e.targets);let o=Ze(e.nodes,t.isProtected);if(o.length>0)return b("protected",o);let a=i=>t.planned.get(i)===!0||typeof t.isIgnored=="function"&&t.isIgnored(i);for(let i=0;i<e.nodes.length;i++){let s=e.nodes[i];if(s.parentNode!==e.parent||Q(s,t.ignoreAttribute,e.parent,a,t.durableForm,t.texts)!==e.content[i])return b("content-changed",e.targets)}for(let i of e.nodes)t.operations.push({type:"remove",node:i}),t.planned.set(i,!1);return t.sim.set(e.parent,e.seqAfter.slice()),null}function ln(e,n){let t=e.indexOf(n);return t<0?null:e.slice(0,t).concat(e.slice(t+1))}function dn(e,n,t){let o=_(e.node),a=n?e.destParent:e.sourceParent,i=n?e.sourceParent:e.destParent,s=X(t,a),d=X(t,i),f=n?e.dest.after:e.source.before,y=n?e.source.after:e.dest.before,p=n?e.sourceSlot:e.destSlot;if(!N(a)||!N(i)||!H(a,t)||!H(i,t))return b("target-detached",e.targets);if(!J(s,f))return b("structure-changed",e.targets);let w=a===i?ln(d,o):d;if(!J(w,y))return b("structure-changed",e.targets);let k=ie(y,p);return k==null?b("unsupported",e.targets):(t.operations.push({type:"insert",parent:i,node:e.node,beforeId:y[k]??null}),t.planned.set(e.node,!0),n?(t.sim.set(i,e.source.before.slice()),a!==i&&t.sim.set(a,e.dest.before.slice())):(t.sim.set(i,e.dest.after.slice()),a!==i&&t.sim.set(a,e.source.after.slice())),null)}function J(e,n){if(!e||!n||e.length!==n.length)return!1;for(let t=0;t<e.length;t++)if(e[t]!==n[t])return!1;return!0}function cn(e,n){for(let t of e.childNodes||[])if(_(t)===n)return t;return null}function Je(e,n){let t=Math.min(e.length,n.length),o=0;for(;o<t&&e[o]===n[o];)o++;let a=0;for(;a<t-o&&e[e.length-1-a]===n[n.length-1-a];)a++;return n.length-a}function Ye(e,n){typeof e.write=="function"?e.write(e.target,n):e.target[e.prop]=n}function nt(e){let n=[];try{let t=null;for(let o of e)t=fn(o,n)||t;return{ok:!0,caret:t}}catch(t){for(let o=n.length-1;o>=0;o--)try{n[o]()}catch{}return{ok:!1,error:t}}}function fn(e,n){if(e.type==="attr"){let t=e.target.getAttribute(e.name);e.value==null?e.target.removeAttribute(e.name):e.target.setAttribute(e.name,e.value),n.push(()=>{t==null?e.target.removeAttribute(e.name):e.target.setAttribute(e.name,t)});return}if(e.type==="text"){let t=e.target.data;return e.target.data=e.data,n.push(()=>{e.target.data=t}),{node:e.target,offset:Je(t,e.data)}}if(e.type==="value"){let t=typeof e.read=="function"?e.read(e.target):e.target[e.prop],o=e.content?e.target.textContent:null;if(Ye(e,e.value),n.push(()=>Ye(e,t)),!e.content)return;let a=e.target.textContent;return a===o?void 0:{root:e.target,textOffset:Je(o,a)}}if(e.type==="insert"){let t=e.beforeId==null?null:cn(e.parent,e.beforeId);if(e.beforeId!=null&&!t)throw new Error("hyper-undo: replay anchor vanished");let o=e.node.parentNode,a=e.node.nextSibling;return e.parent.insertBefore(e.node,t),n.push(()=>{o?o.insertBefore(e.node,a&&a.parentNode===o?a:null):e.node.parentNode===e.parent&&e.parent.removeChild(e.node)}),e.node.nodeType!==3?void 0:{node:e.node,offset:e.node.data.length}}if(e.type==="remove"){let t=e.node.parentNode;if(!t)throw new Error("hyper-undo: replay target is no longer attached");let o=e.node.nextSibling,a=Array.prototype.indexOf.call(t.childNodes,e.node);return t.removeChild(e.node),n.push(()=>t.insertBefore(e.node,o)),e.node.nodeType!==3?void 0:{node:t,offset:a}}}var pn=['[src^="chrome-extension://"]','[href^="chrome-extension://"]','[src^="moz-extension://"]','[href^="moz-extension://"]','[src^="safari-web-extension://"]','[href^="safari-web-extension://"]','[id="1p-menu-live-region"]',"com-1password-button","com-1password-menu","com-1password-notification","grammarly-extension","grammarly-desktop-integration","lt-div","lt-card","lt-toolbar","lt-highlighter","[data-lastpass-root]","[data-lastpass-icon-root]","[data-dashlane-shadowhost]","[data-klarna-extension]",'[class*="klarna-extension"]',".skiptranslate","#goog-gt-tt",".goog-te-banner-frame",".goog-te-balloon-frame","style.darkreader","style[data-darkreader-mode]","style[data-darkreader-scheme]","#loom-companion-mv3","loom-container","[ext-id]"],mn=["data-grammarly","data-lt","data-1p","data-com-onepassword","data-lastpass","data-lp","data-kwimpala","data-bitwarden","data-bw","data-dashlane","data-np","data-protonpass","data-proton-pass","data-keeper","data-rf","data-roboform","data-honey","data-rakuten","data-capital-one","data-wikibuy","data-klarna","data-darkreader","data-evernote","data-pocket","data-pin"],rt=pn.join(","),ot=new RegExp("^(?:"+mn.join("|")+")(?:-|$)");var Dn=Object.freeze({data:Object.freeze({tokens:["no-data"],bundles:["editor-ui"]}),save:Object.freeze({tokens:["no-save"],bundles:["editor-ui"]}),snapshot:Object.freeze({tokens:["no-snapshot"],bundles:["editor-ui"]}),watch:Object.freeze({tokens:["no-watch"],bundles:["editor-ui"]}),undo:Object.freeze({tokens:["no-undo"],bundles:["editor-ui"]}),history:Object.freeze({tokens:[],bundles:["editor-ui"]})}),gn=Object.freeze({"editor-ui":Object.freeze(["no-data","no-save","no-snapshot","no-watch","no-undo"])}),hn=Object.freeze(["no-save","no-snapshot","no-trigger-autosave","no-dirty","no-watch","no-undo","no-data","freeze","editor-ui","live"]);function ue(e,n){if(!e||e.nodeType!==1)return!1;let t=e.getAttribute?.("clay");return!!(t&&t.split(/\s+/).includes(n)||e.hasAttribute?.(n))}function we(e,n){if(ue(e,n))return!0;for(let[t,o]of Object.entries(gn))if(o.includes(n)&&ue(e,t))return!0;return!1}function it(e){return e==="clay"||hn.includes(e)}var yn=["mutations-ignore","save-remove","save-ignore","save-freeze","no-undo","no-watch"];function wn(){if(typeof window>"u")return null;let e=window.clay?.region||window.hyperclay?.region;return e&&typeof e.resolveRegionPolicy=="function"?e.resolveRegionPolicy:null}function be(e){let n=wn();if(n)try{if(!n(e).undoable)return!0}catch{}let t=e&&e.nodeType!==1?e.parentElement:e;if(t&&t.closest&&t.closest(rt))return!0;for(;t&&t.nodeType===1;){if(we(t,"no-undo")||we(t,"no-watch"))return!0;for(let o of yn)if(ue(t,o))return!0;t=t.parentElement}return!1}function bn(e){let n=e.target,t=n.cloneNode(!1),o=t;for(;n.parentElement;){n=n.parentElement;let a=n.cloneNode(!1);a.appendChild(t),t=a}return e.oldValue==null?o.removeAttribute(e.attributeName):o.setAttribute(e.attributeName,e.oldValue),be(o)}function ee(e,n,t){if(t?.type==="attributes"&&it(t.attributeName)){if(be(e)&&bn(t))return!0}else if(be(e))return!0;if(t&&t.type==="attributes"){if(t.attributeName&&ot.test(t.attributeName.toLowerCase()))return!0;if(n)try{if(n(t.attributeName,t.target))return!0}catch{}}return!1}function at(){let e=new Map;function n(a,i){let s=e.get(a);return s||(s=new Set,e.set(a,s)),s.add(i),()=>t(a,i)}function t(a,i){let s=e.get(a);s&&s.delete(i)}function o(a,i){let s=e.get(a);if(s)for(let d of Array.from(s))try{d(i)}catch(f){console.error("[hyper-undo] listener threw",f)}}return{on:n,off:t,emit:o}}var kn=/[.?!]["'\u201D\u2019)\]]*$/,vn=new Set(["insertParagraph","insertLineBreak"]),En=new Set(["insertFromPaste","insertFromDrop","insertFromYank","insertReplacementText","deleteByCut","deleteByDrag"]),An=new Set(["Enter","Backspace","Delete"]),Sn=new Set(["ArrowLeft","ArrowRight","ArrowUp","ArrowDown","Home","End","PageUp","PageDown"]);function ke(e){return kn.test(e)}function ut(e,{sentence:n,endStep:t,stepOpen:o=()=>!0,composing:a}){let i=e.ownerDocument&&e.ownerDocument.defaultView;if(!i)return()=>{};let s=m=>!!m&&typeof m.nodeType=="number"&&(m===e||e.contains(m)),d=!1,f=!1,y=!1,p={kind:"other",host:null},w=m=>En.has(m)||m.startsWith("format")?"alone":m.startsWith("delete")?"delete":m.startsWith("insert")?"insert":"other",k=m=>{if(!m)return null;let T=m.nodeType===1?m:m.parentElement;return T&&T.closest&&T.closest('[contenteditable]:not([contenteditable="false"])')||m},A=()=>{d&&(d=!1,t())},S=m=>{if(!n||m.isComposing||!s(m.target))return;let T=m.inputType||"",V=w(T),Y=k(m.target),te=i.getSelection&&i.getSelection(),G=!te||te.isCollapsed,se=f;f=!1;let U=y;if(y=!1,(V==="alone"||!U&&V!==p.kind&&V!=="other"&&p.kind!=="other"||!U&&(p.host!==null&&Y!==p.host||!G||se))&&t(),p={kind:V,host:Y},V==="alone"){d=!0,setTimeout(A,0);return}let W=T==="insertText"&&(m.data===" "||m.data==="\xA0");!W&&!vn.has(T)||W&&!o()||ke(Nn(i))&&(d=!0,setTimeout(A,0))},R=m=>{s(m.target)&&A()},C=m=>{s(m.target)&&a(!0)},D=m=>{s(m.target)&&a(!1)},K=m=>{s(m.target)&&(f=!0)},B=m=>{if(!s(m.target))return;if(Sn.has(m.key)){f=!0;return}if(!n||m.isComposing||m.keyCode===229||m.ctrlKey||m.metaKey||!m.key||m.key.length!==1&&!An.has(m.key))return;let T=i.getSelection&&i.getSelection(),V=p.host!==null&&k(m.target)!==p.host;!f&&!V&&(!T||T.isCollapsed)||(t(),f=!1,y=!0,setTimeout(()=>{y=!1},0))};return e.addEventListener("beforeinput",S,!0),e.addEventListener("input",R,!0),e.addEventListener("compositionstart",C,!0),e.addEventListener("compositionend",D,!0),e.addEventListener("pointerdown",K,!0),e.addEventListener("keydown",B,!0),()=>{e.removeEventListener("beforeinput",S,!0),e.removeEventListener("input",R,!0),e.removeEventListener("compositionstart",C,!0),e.removeEventListener("compositionend",D,!0),e.removeEventListener("pointerdown",K,!0),e.removeEventListener("keydown",B,!0)}}function Nn(e){let n=e.getSelection&&e.getSelection();if(!n||!n.isCollapsed||!n.anchorNode)return"";let t=n.anchorNode,o=t.nodeType===1?t:t.parentElement,a=o&&o.closest('[contenteditable]:not([contenteditable="false"])');if(!a)return"";let i=a.ownerDocument.createRange();return i.setStart(a,0),i.setEnd(t,n.anchorOffset),i.toString().replace(/\u200B/g,"").slice(-8)}var Tn={maxHistory:100,idleWindowMs:500,idleLabel:"Edit",recording:"automatic",ignoreAttribute:null,isProtected:null,ignoreNode:null,durableForm:null,typingBreak:null,refused:null,debug:!1};function ve(e){let n={...Tn,...e};if(!n.scope)throw new Error("hyper-undo: scope is required");let t=at(),o=[],a=[],i=`u${Math.random().toString(36).slice(2,8)}`,s=0,d=null,f=[],y=null,p=null,w=0,k=!1,A=n.recording==="commands"?"commands":"automatic",S=null,R=!1,C=new WeakMap,D=!1,K=!1,B=null;function m(...r){n.debug&&console.log("[hyper-undo]",...r)}function T(){k||(k=!0,p=V(F),p.observe(n.scope,{childList:!0,attributes:!0,characterData:!0,subtree:!0,attributeOldValue:!0,characterDataOldValue:!0}),B=ut(n.scope,{sentence:n.typingBreak==="sentence",stepOpen:()=>(w===0&&p&&F(p.takeRecords()),f.length>0),endStep:z,composing:yt}),m("started"))}function V(r){let u=typeof document<"u"&&n.scope===document.body,l=typeof window<"u"&&(window.clay?.Mutation||window.hyperclay?.Mutation)||null;return u&&l&&typeof l.createObserver=="function"?(m("sourcing records from the platform Mutation hub (shared observer)"),l.createObserver(r)):new MutationObserver(r)}function Y(){k&&(k=!1,p&&(p.disconnect(),p=null),B&&(B(),B=null),D=!1,q(),o.length=0,a.length=0,f=[],d=null,S=null,R=!1,w=0,C=new WeakMap,m("stopped"))}function te(){let r=n.scope;return!!(r&&r.nodeType===1&&r.isConnected===!1)}function G(r,u){try{let l=u(r);return typeof l=="string"?l:null}catch{return null}}function se(r,{read:u,write:l,prop:c="content"}={}){if(!r||typeof u!="function"||typeof l!="function")return()=>{};let h={prop:c,read:u,write:l,baseline:G(r,u)};return C.set(r,h),()=>{C.get(r)===h&&C.delete(r)}}function U(r){for(let u=r;u;u=u.parentNode)if(C.has(u))return u;return null}function W(r){let u=U(r.target);return!!u&&!(r.type==="attributes"&&r.target===u)}function L(r){let u=new Set;for(let l of r)W(l)&&u.add(U(l.target));return u}function le(r){let u=[];for(let l of r){let c=C.get(l),h=G(l,c.read);if(h!=null){if(ae(h,c.baseline)){c.baseline=h;continue}c.baseline!=null&&u.push({kind:"value",target:l,prop:c.prop,oldValue:c.baseline,newValue:h,read:c.read,write:c.write,content:!0}),c.baseline=h}}return u}function j(r){for(let u of r){let l=C.get(u);l.baseline=G(u,l.read)}}function Te(r){let u=L(r);!K&&f.some(l=>l.kind==="value"?u.has(l.target):r.some(c=>c.target===l.target&&(c.type==="characterData"?l.kind==="text":c.type==="attributes"&&c.attributeName===l.name)))&&(K=!0),j(u)}function F(r){Ce(r,!1)}function Ce(r,u){if(S){for(let c of r)S.records.push(c);return}if(A==="commands"){j(L(r));return}if(w>0){Te(r);return}let l=de(gt(r),u);for(let c of le(L(r)))l.push(c);if(l.length>0){for(let c of l)f.push(c);ce()}}function gt(r){return typeof n.ignoreNode!="function"?r:r.filter(u=>{if(u.type!=="childList")return!0;let l=[...u.addedNodes,...u.removedNodes];return l.length===0?!0:!l.every(c=>ht(c))})}function ht(r){try{return!!n.ignoreNode(r)}catch{return!1}}function de(r,u=!1){let l=_t(r),c=r.filter(h=>(ge(h.target)||l(h.target)||u&&ze(h.target))&&!W(h)&&!ee(h.target,n.ignoreAttribute,h));return We(c,h=>ee(h,n.ignoreAttribute))}function yt(r){D=!!r,!D&&f.length>0&&ce()}function ce(){q(),y=setTimeout(Ie,n.idleWindowMs)}function Ie(){if(y=null,D){y=setTimeout(Ie,n.idleWindowMs);return}fe()}function q(){y!=null&&(clearTimeout(y),y=null)}function fe(){if(y=null,f.length===0)return;let r=f;f=[],ne(n.idleLabel,r)}function ne(r,u){let l=Qe(u,{isIgnored:re,ignoreAttribute:Ve,durableForm:n.durableForm});if(l.steps.length===0)return m("commit",r,"with nothing to record"),t.emit("commit"),null;let c={id:`${i}-${++s}`,label:r,timestamp:Date.now(),primitives:u,certificate:l};for(o.push(c),a.length=0,d=null;o.length>n.maxHistory;)o.shift();return m("commit",r,"primitives:",u.length),t.emit("commit"),c}function re(r){return ee(r,n.ignoreAttribute)}function Ve(r,u,l=null){if(typeof n.ignoreAttribute!="function")return!1;try{return!!n.ignoreAttribute(r,u,l)}catch{return!1}}function wt(r){if(typeof n.isProtected!="function")return!1;try{return!!n.isProtected(r)}catch{return!1}}function bt(r,u){if(!k)throw new Error("hyper-undo: scope not started");if(A==="commands")return xe(r,u,{honorPause:!0,receipt:!1});F(p.takeRecords()),z();let l=w===0?Pe():null,c=u();if(c&&typeof c.then=="function")throw new Error("hyper-undo: commit() fn must be synchronous; returned a Promise");if(Ce(p.takeRecords(),!0),l!=null&&Re(l),f.length===0){t.emit("commit");return}q();let h=f;f=[],ne(r,h)}function kt(r){if(!k)throw new Error("hyper-undo: scope not started");if(!p)return;if(w>1){j(L(p.takeRecords()));return}z();let u=p.takeRecords(),l=de(u,!0);for(let c of le(L(u)))l.push(c);l.length!==0&&(q(),ne(r,l))}function vt(){p&&j(L(p.takeRecords()))}function pe(){let r=n.scope;return r&&r.ownerDocument||(typeof document<"u"?document:null)}function oe(){let r=pe();return!r||!r.head||typeof r.head.querySelector!="function"?null:r.head.querySelector("title")}function Pe(){let r=oe();return r?r.textContent:null}function Re(r){let u=oe();if(!u||r==null)return;let l=u.textContent;l!==r&&_e({kind:"value",target:u,prop:"textContent",oldValue:r,newValue:l})}function Et(r){if(!r)return null;let u=pe();if(!u||r.target!==u||r.prop!=="title")return r;let l=oe();return l?{kind:"value",target:l,prop:"textContent",oldValue:r.oldValue,newValue:r.newValue}:null}function At(){if(typeof window>"u")return!1;let r=window.clay?.effect||window.hyperclay?.effect;return!!(r&&r.running)}function St(){p&&F(p.takeRecords()),z(),S={records:[],primitives:[]}}function Nt(){let r=S;if(S=null,!r)return[];let u=de(r.records,!0);for(let l of le(L(r.records)))u.push(l);for(let l of r.primitives)u.push(l);return u}function xe(r,u,{honorPause:l,receipt:c}){if(typeof u!="function")throw new Error("hyper-undo: command() needs a synchronous function");if(S)throw new Error("hyper-undo: command() cannot nest inside another command");if(R)throw new Error("hyper-undo: command() cannot run during undo or redo");if(At())throw new Error("hyper-undo: command() cannot run inside a derived-effect context");if(u.constructor&&u.constructor.name==="AsyncFunction")throw new Error("hyper-undo: command() fn must be synchronous; it was declared async");let h=l&&w>0,v=h?null:Pe();St();let x=[],E=null,P;try{try{P=u()}catch($){E=$}if(p&&F(p.takeRecords()),P&&typeof P.then=="function"){try{P.then(void 0,()=>{})}catch{}E||(E=new Error("hyper-undo: command() fn must be synchronous; returned a Promise"))}h||Re(v)}finally{x=Nt()}let O=h||x.length===0?null:ne(r,x);if(E)throw E;return c?O?{id:O.id,label:O.label}:null:O}function Tt(r,u){if(!k)throw new Error("hyper-undo: scope not started");return xe(r,u,{honorPause:!1,receipt:!0})}function Ct(r){if(r!=="automatic"&&r!=="commands")throw new Error("hyper-undo: setRecording() takes 'automatic' or 'commands'");if(S)throw new Error("hyper-undo: setRecording() cannot change while a command is running");return A===r||(z(),A=r,m("recording",r)),A}function Oe(r){if(!k||!r||!S&&(w>0||A==="commands"))return;let u=Et(r);u&&_e(u)}function _e(r){if(S){S.primitives.push(r);return}f.push(r),ce()}function It(r,{prop:u="value",oldValue:l,newValue:c,read:h,write:v}={}){!r||l===c||Oe({kind:"value",target:r,prop:u,oldValue:l,newValue:c,read:h,write:v})}function z(){w===0&&p&&F(p.takeRecords()),f.length!==0&&(q(),fe())}function Le(){w===0&&p&&F(p.takeRecords()),w++,m("paused",w)}function Me(){w!==0&&(w--,w===0&&p&&(Te(p.takeRecords()),K&&(K=!1,q(),fe())),m("resumed",w))}function De(r,u){if(te())return Y(),{ok:!1,code:"disconnected"};if(typeof n.holdReplay=="function"&&n.holdReplay())return{ok:!1,code:"held"};z();let l=r==="undo"?o:a,c=r==="undo"?a:o,h=[],v=l.at(-1);if(!v)return{ok:!1,code:"empty"};if(u&&u!==v.id){let E=Be(u)||v;return me(E,r,"stale-entry")}let x=n.refused==="skip"&&!u;for(;v;){let E;R=!0;try{E=et(v.certificate,r,{isIgnored:re,ignoreAttribute:Ve,durableForm:n.durableForm,isProtected:wt,inScope:Lt,liveNodesWithIds:Mt})}finally{R=!1}if(!E.ok){if(!x)return me(v,r,E.code,E.targets);Ke(v,r,E.code,E.targets,h),v=l.at(-1);continue}Le();let P;R=!0;try{P=nt(E.operations)}finally{R=!1,Me()}if(!P.ok){if(m(`${r} failed and was rolled back`,P.error),!x)return me(v,r,"replay-error");Ke(v,r,"replay-error",[],h),v=l.at(-1);continue}j(new Set(E.operations.filter($=>$.type==="value"&&C.has($.target)).map($=>$.target))),l.pop(),c.push(v),d=null;let O={ok:!0,entryId:v.id,label:v.label,direction:r};return h.length&&(O.skipped=h),P.caret&&(O.caret=P.caret),m(r,v.label),t.emit(r,O),O}return{ok:!1,code:"empty",direction:r,skipped:h}}function Ke(r,u,l,c,h){d={entryId:r.id,direction:u,code:l,targets:c};let v=Fe(r.id);(u==="undo"?o:a).pop(),d=null;let E={entryId:r.id,label:r.label,direction:u,code:l,targets:c,description:v};m("skipped",u,l),t.emit("skipped",E),h.push(E)}function Vt(r){return De("undo",r&&r.entryId)}function Pt(r){return De("redo",r&&r.entryId)}function Be(r){return o.find(u=>u.id===r)||a.find(u=>u.id===r)||null}function me(r,u,l,c=[]){return d={entryId:r.id,direction:u,code:l,targets:c},m("refused",u,l),t.emit("refused",{entryId:r.id,label:r.label,direction:u,code:l,targets:c}),{ok:!1,code:l,entryId:r.id,label:r.label,direction:u}}function Rt(r){if(!d||d.entryId!==r)return{ok:!1,code:"not-refused",entryId:r};let u=d.direction,l=u==="undo"?o:a,c=l.at(-1);return!c||c.id!==r?{ok:!1,code:"stale-entry",entryId:r}:(l.pop(),d=null,m("discarded",c.label),t.emit("discard",{entryId:r,label:c.label,direction:u}),{ok:!0,entryId:r,direction:u})}function Fe(r){let u=Be(r);if(!u||!u.certificate||!u.certificate.replayable)return null;let l=d&&d.entryId===u.id?{direction:d.direction,code:d.code,targets:(d.targets||[]).map(c=>({id:c.id??null,kind:c.kind??null}))}:null;return{id:u.id,label:u.label,timestamp:u.timestamp,refused:l,steps:u.certificate.steps.map(Ot)}}function xt(r){return typeof r!="string"?null:r.replace(/#form:("(?:[^"\\]|\\.)*")/g,(u,l)=>{try{return JSON.parse(l)}catch{return""}})}function Ot(r){if(r.type==="move")return{kind:"move",node:{id:I(r.node).id},from:{id:I(r.sourceParent).id},to:{id:I(r.destParent).id}};if(r.type==="add"||r.type==="restore")return{kind:r.type==="add"?"add":"remove",parent:{id:I(r.parent).id},nodes:r.nodes.map((v,x)=>({id:I(v).id,html:xt(r.content[x])}))};let u=r.primitive,l={id:I(u.target).id},c=u.oldValue??null,h=u.newValue??null;return r.type==="value"?{kind:"value",target:l,prop:u.prop??"value",before:c,after:h}:r.type==="text"?{kind:"text",target:l,before:c,after:h}:r.type==="attr-set"||r.type==="attr-add"||r.type==="attr-remove"?{kind:"attribute",target:l,name:u.name??null,before:c,after:h}:{kind:r.type,target:l,before:c,after:h}}function _t(r){let u=new Set,l=!0;for(;l;){l=!1;for(let c of r)if(!(c.type!=="childList"||!c.removedNodes?.length)&&!W(c)&&!(!ge(c.target)&&!qe(c.target,u))&&!ee(c.target,n.ignoreAttribute,c))for(let h of c.removedNodes)u.has(h)||(u.add(h),l=!0)}return c=>!ge(c)&&qe(c,u)}function qe(r,u){for(let l=r;l;l=l.parentNode)if(u.has(l))return!0;return!1}function ge(r){let u=n.scope;return!u||!r||typeof r.nodeType!="number"?!1:r===u||typeof u.contains=="function"&&u.contains(r)}function ze(r){let u=pe(),l=u&&u.head;if(!l||!r)return!1;let c=r.nodeType===1?r:r.parentElement;for(;c&&c!==l;c=c.parentElement)if(c.localName==="title")return!1;return c===l}function Lt(r){let u=n.scope;return!r||!u?!1:r===u||oe()===r||ze(r)?!0:typeof u.contains=="function"?u.contains(r):!1}function Mt(r){let u=n.scope;if(!r||r.length===0||typeof u?.querySelectorAll!="function")return[];let l=new Set(r),c=[];for(let h of u.querySelectorAll("[data-id]"))l.has(h.getAttribute("data-id"))&&(re(h)||c.push(h));return c}function Dt(){p&&j(L(p.takeRecords())),o.length=0,a.length=0,f=[],d=null,q(),m("cleared"),t.emit("clear")}return{_config:n,_tracksContentAt:r=>A!=="commands"&&!!U(r),_recordsAt:r=>A!=="commands"&&!re(r),start:T,stop:Y,commit:bt,commitCaptured:kt,discardCaptured:vt,flush:z,command:Tt,setRecording:Ct,record:Oe,recordValue:It,trackContent:se,undo:Vt,redo:Pt,clear:Dt,discard:Rt,describe:Fe,pause:Le,resume:Me,on:t.on,off:t.off,get canUndo(){return o.length>0},get canRedo(){return a.length>0},get isPaused(){return w>0},get history(){return o.map(r=>{let u={id:r.id,label:r.label,timestamp:r.timestamp};return d&&d.entryId===r.id&&(u.refused={direction:d.direction,code:d.code,targets:d.targets}),u})},get future(){return a.map(r=>{let u={id:r.id,label:r.label,timestamp:r.timestamp};return d&&d.entryId===r.id&&(u.refused={direction:d.direction,code:d.code,targets:d.targets}),u}).reverse()},get scope(){return n.scope}}}var Cn=new Set(["checkbox","radio","button","submit","reset","image","file","range","color"]);function dt(e){let n=typeof e.composedPath=="function"?e.composedPath():[];return n.length>0?n:Vn(e.target)}function In(e){for(let n of dt(e)){if(!n||n.nodeType!==1)continue;let t=String(n.tagName||"").toUpperCase();if(t==="TEXTAREA"||t==="SELECT"||t==="INPUT"&&!Cn.has(String(n.type||n.getAttribute?.("type")||"text").toLowerCase()))return!0}return!1}function st(e){for(let n of dt(e)){if(!n||n.nodeType!==1)continue;if(n.isContentEditable===!0)return!0;let t=n.getAttribute&&n.getAttribute("contenteditable");if(t!=null&&String(t).toLowerCase()!=="false")return!0}return!1}function Ee(e,n){if(!e||!e.closest||!Array.isArray(n))return!1;for(let t of n)try{if(e.closest(t))return!0}catch{}return!1}function lt(e,n){if(In(e))return!0;let t=n._config,o=e.target;return t.editableKeys!=="page"?st(e)||Ee(o,t.shadowKeydownIn):Ee(o,t.ownKeysIn)||n._tracksContentAt(o)?!1:Ee(o,t.shadowKeydownIn)?!0:st(e)&&!n._recordsAt(o)}function Vn(e){let n=[];for(let t=e;t;t=t.parentNode||(t.host??null))n.push(t);return n}function ct(e,n){let t=i=>{let s=e.scope;if(s&&s.nodeType===1&&s.isConnected===!1){n&&n();return}if(i.defaultPrevented||i.isComposing||i.keyCode===229||lt(i,e)||!(i.metaKey||i.ctrlKey))return;let f=(i.key||"").toLowerCase();if(f==="z"&&!i.shiftKey){i.preventDefault(),i.stopPropagation(),e.undo();return}if(f==="z"&&i.shiftKey){i.preventDefault(),i.stopPropagation(),e.redo();return}if(f==="y"&&!i.shiftKey){i.preventDefault(),i.stopPropagation(),e.redo();return}},o=i=>{if(i.inputType!=="historyUndo"&&i.inputType!=="historyRedo")return;let s=e.scope;s&&s.nodeType===1&&s.isConnected===!1||i.defaultPrevented||i.isComposing||lt(i,e)||(i.preventDefault(),i.stopPropagation(),i.inputType==="historyRedo"?e.redo():e.undo())},a=e._config.editableKeys==="page";return window.addEventListener("keydown",t,!0),a&&window.addEventListener("beforeinput",o,!0),()=>{window.removeEventListener("keydown",t,!0),a&&window.removeEventListener("beforeinput",o,!0)}}var Ae=[".CodeMirror",".cm-editor",".monaco-editor",".ace_editor",".ql-editor",".tiptap",".ProseMirror",".overtype-input","[data-richclay-active]",".ck-editor__editable","[data-lexical-editor]","[data-slate-editor]","trix-editor",".mce-content-body"],g=null,Se=null,ft=null,M=null;function pt(e,n){if(M){if(M.inst===e)return M.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 t=ct(e,n);return M={inst:e,cleanup:t},t}function mt(e){M&&M.inst===e&&(M.cleanup(),M=null)}var Ne={defaults:{shadowKeydownIn:Ae},start(e={}){let n=e.scope||(typeof document<"u"?document.body:null);if(!n)throw new Error("hyper-undo: no scope (need document.body or explicit { scope })");if(g){if(Se===n)return console.warn("[hyper-undo] start() called again on existing singleton; ignoring new options"),g;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 t={...e,scope:n,shadowKeydownIn:e.shadowKeydownIn||Ae};return g=ve(t),g.start(),Se=n,e.bindKeys!==!1&&(ft=pt(g,()=>Ne.stop())),g},stop(){g&&(mt(g),ft=null,g.stop(),g=null,Se=null)},create(e={}){let n=e.scope||(typeof document<"u"?document.body:null);if(!n)throw new Error("hyper-undo: no scope (need document.body or explicit { scope })");let t={...e,scope:n,shadowKeydownIn:e.shadowKeydownIn||Ae},o=ve(t),a=null,i={_config:o._config,commit:o.commit,command:o.command,setRecording:o.setRecording,commitCaptured:o.commitCaptured,discardCaptured:o.discardCaptured,flush:o.flush,record:o.record,recordValue:o.recordValue,trackContent:o.trackContent,undo:o.undo,redo:o.redo,clear:o.clear,discard:o.discard,describe:o.describe,pause:o.pause,resume:o.resume,on:o.on,off:o.off,get canUndo(){return o.canUndo},get canRedo(){return o.canRedo},get isPaused(){return o.isPaused},get history(){return o.history},get future(){return o.future},get scope(){return o.scope},start(){e.bindKeys&&(a=pt(o,()=>i.stop())),o.start()},stop(){mt(o),a=null,o.stop()}};return i},commit(e,n){return g?g.commit(e,n):void 0},command(e,n){return g?g.command(e,n):void 0},setRecording(e){return g?g.setRecording(e):void 0},commitCaptured(e){return g?g.commitCaptured(e):void 0},discardCaptured(){return g?g.discardCaptured():void 0},record(e){return g?g.record(e):void 0},recordValue(e,n){return g?g.recordValue(e,n):void 0},trackContent(e,n){return g?g.trackContent(e,n):()=>{}},undo(e){return g?g.undo(e):void 0},redo(e){return g?g.redo(e):void 0},clear(){return g?g.clear():void 0},discard(e){return g?g.discard(e):void 0},describe(e){return g?g.describe(e):void 0},pause(){return g?g.pause():void 0},resume(){return g?g.resume():void 0},flush(){return g?g.flush():void 0},on(e,n){return g?g.on(e,n):void 0},off(e,n){return g?g.off(e,n):void 0},get canUndo(){return g?g.canUndo:!1},get canRedo(){return g?g.canRedo:!1},get history(){return g?g.history:[]},get future(){return g?g.future:[]},get isPaused(){return g?g.isPaused:!1}};var Pn=Ne;return $t(Rn);})();
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createScope } from './scope.js'
|
|
2
2
|
import { installKeys } from './keys.js'
|
|
3
|
+
import { endsSentence } from './typing.js'
|
|
3
4
|
|
|
4
5
|
const DEFAULT_SHADOW_LIST = [
|
|
5
6
|
'.CodeMirror',
|
|
@@ -21,6 +22,14 @@ const DEFAULT_SHADOW_LIST = [
|
|
|
21
22
|
// in richclay/src/richclay.js, which says it marks the region so hyper-undo
|
|
22
23
|
// "defers to Squire instead of fighting it".
|
|
23
24
|
'[data-richclay-active]',
|
|
25
|
+
// Embedded third-party editors keep their own history: the page stack never
|
|
26
|
+
// records what is typed in them, so a page-mode Cmd+Z there would revert some
|
|
27
|
+
// other field instead of the editor's own last change.
|
|
28
|
+
'.ck-editor__editable',
|
|
29
|
+
'[data-lexical-editor]',
|
|
30
|
+
'[data-slate-editor]',
|
|
31
|
+
'trix-editor',
|
|
32
|
+
'.mce-content-body',
|
|
24
33
|
]
|
|
25
34
|
|
|
26
35
|
let singleton = null // the Scope instance returned by createScope
|
|
@@ -113,6 +122,7 @@ const undo = {
|
|
|
113
122
|
flush: inst.flush,
|
|
114
123
|
record: inst.record,
|
|
115
124
|
recordValue: inst.recordValue,
|
|
125
|
+
trackContent: inst.trackContent,
|
|
116
126
|
undo: inst.undo,
|
|
117
127
|
redo: inst.redo,
|
|
118
128
|
clear: inst.clear,
|
|
@@ -152,6 +162,7 @@ const undo = {
|
|
|
152
162
|
discardCaptured() { return singleton ? singleton.discardCaptured() : undefined },
|
|
153
163
|
record(p) { return singleton ? singleton.record(p) : undefined },
|
|
154
164
|
recordValue(t, o) { return singleton ? singleton.recordValue(t, o) : undefined },
|
|
165
|
+
trackContent(root, o) { return singleton ? singleton.trackContent(root, o) : () => {} },
|
|
155
166
|
undo(opts) { return singleton ? singleton.undo(opts) : undefined },
|
|
156
167
|
redo(opts) { return singleton ? singleton.redo(opts) : undefined },
|
|
157
168
|
clear() { return singleton ? singleton.clear() : undefined },
|
|
@@ -169,5 +180,5 @@ const undo = {
|
|
|
169
180
|
get isPaused() { return singleton ? singleton.isPaused : false },
|
|
170
181
|
}
|
|
171
182
|
|
|
172
|
-
export { undo }
|
|
183
|
+
export { undo, endsSentence }
|
|
173
184
|
export default undo
|
package/src/keys.js
CHANGED
|
@@ -2,16 +2,26 @@
|
|
|
2
2
|
// subtree)? The browser's own undo belongs there, not the page stack's.
|
|
3
3
|
const NO_TEXT_INPUTS = new Set(['checkbox', 'radio', 'button', 'submit', 'reset', 'image', 'file', 'range', 'color'])
|
|
4
4
|
|
|
5
|
-
function
|
|
5
|
+
function pathOf(event) {
|
|
6
6
|
const path = typeof event.composedPath === 'function' ? event.composedPath() : []
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
return path.length > 0 ? path : chainFromTarget(event.target)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function inFormControl(event) {
|
|
11
|
+
for (const node of pathOf(event)) {
|
|
9
12
|
if (!node || node.nodeType !== 1) continue
|
|
10
13
|
const tag = String(node.tagName || '').toUpperCase()
|
|
11
14
|
if (tag === 'TEXTAREA' || tag === 'SELECT') return true
|
|
12
15
|
// An input that takes no typed text has no native undo of its own: a checkbox,
|
|
13
16
|
// a radio or a button leaves the key to the page stack.
|
|
14
17
|
if (tag === 'INPUT' && !NO_TEXT_INPUTS.has(String(node.type || node.getAttribute?.('type') || 'text').toLowerCase())) return true
|
|
18
|
+
}
|
|
19
|
+
return false
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function inEditable(event) {
|
|
23
|
+
for (const node of pathOf(event)) {
|
|
24
|
+
if (!node || node.nodeType !== 1) continue
|
|
15
25
|
if (node.isContentEditable === true) return true
|
|
16
26
|
// jsdom (and older engines) do not resolve isContentEditable, so read the
|
|
17
27
|
// declaration itself; an unset or explicit "false" is not editable.
|
|
@@ -21,6 +31,28 @@ function ownsNativeUndo(event) {
|
|
|
21
31
|
return false
|
|
22
32
|
}
|
|
23
33
|
|
|
34
|
+
function inAny(target, selectors) {
|
|
35
|
+
if (!target || !target.closest || !Array.isArray(selectors)) return false
|
|
36
|
+
for (const selector of selectors) {
|
|
37
|
+
try { if (target.closest(selector)) return true }
|
|
38
|
+
catch (_) { /* invalid selector, skip */ }
|
|
39
|
+
}
|
|
40
|
+
return false
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Does this history key or history input belong to someone other than the page
|
|
44
|
+
// stack? "page" owns editable content the scope records; a form control, an
|
|
45
|
+
// authored no-undo region or another in-page editor keeps its own undo.
|
|
46
|
+
function standsDown(event, scope) {
|
|
47
|
+
if (inFormControl(event)) return true
|
|
48
|
+
const config = scope._config
|
|
49
|
+
const target = event.target
|
|
50
|
+
if (config.editableKeys !== 'page') return inEditable(event) || inAny(target, config.shadowKeydownIn)
|
|
51
|
+
if (inAny(target, config.ownKeysIn) || scope._tracksContentAt(target)) return false
|
|
52
|
+
if (inAny(target, config.shadowKeydownIn)) return true
|
|
53
|
+
return inEditable(event) && !scope._recordsAt(target)
|
|
54
|
+
}
|
|
55
|
+
|
|
24
56
|
function chainFromTarget(target) {
|
|
25
57
|
const out = []
|
|
26
58
|
for (let node = target; node; node = node.parentNode || (node.host ?? null)) out.push(node)
|
|
@@ -44,19 +76,7 @@ export function installKeys(scope, onDetached) {
|
|
|
44
76
|
if (event.defaultPrevented) return
|
|
45
77
|
// The IME owns keys while it composes.
|
|
46
78
|
if (event.isComposing || event.keyCode === 229) return
|
|
47
|
-
|
|
48
|
-
// control changes; Save/Enter later commits that text as a page command.
|
|
49
|
-
if (ownsNativeUndo(event)) return
|
|
50
|
-
|
|
51
|
-
// Bypass for in-page editors (CodeMirror, Monaco, etc.)
|
|
52
|
-
const target = event.target
|
|
53
|
-
const shadow = scope._config.shadowKeydownIn
|
|
54
|
-
if (target && target.closest && Array.isArray(shadow)) {
|
|
55
|
-
for (const selector of shadow) {
|
|
56
|
-
try { if (target.closest(selector)) return }
|
|
57
|
-
catch (_) { /* invalid selector, skip */ }
|
|
58
|
-
}
|
|
59
|
-
}
|
|
79
|
+
if (standsDown(event, scope)) return
|
|
60
80
|
|
|
61
81
|
const mod = event.metaKey || event.ctrlKey
|
|
62
82
|
if (!mod) return
|
|
@@ -80,6 +100,24 @@ export function installKeys(scope, onDetached) {
|
|
|
80
100
|
}
|
|
81
101
|
}
|
|
82
102
|
|
|
103
|
+
// Chrome delivers the Edit menu's Undo as a cancellable beforeinput event.
|
|
104
|
+
// Owned by the same rule as the key, it moves the page stack instead.
|
|
105
|
+
const onHistoryInput = (event) => {
|
|
106
|
+
if (event.inputType !== 'historyUndo' && event.inputType !== 'historyRedo') return
|
|
107
|
+
const scopeEl = scope.scope
|
|
108
|
+
if (scopeEl && scopeEl.nodeType === 1 && scopeEl.isConnected === false) return
|
|
109
|
+
if (event.defaultPrevented || event.isComposing) return
|
|
110
|
+
if (standsDown(event, scope)) return
|
|
111
|
+
event.preventDefault(); event.stopPropagation()
|
|
112
|
+
if (event.inputType === 'historyRedo') scope.redo()
|
|
113
|
+
else scope.undo()
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const ownsInput = scope._config.editableKeys === 'page'
|
|
83
117
|
window.addEventListener('keydown', handler, true)
|
|
84
|
-
|
|
118
|
+
if (ownsInput) window.addEventListener('beforeinput', onHistoryInput, true)
|
|
119
|
+
return () => {
|
|
120
|
+
window.removeEventListener('keydown', handler, true)
|
|
121
|
+
if (ownsInput) window.removeEventListener('beforeinput', onHistoryInput, true)
|
|
122
|
+
}
|
|
85
123
|
}
|
package/src/primitives.js
CHANGED
|
@@ -124,7 +124,7 @@ export function recordsToPrimitives(records, ignoreNode = null) {
|
|
|
124
124
|
// more than once. Keep ONE primitive per (target, field) carrying the first old
|
|
125
125
|
// value and the final new value, and drop the ones whose endpoints are equal:
|
|
126
126
|
// undo must walk back to the value before the entry, not to an intermediate one.
|
|
127
|
-
export function normalizePrimitives(primitives) {
|
|
127
|
+
export function normalizePrimitives(primitives, { sameContent = (a, b) => a === b } = {}) {
|
|
128
128
|
const out = []
|
|
129
129
|
const slots = new Map() // target → Map(field → index in out)
|
|
130
130
|
|
|
@@ -148,6 +148,7 @@ export function normalizePrimitives(primitives) {
|
|
|
148
148
|
for (const p of out) {
|
|
149
149
|
if (scalarField(p) == null) { normalized.push(p); continue }
|
|
150
150
|
if (p.oldValue === p.newValue) continue
|
|
151
|
+
if (p.kind === 'value' && p.content === true && sameContent(p.oldValue, p.newValue)) continue
|
|
151
152
|
normalized.push(reclassifyScalar(p))
|
|
152
153
|
}
|
|
153
154
|
return normalized
|