hyper-undo 0.4.0 → 0.5.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 +15 -0
- package/dist/hyper-undo.min.js +1 -1
- package/package.json +11 -1
- package/src/extension-noise.js +5 -1
- package/src/filter.js +9 -7
- package/src/scope.js +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# hyper-undo changelog
|
|
2
2
|
|
|
3
|
+
## [0.5.0] - 2026-08-12
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Script to copy the built bundle to clayjs
|
|
7
|
+
- `kind`, `status`, and `url` declarations in the hyper key
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
- Platform namespace is now read off `window.clay` in addition to `hyperclay`
|
|
11
|
+
- Updated hyper-undo
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
- Documentation that blocked outside users
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
3
18
|
## [0.4.0] - 2026-06-16
|
|
4
19
|
|
|
5
20
|
### Changed
|
package/dist/hyper-undo.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var hyperundo=(()=>{var T=Object.defineProperty;var ee=Object.getOwnPropertyDescriptor;var te=Object.getOwnPropertyNames;var re=Object.prototype.hasOwnProperty;var ne=(e,t)=>{for(var n in t)T(e,n,{get:t[n],enumerable:!0})},oe=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of te(t))!re.call(e,o)&&o!==n&&T(e,o,{get:()=>t[o],enumerable:!(r=ee(t,o))||r.enumerable});return e};var ae=e=>oe(T({},"__esModule",{value:!0}),e);var me={};ne(me,{default:()=>pe,undo:()=>C});function ie(e,t=null){let n=[];if(e.type==="attributes"){let r=e.target,o=e.attributeName,i=e.oldValue,u=r.getAttribute(o);return i==null&&u!=null?n.push({kind:"attr-add",target:r,name:o,newValue:u}):i!=null&&u==null?n.push({kind:"attr-remove",target:r,name:o,oldValue:i}):i!==u&&n.push({kind:"attr-set",target:r,name:o,oldValue:i,newValue:u}),n}if(e.type==="characterData")return n.push({kind:"text",target:e.target,oldValue:e.oldValue,newValue:e.target.data}),n;if(e.type==="childList"){let r=e.target,o=e.nextSibling,i=t?Array.from(e.addedNodes).filter(d=>!t(d)):Array.from(e.addedNodes),u=t?Array.from(e.removedNodes).filter(d=>!t(d)):Array.from(e.removedNodes);return i.length>0&&n.push({kind:"add",parent:r,nodes:i,before:o}),u.length>0&&n.push({kind:"remove",parent:r,nodes:u,before:o}),n}return n}function P(e,t=null){let n=[],r=new Map;for(let o of e){if(o.type==="attributes"){let i=o.target,u=o.attributeName,d=r.get(i);d||(d=new Map,r.set(i,d)),d.has(u)||(d.set(u,{idx:n.length,oldValue:o.oldValue}),n.push(null));continue}for(let i of ie(o,t))n.push(i)}for(let[o,i]of r)for(let[u,d]of i){let l=d.oldValue,m=o.getAttribute(u),p=null;l===m?p=null:l==null?p={kind:"attr-add",target:o,name:u,newValue:m}:m==null?p={kind:"attr-remove",target:o,name:u,oldValue:l}:p={kind:"attr-set",target:o,name:u,oldValue:l,newValue:m},n[d.idx]=p}return n.filter(o=>o!=null)}function L(e){switch(e.kind){case"attr-set":case"attr-add":e.target.setAttribute(e.name,e.newValue);return;case"attr-remove":e.target.removeAttribute(e.name);return;case"text":e.target.data=e.newValue;return;case"value":e.target[e.prop]=e.newValue;return;case"add":for(let t of e.nodes)e.before&&e.before.parentNode===e.parent?e.parent.insertBefore(t,e.before):e.parent.appendChild(t);return;case"remove":for(let t of e.nodes)t.parentNode===e.parent&&e.parent.removeChild(t);return}}function D(e){switch(e.kind){case"attr-set":e.target.setAttribute(e.name,e.oldValue);return;case"attr-add":e.target.removeAttribute(e.name);return;case"attr-remove":e.target.setAttribute(e.name,e.oldValue);return;case"text":e.target.data=e.oldValue;return;case"value":e.target[e.prop]=e.oldValue;return;case"add":for(let t of e.nodes)t.parentNode===e.parent&&e.parent.removeChild(t);return;case"remove":for(let t of e.nodes)e.before&&e.before.parentNode===e.parent?e.parent.insertBefore(t,e.before):e.parent.appendChild(t);return}}var se=['[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]"],ue=["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"],M=se.join(","),U=new RegExp("^(?:"+ue.join("|")+")(?:-|$)");var de=["mutations-ignore","save-remove","save-ignore","save-freeze","no-undo","no-watch"];function le(){return typeof window<"u"&&window.hyperclay&&window.hyperclay.region&&typeof window.hyperclay.region.resolveRegionPolicy=="function"?window.hyperclay.region.resolveRegionPolicy:null}function ce(e){let t=le();if(t)try{return!t(e).undoable}catch{}let n=e&&e.nodeType!==1?e.parentElement:e;if(n&&n.closest&&n.closest(M))return!0;for(;n&&n.nodeType===1;){for(let r of de)if(n.hasAttribute&&n.hasAttribute(r))return!0;n=n.parentElement}return!1}function R(e,t,n){if(ce(e))return!0;if(n&&n.type==="attributes"){if(n.attributeName&&U.test(n.attributeName.toLowerCase()))return!0;if(t)try{if(t(n.attributeName,n.target))return!0}catch{}}return!1}function X(){let e=new Map;function t(o,i){let u=e.get(o);return u||(u=new Set,e.set(o,u)),u.add(i),()=>n(o,i)}function n(o,i){let u=e.get(o);u&&u.delete(i)}function r(o,i){let u=e.get(o);if(u)for(let d of Array.from(u))try{d(i)}catch(l){console.error("[hyper-undo] listener threw",l)}}return{on:t,off:n,emit:r}}var fe={maxHistory:100,idleWindowMs:500,idleLabel:"Edit",ignoreAttribute:null,debug:!1};function N(e){let t={...fe,...e};if(!t.scope)throw new Error("hyper-undo: scope is required");let n=X(),r=[],o=[],i=[],u=null,d=null,l=0,m=!1;function p(...s){t.debug&&console.log("[hyper-undo]",...s)}function H(){m||(m=!0,d=W(g),d.observe(t.scope,{childList:!0,attributes:!0,characterData:!0,subtree:!0,attributeOldValue:!0,characterDataOldValue:!0}),p("started"))}function W(s){let c=typeof document<"u"&&t.scope===document.body,f=typeof window<"u"&&window.hyperclay&&window.hyperclay.Mutation||null;return c&&f&&typeof f.createObserver=="function"?(p("sourcing records from window.hyperclay.Mutation (shared observer)"),f.createObserver(s)):new MutationObserver(s)}function E(){m&&(m=!1,d&&(d.disconnect(),d=null),y(),r.length=0,o.length=0,i=[],l=0,p("stopped"))}function V(){let s=t.scope;return!!(s&&s.nodeType===1&&s.isConnected===!1)}function g(s){if(l>0)return;let c=_(s);if(c.length>0){for(let f of c)i.push(f);I()}}function _(s){let c=s.filter(f=>!R(f.target,t.ignoreAttribute,f));return P(c,f=>R(f,t.ignoreAttribute))}function I(){y(),u=setTimeout(K,t.idleWindowMs)}function y(){u!=null&&(clearTimeout(u),u=null)}function K(){if(u=null,i.length===0)return;let s=i;i=[],v(t.idleLabel,s)}function v(s,c){for(r.push({label:s,timestamp:Date.now(),primitives:c}),o.length=0;r.length>t.maxHistory;)r.shift();p("commit",s,"primitives:",c.length),n.emit("commit")}function j(s,c){if(!m)throw new Error("hyper-undo: scope not started");g(d.takeRecords()),w();let f=c();if(f&&typeof f.then=="function")throw new Error("hyper-undo: commit() fn must be synchronous; returned a Promise");if(g(d.takeRecords()),i.length===0){n.emit("commit");return}y();let b=i;i=[],v(s,b)}function G(s){if(!m)throw new Error("hyper-undo: scope not started");if(!d)return;if(l>1){d.takeRecords();return}w();let c=_(d.takeRecords());c.length!==0&&(y(),v(s,c))}function Z(){d&&d.takeRecords()}function O(s){!m||l>0||!s||(i.push(s),I())}function $(s,{prop:c="value",oldValue:f,newValue:b}={}){!s||f===b||O({kind:"value",target:s,prop:c,oldValue:f,newValue:b})}function w(){l===0&&d&&g(d.takeRecords()),i.length!==0&&(y(),K())}function k(){l===0&&d&&g(d.takeRecords()),l++,p("paused",l)}function x(){l!==0&&(l--,l===0&&d&&d.takeRecords(),p("resumed",l))}function J(){if(V()){E();return}w();let s=r.pop();if(s){k();try{for(let c=s.primitives.length-1;c>=0;c--)try{D(s.primitives[c])}catch(f){p("undo primitive failed (continuing)",f)}}finally{x()}o.push(s),p("undo",s.label),n.emit("undo")}}function Q(){if(V()){E();return}w();let s=o.pop();if(s){k();try{for(let c of s.primitives)try{L(c)}catch(f){p("redo primitive failed (continuing)",f)}}finally{x()}r.push(s),p("redo",s.label),n.emit("redo")}}function Y(){d&&d.takeRecords(),r.length=0,o.length=0,i=[],y(),p("cleared"),n.emit("clear")}return{_config:t,start:H,stop:E,commit:j,commitCaptured:G,discardCaptured:Z,flush:w,record:O,recordValue:$,undo:J,redo:Q,clear:Y,pause:k,resume:x,on:n.on,off:n.off,get canUndo(){return r.length>0},get canRedo(){return o.length>0},get isPaused(){return l>0},get history(){return r.map(s=>({label:s.label,timestamp:s.timestamp}))},get scope(){return t.scope}}}function q(e,t){let n=r=>{let o=e.scope;if(o&&o.nodeType===1&&o.isConnected===!1){t&&t();return}let i=r.target,u=e._config.shadowKeydownIn;if(i&&i.closest&&Array.isArray(u))for(let m of u)try{if(i.closest(m))return}catch{}if(!(r.metaKey||r.ctrlKey))return;let l=(r.key||"").toLowerCase();if(l==="z"&&!r.shiftKey){r.preventDefault(),r.stopPropagation(),e.undo();return}if(l==="z"&&r.shiftKey){r.preventDefault(),r.stopPropagation(),e.redo();return}if(l==="y"&&!r.shiftKey){r.preventDefault(),r.stopPropagation(),e.redo();return}};return window.addEventListener("keydown",n,!0),()=>window.removeEventListener("keydown",n,!0)}var A=[".CodeMirror",".cm-editor",".monaco-editor",".ace_editor",".ql-editor",".tiptap",".ProseMirror"],a=null,S=null,z=null,h=null;function F(e,t){if(h){if(h.inst===e)return h.cleanup;throw new Error("hyper-undo: another scope already owns the global Cmd+Z binding (bindKeys: true). Stop it first, or use bindKeys: false for additional scopes.")}let n=q(e,t);return h={inst:e,cleanup:n},n}function B(e){h&&h.inst===e&&(h.cleanup(),h=null)}var C={defaults:{shadowKeydownIn:A},start(e={}){let t=e.scope||(typeof document<"u"?document.body:null);if(!t)throw new Error("hyper-undo: no scope (need document.body or explicit { scope })");if(a){if(S===t)return console.warn("[hyper-undo] start() called again on existing singleton; ignoring new options"),a;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 n={...e,scope:t,shadowKeydownIn:e.shadowKeydownIn||A};return a=N(n),a.start(),S=t,e.bindKeys!==!1&&(z=F(a,()=>C.stop())),a},stop(){a&&(B(a),z=null,a.stop(),a=null,S=null)},create(e={}){let t=e.scope||(typeof document<"u"?document.body:null);if(!t)throw new Error("hyper-undo: no scope (need document.body or explicit { scope })");let n={...e,scope:t,shadowKeydownIn:e.shadowKeydownIn||A},r=N(n),o=null,i={_config:r._config,commit:r.commit,commitCaptured:r.commitCaptured,discardCaptured:r.discardCaptured,flush:r.flush,record:r.record,recordValue:r.recordValue,undo:r.undo,redo:r.redo,clear:r.clear,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 scope(){return r.scope},start(){e.bindKeys&&(o=F(r,()=>i.stop())),r.start()},stop(){B(r),o=null,r.stop()}};return i},commit(e,t){return a?a.commit(e,t):void 0},commitCaptured(e){return a?a.commitCaptured(e):void 0},discardCaptured(){return a?a.discardCaptured():void 0},record(e){return a?a.record(e):void 0},recordValue(e,t){return a?a.recordValue(e,t):void 0},undo(){return a?a.undo():void 0},redo(){return a?a.redo():void 0},clear(){return a?a.clear():void 0},pause(){return a?a.pause():void 0},resume(){return a?a.resume():void 0},flush(){return a?a.flush():void 0},on(e,t){return a?a.on(e,t):void 0},off(e,t){return a?a.off(e,t):void 0},get canUndo(){return a?a.canUndo:!1},get canRedo(){return a?a.canRedo:!1},get history(){return a?a.history:[]},get isPaused(){return a?a.isPaused:!1}};var pe=C;return ae(me);})();
|
|
1
|
+
var hyperundo=(()=>{var T=Object.defineProperty;var ee=Object.getOwnPropertyDescriptor;var te=Object.getOwnPropertyNames;var re=Object.prototype.hasOwnProperty;var ne=(e,t)=>{for(var n in t)T(e,n,{get:t[n],enumerable:!0})},oe=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of te(t))!re.call(e,o)&&o!==n&&T(e,o,{get:()=>t[o],enumerable:!(r=ee(t,o))||r.enumerable});return e};var ae=e=>oe(T({},"__esModule",{value:!0}),e);var me={};ne(me,{default:()=>pe,undo:()=>C});function ie(e,t=null){let n=[];if(e.type==="attributes"){let r=e.target,o=e.attributeName,i=e.oldValue,u=r.getAttribute(o);return i==null&&u!=null?n.push({kind:"attr-add",target:r,name:o,newValue:u}):i!=null&&u==null?n.push({kind:"attr-remove",target:r,name:o,oldValue:i}):i!==u&&n.push({kind:"attr-set",target:r,name:o,oldValue:i,newValue:u}),n}if(e.type==="characterData")return n.push({kind:"text",target:e.target,oldValue:e.oldValue,newValue:e.target.data}),n;if(e.type==="childList"){let r=e.target,o=e.nextSibling,i=t?Array.from(e.addedNodes).filter(d=>!t(d)):Array.from(e.addedNodes),u=t?Array.from(e.removedNodes).filter(d=>!t(d)):Array.from(e.removedNodes);return i.length>0&&n.push({kind:"add",parent:r,nodes:i,before:o}),u.length>0&&n.push({kind:"remove",parent:r,nodes:u,before:o}),n}return n}function P(e,t=null){let n=[],r=new Map;for(let o of e){if(o.type==="attributes"){let i=o.target,u=o.attributeName,d=r.get(i);d||(d=new Map,r.set(i,d)),d.has(u)||(d.set(u,{idx:n.length,oldValue:o.oldValue}),n.push(null));continue}for(let i of ie(o,t))n.push(i)}for(let[o,i]of r)for(let[u,d]of i){let l=d.oldValue,m=o.getAttribute(u),p=null;l===m?p=null:l==null?p={kind:"attr-add",target:o,name:u,newValue:m}:m==null?p={kind:"attr-remove",target:o,name:u,oldValue:l}:p={kind:"attr-set",target:o,name:u,oldValue:l,newValue:m},n[d.idx]=p}return n.filter(o=>o!=null)}function L(e){switch(e.kind){case"attr-set":case"attr-add":e.target.setAttribute(e.name,e.newValue);return;case"attr-remove":e.target.removeAttribute(e.name);return;case"text":e.target.data=e.newValue;return;case"value":e.target[e.prop]=e.newValue;return;case"add":for(let t of e.nodes)e.before&&e.before.parentNode===e.parent?e.parent.insertBefore(t,e.before):e.parent.appendChild(t);return;case"remove":for(let t of e.nodes)t.parentNode===e.parent&&e.parent.removeChild(t);return}}function D(e){switch(e.kind){case"attr-set":e.target.setAttribute(e.name,e.oldValue);return;case"attr-add":e.target.removeAttribute(e.name);return;case"attr-remove":e.target.setAttribute(e.name,e.oldValue);return;case"text":e.target.data=e.oldValue;return;case"value":e.target[e.prop]=e.oldValue;return;case"add":for(let t of e.nodes)t.parentNode===e.parent&&e.parent.removeChild(t);return;case"remove":for(let t of e.nodes)e.before&&e.before.parentNode===e.parent?e.parent.insertBefore(t,e.before):e.parent.appendChild(t);return}}var se=['[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]"],ue=["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"],M=se.join(","),U=new RegExp("^(?:"+ue.join("|")+")(?:-|$)");var de=["mutations-ignore","save-remove","save-ignore","save-freeze","no-undo","no-watch"];function le(){if(typeof window>"u")return null;let e=window.clay?.region||window.hyperclay?.region;return e&&typeof e.resolveRegionPolicy=="function"?e.resolveRegionPolicy:null}function ce(e){let t=le();if(t)try{return!t(e).undoable}catch{}let n=e&&e.nodeType!==1?e.parentElement:e;if(n&&n.closest&&n.closest(M))return!0;for(;n&&n.nodeType===1;){for(let r of de)if(n.hasAttribute&&n.hasAttribute(r))return!0;n=n.parentElement}return!1}function R(e,t,n){if(ce(e))return!0;if(n&&n.type==="attributes"){if(n.attributeName&&U.test(n.attributeName.toLowerCase()))return!0;if(t)try{if(t(n.attributeName,n.target))return!0}catch{}}return!1}function X(){let e=new Map;function t(o,i){let u=e.get(o);return u||(u=new Set,e.set(o,u)),u.add(i),()=>n(o,i)}function n(o,i){let u=e.get(o);u&&u.delete(i)}function r(o,i){let u=e.get(o);if(u)for(let d of Array.from(u))try{d(i)}catch(l){console.error("[hyper-undo] listener threw",l)}}return{on:t,off:n,emit:r}}var fe={maxHistory:100,idleWindowMs:500,idleLabel:"Edit",ignoreAttribute:null,debug:!1};function N(e){let t={...fe,...e};if(!t.scope)throw new Error("hyper-undo: scope is required");let n=X(),r=[],o=[],i=[],u=null,d=null,l=0,m=!1;function p(...s){t.debug&&console.log("[hyper-undo]",...s)}function H(){m||(m=!0,d=W(g),d.observe(t.scope,{childList:!0,attributes:!0,characterData:!0,subtree:!0,attributeOldValue:!0,characterDataOldValue:!0}),p("started"))}function W(s){let c=typeof document<"u"&&t.scope===document.body,f=typeof window<"u"&&(window.clay?.Mutation||window.hyperclay?.Mutation)||null;return c&&f&&typeof f.createObserver=="function"?(p("sourcing records from the platform Mutation hub (shared observer)"),f.createObserver(s)):new MutationObserver(s)}function E(){m&&(m=!1,d&&(d.disconnect(),d=null),y(),r.length=0,o.length=0,i=[],l=0,p("stopped"))}function V(){let s=t.scope;return!!(s&&s.nodeType===1&&s.isConnected===!1)}function g(s){if(l>0)return;let c=_(s);if(c.length>0){for(let f of c)i.push(f);I()}}function _(s){let c=s.filter(f=>!R(f.target,t.ignoreAttribute,f));return P(c,f=>R(f,t.ignoreAttribute))}function I(){y(),u=setTimeout(K,t.idleWindowMs)}function y(){u!=null&&(clearTimeout(u),u=null)}function K(){if(u=null,i.length===0)return;let s=i;i=[],v(t.idleLabel,s)}function v(s,c){for(r.push({label:s,timestamp:Date.now(),primitives:c}),o.length=0;r.length>t.maxHistory;)r.shift();p("commit",s,"primitives:",c.length),n.emit("commit")}function j(s,c){if(!m)throw new Error("hyper-undo: scope not started");g(d.takeRecords()),w();let f=c();if(f&&typeof f.then=="function")throw new Error("hyper-undo: commit() fn must be synchronous; returned a Promise");if(g(d.takeRecords()),i.length===0){n.emit("commit");return}y();let b=i;i=[],v(s,b)}function G(s){if(!m)throw new Error("hyper-undo: scope not started");if(!d)return;if(l>1){d.takeRecords();return}w();let c=_(d.takeRecords());c.length!==0&&(y(),v(s,c))}function Z(){d&&d.takeRecords()}function O(s){!m||l>0||!s||(i.push(s),I())}function $(s,{prop:c="value",oldValue:f,newValue:b}={}){!s||f===b||O({kind:"value",target:s,prop:c,oldValue:f,newValue:b})}function w(){l===0&&d&&g(d.takeRecords()),i.length!==0&&(y(),K())}function k(){l===0&&d&&g(d.takeRecords()),l++,p("paused",l)}function x(){l!==0&&(l--,l===0&&d&&d.takeRecords(),p("resumed",l))}function J(){if(V()){E();return}w();let s=r.pop();if(s){k();try{for(let c=s.primitives.length-1;c>=0;c--)try{D(s.primitives[c])}catch(f){p("undo primitive failed (continuing)",f)}}finally{x()}o.push(s),p("undo",s.label),n.emit("undo")}}function Q(){if(V()){E();return}w();let s=o.pop();if(s){k();try{for(let c of s.primitives)try{L(c)}catch(f){p("redo primitive failed (continuing)",f)}}finally{x()}r.push(s),p("redo",s.label),n.emit("redo")}}function Y(){d&&d.takeRecords(),r.length=0,o.length=0,i=[],y(),p("cleared"),n.emit("clear")}return{_config:t,start:H,stop:E,commit:j,commitCaptured:G,discardCaptured:Z,flush:w,record:O,recordValue:$,undo:J,redo:Q,clear:Y,pause:k,resume:x,on:n.on,off:n.off,get canUndo(){return r.length>0},get canRedo(){return o.length>0},get isPaused(){return l>0},get history(){return r.map(s=>({label:s.label,timestamp:s.timestamp}))},get scope(){return t.scope}}}function q(e,t){let n=r=>{let o=e.scope;if(o&&o.nodeType===1&&o.isConnected===!1){t&&t();return}let i=r.target,u=e._config.shadowKeydownIn;if(i&&i.closest&&Array.isArray(u))for(let m of u)try{if(i.closest(m))return}catch{}if(!(r.metaKey||r.ctrlKey))return;let l=(r.key||"").toLowerCase();if(l==="z"&&!r.shiftKey){r.preventDefault(),r.stopPropagation(),e.undo();return}if(l==="z"&&r.shiftKey){r.preventDefault(),r.stopPropagation(),e.redo();return}if(l==="y"&&!r.shiftKey){r.preventDefault(),r.stopPropagation(),e.redo();return}};return window.addEventListener("keydown",n,!0),()=>window.removeEventListener("keydown",n,!0)}var A=[".CodeMirror",".cm-editor",".monaco-editor",".ace_editor",".ql-editor",".tiptap",".ProseMirror"],a=null,S=null,z=null,h=null;function F(e,t){if(h){if(h.inst===e)return h.cleanup;throw new Error("hyper-undo: another scope already owns the global Cmd+Z binding (bindKeys: true). Stop it first, or use bindKeys: false for additional scopes.")}let n=q(e,t);return h={inst:e,cleanup:n},n}function B(e){h&&h.inst===e&&(h.cleanup(),h=null)}var C={defaults:{shadowKeydownIn:A},start(e={}){let t=e.scope||(typeof document<"u"?document.body:null);if(!t)throw new Error("hyper-undo: no scope (need document.body or explicit { scope })");if(a){if(S===t)return console.warn("[hyper-undo] start() called again on existing singleton; ignoring new options"),a;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 n={...e,scope:t,shadowKeydownIn:e.shadowKeydownIn||A};return a=N(n),a.start(),S=t,e.bindKeys!==!1&&(z=F(a,()=>C.stop())),a},stop(){a&&(B(a),z=null,a.stop(),a=null,S=null)},create(e={}){let t=e.scope||(typeof document<"u"?document.body:null);if(!t)throw new Error("hyper-undo: no scope (need document.body or explicit { scope })");let n={...e,scope:t,shadowKeydownIn:e.shadowKeydownIn||A},r=N(n),o=null,i={_config:r._config,commit:r.commit,commitCaptured:r.commitCaptured,discardCaptured:r.discardCaptured,flush:r.flush,record:r.record,recordValue:r.recordValue,undo:r.undo,redo:r.redo,clear:r.clear,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 scope(){return r.scope},start(){e.bindKeys&&(o=F(r,()=>i.stop())),r.start()},stop(){B(r),o=null,r.stop()}};return i},commit(e,t){return a?a.commit(e,t):void 0},commitCaptured(e){return a?a.commitCaptured(e):void 0},discardCaptured(){return a?a.discardCaptured():void 0},record(e){return a?a.record(e):void 0},recordValue(e,t){return a?a.recordValue(e,t):void 0},undo(){return a?a.undo():void 0},redo(){return a?a.redo():void 0},clear(){return a?a.clear():void 0},pause(){return a?a.pause():void 0},resume(){return a?a.resume():void 0},flush(){return a?a.flush():void 0},on(e,t){return a?a.on(e,t):void 0},off(e,t){return a?a.off(e,t):void 0},get canUndo(){return a?a.canUndo:!1},get canRedo(){return a?a.canRedo:!1},get history(){return a?a.history:[]},get isPaused(){return a?a.isPaused:!1}};var pe=C;return ae(me);})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hyper-undo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "DOM-state undo/redo via MutationObserver inverse-op replay. Standalone or paired with hyperclayjs.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"undo",
|
|
@@ -15,6 +15,10 @@
|
|
|
15
15
|
"url": "https://github.com/panphora/hyper-undo/issues"
|
|
16
16
|
},
|
|
17
17
|
"license": "MIT",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/panphora/hyper-undo.git"
|
|
21
|
+
},
|
|
18
22
|
"author": "David Miranda",
|
|
19
23
|
"type": "module",
|
|
20
24
|
"main": "src/index.js",
|
|
@@ -32,6 +36,7 @@
|
|
|
32
36
|
"scripts": {
|
|
33
37
|
"build": "npx esbuild src/index.js --bundle --minify --format=iife --global-name=hyperundo --outfile=dist/hyper-undo.min.js",
|
|
34
38
|
"copy-to-hyperclayjs": "npm run build && node scripts/copy-to-hyperclayjs.js",
|
|
39
|
+
"copy-to-clayjs": "npm run build && node scripts/copy-to-clayjs.js",
|
|
35
40
|
"test:node": "node --test 'test-node/**/*.test.js'",
|
|
36
41
|
"test:shimmed": "HYPER_UNDO_SHIMMED=1 node --test 'test-node/**/*.test.js'",
|
|
37
42
|
"test": "npm run test:node && npm run test:shimmed",
|
|
@@ -41,5 +46,10 @@
|
|
|
41
46
|
"devDependencies": {
|
|
42
47
|
"esbuild": "^0.25.0",
|
|
43
48
|
"jsdom": "^25.0.0"
|
|
49
|
+
},
|
|
50
|
+
"hyper": {
|
|
51
|
+
"kind": "lib",
|
|
52
|
+
"status": "active",
|
|
53
|
+
"url": "https://github.com/panphora/hyper-undo"
|
|
44
54
|
}
|
|
45
55
|
}
|
package/src/extension-noise.js
CHANGED
|
@@ -52,7 +52,11 @@ export function stripExtensionNoise(root) {
|
|
|
52
52
|
if (!root || !root.querySelectorAll) return
|
|
53
53
|
try {
|
|
54
54
|
for (const el of root.querySelectorAll(EXTENSION_NODE_SELECTOR)) el.remove()
|
|
55
|
-
|
|
55
|
+
// querySelectorAll('*') never returns the root itself, so an extension stamp on
|
|
56
|
+
// <html> used to ride into every saved file. The root is the one element an
|
|
57
|
+
// extension can mark on a page with no other match.
|
|
58
|
+
for (const el of [root, ...root.querySelectorAll('*')]) {
|
|
59
|
+
if (!el.attributes) continue
|
|
56
60
|
for (const attr of [...el.attributes]) {
|
|
57
61
|
if (EXTENSION_ATTR_PATTERN.test(attr.name.toLowerCase())) el.removeAttribute(attr.name)
|
|
58
62
|
}
|
package/src/filter.js
CHANGED
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
// browser-extension content), when the record is an extension marker attribute,
|
|
5
5
|
// or when the caller's ignoreAttributePredicate rejects it.
|
|
6
6
|
//
|
|
7
|
-
// When paired with
|
|
8
|
-
// resolver
|
|
9
|
-
// undo and the platform
|
|
10
|
-
//
|
|
7
|
+
// When paired with a platform client we delegate the region decision to the SAME
|
|
8
|
+
// resolver it ships (clay.region.resolveRegionPolicy under clayjs,
|
|
9
|
+
// hyperclay.region.resolveRegionPolicy under hyperclayjs), so undo and the platform
|
|
10
|
+
// can never drift. Standalone (neither on window) we fall back to a local marker
|
|
11
|
+
// walk that mirrors the resolver's `undoable` axis.
|
|
11
12
|
|
|
12
13
|
import { EXTENSION_NODE_SELECTOR, EXTENSION_ATTR_PATTERN } from './extension-noise.js'
|
|
13
14
|
|
|
@@ -18,9 +19,10 @@ import { EXTENSION_NODE_SELECTOR, EXTENSION_ATTR_PATTERN } from './extension-noi
|
|
|
18
19
|
const IGNORE_ATTRS = ['mutations-ignore', 'save-remove', 'save-ignore', 'save-freeze', 'no-undo', 'no-watch']
|
|
19
20
|
|
|
20
21
|
function sharedResolver() {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
if (typeof window === 'undefined') return null
|
|
23
|
+
const region = window.clay?.region || window.hyperclay?.region
|
|
24
|
+
return region && typeof region.resolveRegionPolicy === 'function'
|
|
25
|
+
? region.resolveRegionPolicy
|
|
24
26
|
: null
|
|
25
27
|
}
|
|
26
28
|
|
package/src/scope.js
CHANGED
|
@@ -51,9 +51,10 @@ export function createScope(opts) {
|
|
|
51
51
|
// to the ones above, so nothing here needs to change between the two paths.
|
|
52
52
|
function createRecordSource(handler) {
|
|
53
53
|
const isBodySingleton = typeof document !== 'undefined' && config.scope === document.body
|
|
54
|
-
const hub = (typeof window !== 'undefined' &&
|
|
54
|
+
const hub = (typeof window !== 'undefined' &&
|
|
55
|
+
(window.clay?.Mutation || window.hyperclay?.Mutation)) || null
|
|
55
56
|
if (isBodySingleton && hub && typeof hub.createObserver === 'function') {
|
|
56
|
-
log('sourcing records from
|
|
57
|
+
log('sourcing records from the platform Mutation hub (shared observer)')
|
|
57
58
|
return hub.createObserver(handler)
|
|
58
59
|
}
|
|
59
60
|
return new MutationObserver(handler)
|