louper 0.1.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.
@@ -0,0 +1,25 @@
1
+ "use strict";var Louper=(()=>{var _={zoomLevel:2,radius:150,borderWidth:3,borderColor:"white",hotkey:"Alt"},D=1,F=10,K=.002,I=100;function q(f={}){let t={..._,...f},l=document.createElement("louper-lens"),c=l.attachShadow({mode:"closed"}),u=document.createElement("style");u.textContent=g(),c.appendChild(u);let p=document.createElement("div");p.className="louper-clone-wrapper";let n=document.createElement("div");n.className="louper-clone-container",p.appendChild(n),c.appendChild(p),document.body.appendChild(l);let r=!1,y=0,L=0,b=0,E=0,a=t.zoomLevel,h=null,x=!1,d=null;function g(){let e=t.radius*2;return`
2
+ :host {
3
+ position: fixed; top: 0; left: 0;
4
+ z-index: 2147483647;
5
+ pointer-events: none;
6
+ width: ${e}px; height: ${e}px;
7
+ border-radius: 50%;
8
+ border: ${t.borderWidth}px solid ${t.borderColor};
9
+ box-shadow: 0 0 0 1px rgba(0,0,0,0.1);
10
+ overflow: hidden; opacity: 0;
11
+ transition: opacity ${I}ms ease-out;
12
+ will-change: transform, opacity;
13
+ }
14
+ :host(.louper-active) { opacity: 1; transition: none; }
15
+ .louper-clone-wrapper {
16
+ position: absolute; top: 0; left: 0;
17
+ width: 100%; height: 100%;
18
+ overflow: hidden; border-radius: 50%;
19
+ }
20
+ .louper-clone-container {
21
+ position: absolute; top: 0; left: 0;
22
+ transform-origin: 0 0;
23
+ }
24
+ `}function Y(){x=!1,h=null;let{radius:e,borderWidth:i}=t;l.style.transform=`translate(${y-e-i}px,${L-e-i}px)`;let M=e-(y+b)*a,T=e-(L+E)*a;n.style.transform=`scale(${a}) translate(${M/a}px,${T/a}px)`}function m(){x||(x=!0,h=requestAnimationFrame(Y))}function k(){let e=document.documentElement,i=e.cloneNode(!0);i.querySelectorAll("script").forEach(o=>o.remove()),i.querySelectorAll("louper-lens").forEach(o=>o.remove()),c.querySelectorAll("[data-louper-sheet]").forEach(o=>o.remove());for(let o of Array.from(document.styleSheets))try{if(o.href){let s=document.createElement("link");s.rel="stylesheet",s.href=o.href,s.setAttribute("data-louper-sheet",""),c.appendChild(s)}else if(o.ownerNode instanceof HTMLStyleElement){let s=document.createElement("style");s.textContent=o.ownerNode.textContent,s.setAttribute("data-louper-sheet",""),c.appendChild(s)}}catch{}let M=e.querySelectorAll("canvas"),T=i.querySelectorAll("canvas");M.forEach((o,s)=>{let w=T[s];if(!w)return;w.width=o.width,w.height=o.height;let W=w.getContext("2d");if(W)try{W.drawImage(o,0,0)}catch{}}),i.style.margin="0",i.style.position="absolute",i.style.top="0",i.style.left="0",i.style.width=e.scrollWidth+"px",i.style.height=e.scrollHeight+"px",n.innerHTML="",n.appendChild(i)}let C=new MutationObserver(()=>{r&&(d&&clearTimeout(d),d=setTimeout(()=>{r&&k()},16))});function O(){r||(r=!0,b=window.scrollX,E=window.scrollY,a=t.zoomLevel,k(),l.classList.add("louper-active"),m(),C.observe(document.body,{childList:!0,subtree:!0,characterData:!0,attributes:!0,attributeFilter:["class","style","data-state"]}))}function v(){r&&(r=!1,C.disconnect(),d&&(clearTimeout(d),d=null),l.classList.remove("louper-active"),setTimeout(()=>{r||(n.innerHTML="")},I))}function S(e){e.key===t.hotkey&&!r&&O()}function A(e){e.key===t.hotkey&&v()}function z(e){y=e.clientX,L=e.clientY,r&&m()}function N(){r&&(b=window.scrollX,E=window.scrollY,m())}function $(e){r&&(e.preventDefault(),a=Math.min(F,Math.max(D,a*(1-e.deltaY*K))),m())}t.hotkey&&(window.addEventListener("keydown",S),window.addEventListener("keyup",A),window.addEventListener("blur",v)),window.addEventListener("mousemove",z),window.addEventListener("scroll",N,{passive:!0}),window.addEventListener("wheel",$,{passive:!1});function H(){r=!1,C.disconnect(),d&&clearTimeout(d),h!==null&&cancelAnimationFrame(h),window.removeEventListener("keydown",S),window.removeEventListener("keyup",A),window.removeEventListener("mousemove",z),window.removeEventListener("blur",v),window.removeEventListener("scroll",N),window.removeEventListener("wheel",$),l.remove()}function X(e){t={...t,...e},e.zoomLevel!==void 0&&(a=e.zoomLevel),u.textContent=g(),r&&m()}return{destroy:H,update:X,activate:O,deactivate:v}}function P(){let f=document.currentScript;if(!f)return{};let{zoom:t,radius:l,borderWidth:c,borderColor:u,hotkey:p}=f.dataset,n={};return t&&(n.zoomLevel=Number(t)),l&&(n.radius=Number(l)),c&&(n.borderWidth=Number(c)),u&&(n.borderColor=u),p&&(n.hotkey=p),n}var R=q(P());window.__louper=R;})();
25
+ //# sourceMappingURL=auto.global.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/auto.ts"],"sourcesContent":["export interface LoupeOptions {\n /** Default: 2 */\n zoomLevel?: number\n /** Lens radius in px. Default: 150 */\n radius?: number\n /** Default: 3 */\n borderWidth?: number\n /** Default: \"white\" */\n borderColor?: string\n /** Default: \"Alt\". Set to false to disable. */\n hotkey?: 'Alt' | 'Control' | 'Shift' | 'Meta' | false\n}\n\nexport interface LoupeInstance {\n destroy: () => void\n update: (opts: Partial<LoupeOptions>) => void\n activate: () => void\n deactivate: () => void\n}\n\nexport const DEFAULTS: Required<LoupeOptions> = {\n zoomLevel: 2,\n radius: 150,\n borderWidth: 3,\n borderColor: 'white',\n hotkey: 'Alt',\n}\n\nconst MIN_ZOOM = 1\nconst MAX_ZOOM = 10\nconst ZOOM_SENSITIVITY = 0.002\nconst FADE_OUT = 100\n\nexport function createLouper(userOpts: LoupeOptions = {}): LoupeInstance {\n let opts = { ...DEFAULTS, ...userOpts }\n\n const host = document.createElement('louper-lens')\n const root = host.attachShadow({ mode: 'closed' })\n const styleEl = document.createElement('style')\n styleEl.textContent = css()\n root.appendChild(styleEl)\n const wrapper = document.createElement('div')\n wrapper.className = 'louper-clone-wrapper'\n const content = document.createElement('div')\n content.className = 'louper-clone-container'\n wrapper.appendChild(content)\n root.appendChild(wrapper)\n document.body.appendChild(host)\n\n let active = false\n let mouseX = 0, mouseY = 0, scrollX = 0, scrollY = 0\n let zoomLevel = opts.zoomLevel\n let rafId: number | null = null\n let pending = false\n let mutationTimer: ReturnType<typeof setTimeout> | null = null\n\n function css() {\n const d = opts.radius * 2\n return `\n :host {\n position: fixed; top: 0; left: 0;\n z-index: 2147483647;\n pointer-events: none;\n width: ${d}px; height: ${d}px;\n border-radius: 50%;\n border: ${opts.borderWidth}px solid ${opts.borderColor};\n box-shadow: 0 0 0 1px rgba(0,0,0,0.1);\n overflow: hidden; opacity: 0;\n transition: opacity ${FADE_OUT}ms ease-out;\n will-change: transform, opacity;\n }\n :host(.louper-active) { opacity: 1; transition: none; }\n .louper-clone-wrapper {\n position: absolute; top: 0; left: 0;\n width: 100%; height: 100%;\n overflow: hidden; border-radius: 50%;\n }\n .louper-clone-container {\n position: absolute; top: 0; left: 0;\n transform-origin: 0 0;\n }\n `\n }\n\n function render() {\n pending = false\n rafId = null\n const { radius, borderWidth } = opts\n host.style.transform = `translate(${mouseX - radius - borderWidth}px,${mouseY - radius - borderWidth}px)`\n const cx = radius - (mouseX + scrollX) * zoomLevel\n const cy = radius - (mouseY + scrollY) * zoomLevel\n content.style.transform = `scale(${zoomLevel}) translate(${cx / zoomLevel}px,${cy / zoomLevel}px)`\n }\n\n function scheduleRender() {\n if (!pending) {\n pending = true\n rafId = requestAnimationFrame(render)\n }\n }\n\n function clonePage() {\n const docEl = document.documentElement\n const copy = docEl.cloneNode(true) as HTMLElement\n copy.querySelectorAll('script').forEach(s => s.remove())\n copy.querySelectorAll('louper-lens').forEach(el => el.remove())\n\n root.querySelectorAll('[data-louper-sheet]').forEach(el => el.remove())\n for (const sheet of Array.from(document.styleSheets)) {\n try {\n if (sheet.href) {\n const link = document.createElement('link')\n link.rel = 'stylesheet'\n link.href = sheet.href\n link.setAttribute('data-louper-sheet', '')\n root.appendChild(link)\n } else if (sheet.ownerNode instanceof HTMLStyleElement) {\n const s = document.createElement('style')\n s.textContent = sheet.ownerNode.textContent\n s.setAttribute('data-louper-sheet', '')\n root.appendChild(s)\n }\n } catch {} // cross-origin\n }\n\n const srcCanvases = docEl.querySelectorAll('canvas')\n const dstCanvases = copy.querySelectorAll('canvas')\n srcCanvases.forEach((src, i) => {\n const dst = dstCanvases[i]\n if (!dst) return\n dst.width = src.width\n dst.height = src.height\n const ctx = dst.getContext('2d')\n if (ctx) try { ctx.drawImage(src, 0, 0) } catch {} // tainted\n })\n\n copy.style.margin = '0'\n copy.style.position = 'absolute'\n copy.style.top = '0'\n copy.style.left = '0'\n copy.style.width = docEl.scrollWidth + 'px'\n copy.style.height = docEl.scrollHeight + 'px'\n content.innerHTML = ''\n content.appendChild(copy)\n }\n\n const observer = new MutationObserver(() => {\n if (!active) return\n if (mutationTimer) clearTimeout(mutationTimer)\n mutationTimer = setTimeout(() => { if (active) clonePage() }, 16)\n })\n\n function activate() {\n if (active) return\n active = true\n scrollX = window.scrollX\n scrollY = window.scrollY\n zoomLevel = opts.zoomLevel\n clonePage()\n host.classList.add('louper-active')\n scheduleRender()\n observer.observe(document.body, {\n childList: true, subtree: true, characterData: true,\n attributes: true, attributeFilter: ['class', 'style', 'data-state'],\n })\n }\n\n function deactivate() {\n if (!active) return\n active = false\n observer.disconnect()\n if (mutationTimer) { clearTimeout(mutationTimer); mutationTimer = null }\n host.classList.remove('louper-active')\n setTimeout(() => { if (!active) content.innerHTML = '' }, FADE_OUT)\n }\n\n function onKeyDown(e: KeyboardEvent) { if (e.key === opts.hotkey && !active) activate() }\n function onKeyUp(e: KeyboardEvent) { if (e.key === opts.hotkey) deactivate() }\n function onMouseMove(e: MouseEvent) {\n mouseX = e.clientX\n mouseY = e.clientY\n if (active) scheduleRender()\n }\n function onScroll() {\n if (!active) return\n scrollX = window.scrollX\n scrollY = window.scrollY\n scheduleRender()\n }\n function onWheel(e: WheelEvent) {\n if (!active) return\n e.preventDefault()\n zoomLevel = Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, zoomLevel * (1 - e.deltaY * ZOOM_SENSITIVITY)))\n scheduleRender()\n }\n\n if (opts.hotkey) {\n window.addEventListener('keydown', onKeyDown)\n window.addEventListener('keyup', onKeyUp)\n window.addEventListener('blur', deactivate)\n }\n window.addEventListener('mousemove', onMouseMove)\n window.addEventListener('scroll', onScroll, { passive: true })\n window.addEventListener('wheel', onWheel, { passive: false })\n\n function destroy() {\n active = false\n observer.disconnect()\n if (mutationTimer) clearTimeout(mutationTimer)\n if (rafId !== null) cancelAnimationFrame(rafId)\n window.removeEventListener('keydown', onKeyDown)\n window.removeEventListener('keyup', onKeyUp)\n window.removeEventListener('mousemove', onMouseMove)\n window.removeEventListener('blur', deactivate)\n window.removeEventListener('scroll', onScroll)\n window.removeEventListener('wheel', onWheel)\n host.remove()\n }\n\n function update(newOpts: Partial<LoupeOptions>) {\n opts = { ...opts, ...newOpts }\n if (newOpts.zoomLevel !== undefined) zoomLevel = newOpts.zoomLevel\n styleEl.textContent = css()\n if (active) scheduleRender()\n }\n\n return { destroy, update, activate, deactivate }\n}\n","import { createLouper, type LoupeOptions } from '.'\n\nfunction getScriptOptions(): LoupeOptions {\n const el = document.currentScript as HTMLScriptElement | null\n if (!el) return {}\n const { zoom, radius, borderWidth, borderColor, hotkey } = el.dataset\n const opts: LoupeOptions = {}\n if (zoom) opts.zoomLevel = Number(zoom)\n if (radius) opts.radius = Number(radius)\n if (borderWidth) opts.borderWidth = Number(borderWidth)\n if (borderColor) opts.borderColor = borderColor\n if (hotkey) opts.hotkey = hotkey as LoupeOptions['hotkey']\n return opts\n}\n\nconst instance = createLouper(getScriptOptions())\n;(window as any).__louper = instance\n"],"mappings":"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","names":["DEFAULTS","MIN_ZOOM","MAX_ZOOM","ZOOM_SENSITIVITY","FADE_OUT","createLouper","userOpts","opts","host","root","styleEl","css","wrapper","content","active","mouseX","mouseY","scrollX","scrollY","zoomLevel","rafId","pending","mutationTimer","d","render","radius","borderWidth","cx","cy","scheduleRender","clonePage","docEl","copy","s","el","sheet","link","srcCanvases","dstCanvases","src","i","dst","ctx","observer","activate","deactivate","onKeyDown","onKeyUp","onMouseMove","onScroll","onWheel","destroy","update","newOpts","getScriptOptions","el","zoom","radius","borderWidth","borderColor","hotkey","opts","instance","createLouper"]}
package/dist/index.cjs ADDED
@@ -0,0 +1,239 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ DEFAULTS: () => DEFAULTS,
24
+ createLouper: () => createLouper
25
+ });
26
+ module.exports = __toCommonJS(src_exports);
27
+ var DEFAULTS = {
28
+ zoomLevel: 2,
29
+ radius: 150,
30
+ borderWidth: 3,
31
+ borderColor: "white",
32
+ hotkey: "Alt"
33
+ };
34
+ var MIN_ZOOM = 1;
35
+ var MAX_ZOOM = 10;
36
+ var ZOOM_SENSITIVITY = 2e-3;
37
+ var FADE_OUT = 100;
38
+ function createLouper(userOpts = {}) {
39
+ let opts = { ...DEFAULTS, ...userOpts };
40
+ const host = document.createElement("louper-lens");
41
+ const root = host.attachShadow({ mode: "closed" });
42
+ const styleEl = document.createElement("style");
43
+ styleEl.textContent = css();
44
+ root.appendChild(styleEl);
45
+ const wrapper = document.createElement("div");
46
+ wrapper.className = "louper-clone-wrapper";
47
+ const content = document.createElement("div");
48
+ content.className = "louper-clone-container";
49
+ wrapper.appendChild(content);
50
+ root.appendChild(wrapper);
51
+ document.body.appendChild(host);
52
+ let active = false;
53
+ let mouseX = 0, mouseY = 0, scrollX = 0, scrollY = 0;
54
+ let zoomLevel = opts.zoomLevel;
55
+ let rafId = null;
56
+ let pending = false;
57
+ let mutationTimer = null;
58
+ function css() {
59
+ const d = opts.radius * 2;
60
+ return `
61
+ :host {
62
+ position: fixed; top: 0; left: 0;
63
+ z-index: 2147483647;
64
+ pointer-events: none;
65
+ width: ${d}px; height: ${d}px;
66
+ border-radius: 50%;
67
+ border: ${opts.borderWidth}px solid ${opts.borderColor};
68
+ box-shadow: 0 0 0 1px rgba(0,0,0,0.1);
69
+ overflow: hidden; opacity: 0;
70
+ transition: opacity ${FADE_OUT}ms ease-out;
71
+ will-change: transform, opacity;
72
+ }
73
+ :host(.louper-active) { opacity: 1; transition: none; }
74
+ .louper-clone-wrapper {
75
+ position: absolute; top: 0; left: 0;
76
+ width: 100%; height: 100%;
77
+ overflow: hidden; border-radius: 50%;
78
+ }
79
+ .louper-clone-container {
80
+ position: absolute; top: 0; left: 0;
81
+ transform-origin: 0 0;
82
+ }
83
+ `;
84
+ }
85
+ function render() {
86
+ pending = false;
87
+ rafId = null;
88
+ const { radius, borderWidth } = opts;
89
+ host.style.transform = `translate(${mouseX - radius - borderWidth}px,${mouseY - radius - borderWidth}px)`;
90
+ const cx = radius - (mouseX + scrollX) * zoomLevel;
91
+ const cy = radius - (mouseY + scrollY) * zoomLevel;
92
+ content.style.transform = `scale(${zoomLevel}) translate(${cx / zoomLevel}px,${cy / zoomLevel}px)`;
93
+ }
94
+ function scheduleRender() {
95
+ if (!pending) {
96
+ pending = true;
97
+ rafId = requestAnimationFrame(render);
98
+ }
99
+ }
100
+ function clonePage() {
101
+ const docEl = document.documentElement;
102
+ const copy = docEl.cloneNode(true);
103
+ copy.querySelectorAll("script").forEach((s) => s.remove());
104
+ copy.querySelectorAll("louper-lens").forEach((el) => el.remove());
105
+ root.querySelectorAll("[data-louper-sheet]").forEach((el) => el.remove());
106
+ for (const sheet of Array.from(document.styleSheets)) {
107
+ try {
108
+ if (sheet.href) {
109
+ const link = document.createElement("link");
110
+ link.rel = "stylesheet";
111
+ link.href = sheet.href;
112
+ link.setAttribute("data-louper-sheet", "");
113
+ root.appendChild(link);
114
+ } else if (sheet.ownerNode instanceof HTMLStyleElement) {
115
+ const s = document.createElement("style");
116
+ s.textContent = sheet.ownerNode.textContent;
117
+ s.setAttribute("data-louper-sheet", "");
118
+ root.appendChild(s);
119
+ }
120
+ } catch {
121
+ }
122
+ }
123
+ const srcCanvases = docEl.querySelectorAll("canvas");
124
+ const dstCanvases = copy.querySelectorAll("canvas");
125
+ srcCanvases.forEach((src, i) => {
126
+ const dst = dstCanvases[i];
127
+ if (!dst) return;
128
+ dst.width = src.width;
129
+ dst.height = src.height;
130
+ const ctx = dst.getContext("2d");
131
+ if (ctx) try {
132
+ ctx.drawImage(src, 0, 0);
133
+ } catch {
134
+ }
135
+ });
136
+ copy.style.margin = "0";
137
+ copy.style.position = "absolute";
138
+ copy.style.top = "0";
139
+ copy.style.left = "0";
140
+ copy.style.width = docEl.scrollWidth + "px";
141
+ copy.style.height = docEl.scrollHeight + "px";
142
+ content.innerHTML = "";
143
+ content.appendChild(copy);
144
+ }
145
+ const observer = new MutationObserver(() => {
146
+ if (!active) return;
147
+ if (mutationTimer) clearTimeout(mutationTimer);
148
+ mutationTimer = setTimeout(() => {
149
+ if (active) clonePage();
150
+ }, 16);
151
+ });
152
+ function activate() {
153
+ if (active) return;
154
+ active = true;
155
+ scrollX = window.scrollX;
156
+ scrollY = window.scrollY;
157
+ zoomLevel = opts.zoomLevel;
158
+ clonePage();
159
+ host.classList.add("louper-active");
160
+ scheduleRender();
161
+ observer.observe(document.body, {
162
+ childList: true,
163
+ subtree: true,
164
+ characterData: true,
165
+ attributes: true,
166
+ attributeFilter: ["class", "style", "data-state"]
167
+ });
168
+ }
169
+ function deactivate() {
170
+ if (!active) return;
171
+ active = false;
172
+ observer.disconnect();
173
+ if (mutationTimer) {
174
+ clearTimeout(mutationTimer);
175
+ mutationTimer = null;
176
+ }
177
+ host.classList.remove("louper-active");
178
+ setTimeout(() => {
179
+ if (!active) content.innerHTML = "";
180
+ }, FADE_OUT);
181
+ }
182
+ function onKeyDown(e) {
183
+ if (e.key === opts.hotkey && !active) activate();
184
+ }
185
+ function onKeyUp(e) {
186
+ if (e.key === opts.hotkey) deactivate();
187
+ }
188
+ function onMouseMove(e) {
189
+ mouseX = e.clientX;
190
+ mouseY = e.clientY;
191
+ if (active) scheduleRender();
192
+ }
193
+ function onScroll() {
194
+ if (!active) return;
195
+ scrollX = window.scrollX;
196
+ scrollY = window.scrollY;
197
+ scheduleRender();
198
+ }
199
+ function onWheel(e) {
200
+ if (!active) return;
201
+ e.preventDefault();
202
+ zoomLevel = Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, zoomLevel * (1 - e.deltaY * ZOOM_SENSITIVITY)));
203
+ scheduleRender();
204
+ }
205
+ if (opts.hotkey) {
206
+ window.addEventListener("keydown", onKeyDown);
207
+ window.addEventListener("keyup", onKeyUp);
208
+ window.addEventListener("blur", deactivate);
209
+ }
210
+ window.addEventListener("mousemove", onMouseMove);
211
+ window.addEventListener("scroll", onScroll, { passive: true });
212
+ window.addEventListener("wheel", onWheel, { passive: false });
213
+ function destroy() {
214
+ active = false;
215
+ observer.disconnect();
216
+ if (mutationTimer) clearTimeout(mutationTimer);
217
+ if (rafId !== null) cancelAnimationFrame(rafId);
218
+ window.removeEventListener("keydown", onKeyDown);
219
+ window.removeEventListener("keyup", onKeyUp);
220
+ window.removeEventListener("mousemove", onMouseMove);
221
+ window.removeEventListener("blur", deactivate);
222
+ window.removeEventListener("scroll", onScroll);
223
+ window.removeEventListener("wheel", onWheel);
224
+ host.remove();
225
+ }
226
+ function update(newOpts) {
227
+ opts = { ...opts, ...newOpts };
228
+ if (newOpts.zoomLevel !== void 0) zoomLevel = newOpts.zoomLevel;
229
+ styleEl.textContent = css();
230
+ if (active) scheduleRender();
231
+ }
232
+ return { destroy, update, activate, deactivate };
233
+ }
234
+ // Annotate the CommonJS export names for ESM import in node:
235
+ 0 && (module.exports = {
236
+ DEFAULTS,
237
+ createLouper
238
+ });
239
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export interface LoupeOptions {\n /** Default: 2 */\n zoomLevel?: number\n /** Lens radius in px. Default: 150 */\n radius?: number\n /** Default: 3 */\n borderWidth?: number\n /** Default: \"white\" */\n borderColor?: string\n /** Default: \"Alt\". Set to false to disable. */\n hotkey?: 'Alt' | 'Control' | 'Shift' | 'Meta' | false\n}\n\nexport interface LoupeInstance {\n destroy: () => void\n update: (opts: Partial<LoupeOptions>) => void\n activate: () => void\n deactivate: () => void\n}\n\nexport const DEFAULTS: Required<LoupeOptions> = {\n zoomLevel: 2,\n radius: 150,\n borderWidth: 3,\n borderColor: 'white',\n hotkey: 'Alt',\n}\n\nconst MIN_ZOOM = 1\nconst MAX_ZOOM = 10\nconst ZOOM_SENSITIVITY = 0.002\nconst FADE_OUT = 100\n\nexport function createLouper(userOpts: LoupeOptions = {}): LoupeInstance {\n let opts = { ...DEFAULTS, ...userOpts }\n\n const host = document.createElement('louper-lens')\n const root = host.attachShadow({ mode: 'closed' })\n const styleEl = document.createElement('style')\n styleEl.textContent = css()\n root.appendChild(styleEl)\n const wrapper = document.createElement('div')\n wrapper.className = 'louper-clone-wrapper'\n const content = document.createElement('div')\n content.className = 'louper-clone-container'\n wrapper.appendChild(content)\n root.appendChild(wrapper)\n document.body.appendChild(host)\n\n let active = false\n let mouseX = 0, mouseY = 0, scrollX = 0, scrollY = 0\n let zoomLevel = opts.zoomLevel\n let rafId: number | null = null\n let pending = false\n let mutationTimer: ReturnType<typeof setTimeout> | null = null\n\n function css() {\n const d = opts.radius * 2\n return `\n :host {\n position: fixed; top: 0; left: 0;\n z-index: 2147483647;\n pointer-events: none;\n width: ${d}px; height: ${d}px;\n border-radius: 50%;\n border: ${opts.borderWidth}px solid ${opts.borderColor};\n box-shadow: 0 0 0 1px rgba(0,0,0,0.1);\n overflow: hidden; opacity: 0;\n transition: opacity ${FADE_OUT}ms ease-out;\n will-change: transform, opacity;\n }\n :host(.louper-active) { opacity: 1; transition: none; }\n .louper-clone-wrapper {\n position: absolute; top: 0; left: 0;\n width: 100%; height: 100%;\n overflow: hidden; border-radius: 50%;\n }\n .louper-clone-container {\n position: absolute; top: 0; left: 0;\n transform-origin: 0 0;\n }\n `\n }\n\n function render() {\n pending = false\n rafId = null\n const { radius, borderWidth } = opts\n host.style.transform = `translate(${mouseX - radius - borderWidth}px,${mouseY - radius - borderWidth}px)`\n const cx = radius - (mouseX + scrollX) * zoomLevel\n const cy = radius - (mouseY + scrollY) * zoomLevel\n content.style.transform = `scale(${zoomLevel}) translate(${cx / zoomLevel}px,${cy / zoomLevel}px)`\n }\n\n function scheduleRender() {\n if (!pending) {\n pending = true\n rafId = requestAnimationFrame(render)\n }\n }\n\n function clonePage() {\n const docEl = document.documentElement\n const copy = docEl.cloneNode(true) as HTMLElement\n copy.querySelectorAll('script').forEach(s => s.remove())\n copy.querySelectorAll('louper-lens').forEach(el => el.remove())\n\n root.querySelectorAll('[data-louper-sheet]').forEach(el => el.remove())\n for (const sheet of Array.from(document.styleSheets)) {\n try {\n if (sheet.href) {\n const link = document.createElement('link')\n link.rel = 'stylesheet'\n link.href = sheet.href\n link.setAttribute('data-louper-sheet', '')\n root.appendChild(link)\n } else if (sheet.ownerNode instanceof HTMLStyleElement) {\n const s = document.createElement('style')\n s.textContent = sheet.ownerNode.textContent\n s.setAttribute('data-louper-sheet', '')\n root.appendChild(s)\n }\n } catch {} // cross-origin\n }\n\n const srcCanvases = docEl.querySelectorAll('canvas')\n const dstCanvases = copy.querySelectorAll('canvas')\n srcCanvases.forEach((src, i) => {\n const dst = dstCanvases[i]\n if (!dst) return\n dst.width = src.width\n dst.height = src.height\n const ctx = dst.getContext('2d')\n if (ctx) try { ctx.drawImage(src, 0, 0) } catch {} // tainted\n })\n\n copy.style.margin = '0'\n copy.style.position = 'absolute'\n copy.style.top = '0'\n copy.style.left = '0'\n copy.style.width = docEl.scrollWidth + 'px'\n copy.style.height = docEl.scrollHeight + 'px'\n content.innerHTML = ''\n content.appendChild(copy)\n }\n\n const observer = new MutationObserver(() => {\n if (!active) return\n if (mutationTimer) clearTimeout(mutationTimer)\n mutationTimer = setTimeout(() => { if (active) clonePage() }, 16)\n })\n\n function activate() {\n if (active) return\n active = true\n scrollX = window.scrollX\n scrollY = window.scrollY\n zoomLevel = opts.zoomLevel\n clonePage()\n host.classList.add('louper-active')\n scheduleRender()\n observer.observe(document.body, {\n childList: true, subtree: true, characterData: true,\n attributes: true, attributeFilter: ['class', 'style', 'data-state'],\n })\n }\n\n function deactivate() {\n if (!active) return\n active = false\n observer.disconnect()\n if (mutationTimer) { clearTimeout(mutationTimer); mutationTimer = null }\n host.classList.remove('louper-active')\n setTimeout(() => { if (!active) content.innerHTML = '' }, FADE_OUT)\n }\n\n function onKeyDown(e: KeyboardEvent) { if (e.key === opts.hotkey && !active) activate() }\n function onKeyUp(e: KeyboardEvent) { if (e.key === opts.hotkey) deactivate() }\n function onMouseMove(e: MouseEvent) {\n mouseX = e.clientX\n mouseY = e.clientY\n if (active) scheduleRender()\n }\n function onScroll() {\n if (!active) return\n scrollX = window.scrollX\n scrollY = window.scrollY\n scheduleRender()\n }\n function onWheel(e: WheelEvent) {\n if (!active) return\n e.preventDefault()\n zoomLevel = Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, zoomLevel * (1 - e.deltaY * ZOOM_SENSITIVITY)))\n scheduleRender()\n }\n\n if (opts.hotkey) {\n window.addEventListener('keydown', onKeyDown)\n window.addEventListener('keyup', onKeyUp)\n window.addEventListener('blur', deactivate)\n }\n window.addEventListener('mousemove', onMouseMove)\n window.addEventListener('scroll', onScroll, { passive: true })\n window.addEventListener('wheel', onWheel, { passive: false })\n\n function destroy() {\n active = false\n observer.disconnect()\n if (mutationTimer) clearTimeout(mutationTimer)\n if (rafId !== null) cancelAnimationFrame(rafId)\n window.removeEventListener('keydown', onKeyDown)\n window.removeEventListener('keyup', onKeyUp)\n window.removeEventListener('mousemove', onMouseMove)\n window.removeEventListener('blur', deactivate)\n window.removeEventListener('scroll', onScroll)\n window.removeEventListener('wheel', onWheel)\n host.remove()\n }\n\n function update(newOpts: Partial<LoupeOptions>) {\n opts = { ...opts, ...newOpts }\n if (newOpts.zoomLevel !== undefined) zoomLevel = newOpts.zoomLevel\n styleEl.textContent = css()\n if (active) scheduleRender()\n }\n\n return { destroy, update, activate, deactivate }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,22 @@
1
+ interface LoupeOptions {
2
+ /** Default: 2 */
3
+ zoomLevel?: number;
4
+ /** Lens radius in px. Default: 150 */
5
+ radius?: number;
6
+ /** Default: 3 */
7
+ borderWidth?: number;
8
+ /** Default: "white" */
9
+ borderColor?: string;
10
+ /** Default: "Alt". Set to false to disable. */
11
+ hotkey?: 'Alt' | 'Control' | 'Shift' | 'Meta' | false;
12
+ }
13
+ interface LoupeInstance {
14
+ destroy: () => void;
15
+ update: (opts: Partial<LoupeOptions>) => void;
16
+ activate: () => void;
17
+ deactivate: () => void;
18
+ }
19
+ declare const DEFAULTS: Required<LoupeOptions>;
20
+ declare function createLouper(userOpts?: LoupeOptions): LoupeInstance;
21
+
22
+ export { DEFAULTS, type LoupeInstance, type LoupeOptions, createLouper };
@@ -0,0 +1,22 @@
1
+ interface LoupeOptions {
2
+ /** Default: 2 */
3
+ zoomLevel?: number;
4
+ /** Lens radius in px. Default: 150 */
5
+ radius?: number;
6
+ /** Default: 3 */
7
+ borderWidth?: number;
8
+ /** Default: "white" */
9
+ borderColor?: string;
10
+ /** Default: "Alt". Set to false to disable. */
11
+ hotkey?: 'Alt' | 'Control' | 'Shift' | 'Meta' | false;
12
+ }
13
+ interface LoupeInstance {
14
+ destroy: () => void;
15
+ update: (opts: Partial<LoupeOptions>) => void;
16
+ activate: () => void;
17
+ deactivate: () => void;
18
+ }
19
+ declare const DEFAULTS: Required<LoupeOptions>;
20
+ declare function createLouper(userOpts?: LoupeOptions): LoupeInstance;
21
+
22
+ export { DEFAULTS, type LoupeInstance, type LoupeOptions, createLouper };
package/dist/index.js ADDED
@@ -0,0 +1,213 @@
1
+ // src/index.ts
2
+ var DEFAULTS = {
3
+ zoomLevel: 2,
4
+ radius: 150,
5
+ borderWidth: 3,
6
+ borderColor: "white",
7
+ hotkey: "Alt"
8
+ };
9
+ var MIN_ZOOM = 1;
10
+ var MAX_ZOOM = 10;
11
+ var ZOOM_SENSITIVITY = 2e-3;
12
+ var FADE_OUT = 100;
13
+ function createLouper(userOpts = {}) {
14
+ let opts = { ...DEFAULTS, ...userOpts };
15
+ const host = document.createElement("louper-lens");
16
+ const root = host.attachShadow({ mode: "closed" });
17
+ const styleEl = document.createElement("style");
18
+ styleEl.textContent = css();
19
+ root.appendChild(styleEl);
20
+ const wrapper = document.createElement("div");
21
+ wrapper.className = "louper-clone-wrapper";
22
+ const content = document.createElement("div");
23
+ content.className = "louper-clone-container";
24
+ wrapper.appendChild(content);
25
+ root.appendChild(wrapper);
26
+ document.body.appendChild(host);
27
+ let active = false;
28
+ let mouseX = 0, mouseY = 0, scrollX = 0, scrollY = 0;
29
+ let zoomLevel = opts.zoomLevel;
30
+ let rafId = null;
31
+ let pending = false;
32
+ let mutationTimer = null;
33
+ function css() {
34
+ const d = opts.radius * 2;
35
+ return `
36
+ :host {
37
+ position: fixed; top: 0; left: 0;
38
+ z-index: 2147483647;
39
+ pointer-events: none;
40
+ width: ${d}px; height: ${d}px;
41
+ border-radius: 50%;
42
+ border: ${opts.borderWidth}px solid ${opts.borderColor};
43
+ box-shadow: 0 0 0 1px rgba(0,0,0,0.1);
44
+ overflow: hidden; opacity: 0;
45
+ transition: opacity ${FADE_OUT}ms ease-out;
46
+ will-change: transform, opacity;
47
+ }
48
+ :host(.louper-active) { opacity: 1; transition: none; }
49
+ .louper-clone-wrapper {
50
+ position: absolute; top: 0; left: 0;
51
+ width: 100%; height: 100%;
52
+ overflow: hidden; border-radius: 50%;
53
+ }
54
+ .louper-clone-container {
55
+ position: absolute; top: 0; left: 0;
56
+ transform-origin: 0 0;
57
+ }
58
+ `;
59
+ }
60
+ function render() {
61
+ pending = false;
62
+ rafId = null;
63
+ const { radius, borderWidth } = opts;
64
+ host.style.transform = `translate(${mouseX - radius - borderWidth}px,${mouseY - radius - borderWidth}px)`;
65
+ const cx = radius - (mouseX + scrollX) * zoomLevel;
66
+ const cy = radius - (mouseY + scrollY) * zoomLevel;
67
+ content.style.transform = `scale(${zoomLevel}) translate(${cx / zoomLevel}px,${cy / zoomLevel}px)`;
68
+ }
69
+ function scheduleRender() {
70
+ if (!pending) {
71
+ pending = true;
72
+ rafId = requestAnimationFrame(render);
73
+ }
74
+ }
75
+ function clonePage() {
76
+ const docEl = document.documentElement;
77
+ const copy = docEl.cloneNode(true);
78
+ copy.querySelectorAll("script").forEach((s) => s.remove());
79
+ copy.querySelectorAll("louper-lens").forEach((el) => el.remove());
80
+ root.querySelectorAll("[data-louper-sheet]").forEach((el) => el.remove());
81
+ for (const sheet of Array.from(document.styleSheets)) {
82
+ try {
83
+ if (sheet.href) {
84
+ const link = document.createElement("link");
85
+ link.rel = "stylesheet";
86
+ link.href = sheet.href;
87
+ link.setAttribute("data-louper-sheet", "");
88
+ root.appendChild(link);
89
+ } else if (sheet.ownerNode instanceof HTMLStyleElement) {
90
+ const s = document.createElement("style");
91
+ s.textContent = sheet.ownerNode.textContent;
92
+ s.setAttribute("data-louper-sheet", "");
93
+ root.appendChild(s);
94
+ }
95
+ } catch {
96
+ }
97
+ }
98
+ const srcCanvases = docEl.querySelectorAll("canvas");
99
+ const dstCanvases = copy.querySelectorAll("canvas");
100
+ srcCanvases.forEach((src, i) => {
101
+ const dst = dstCanvases[i];
102
+ if (!dst) return;
103
+ dst.width = src.width;
104
+ dst.height = src.height;
105
+ const ctx = dst.getContext("2d");
106
+ if (ctx) try {
107
+ ctx.drawImage(src, 0, 0);
108
+ } catch {
109
+ }
110
+ });
111
+ copy.style.margin = "0";
112
+ copy.style.position = "absolute";
113
+ copy.style.top = "0";
114
+ copy.style.left = "0";
115
+ copy.style.width = docEl.scrollWidth + "px";
116
+ copy.style.height = docEl.scrollHeight + "px";
117
+ content.innerHTML = "";
118
+ content.appendChild(copy);
119
+ }
120
+ const observer = new MutationObserver(() => {
121
+ if (!active) return;
122
+ if (mutationTimer) clearTimeout(mutationTimer);
123
+ mutationTimer = setTimeout(() => {
124
+ if (active) clonePage();
125
+ }, 16);
126
+ });
127
+ function activate() {
128
+ if (active) return;
129
+ active = true;
130
+ scrollX = window.scrollX;
131
+ scrollY = window.scrollY;
132
+ zoomLevel = opts.zoomLevel;
133
+ clonePage();
134
+ host.classList.add("louper-active");
135
+ scheduleRender();
136
+ observer.observe(document.body, {
137
+ childList: true,
138
+ subtree: true,
139
+ characterData: true,
140
+ attributes: true,
141
+ attributeFilter: ["class", "style", "data-state"]
142
+ });
143
+ }
144
+ function deactivate() {
145
+ if (!active) return;
146
+ active = false;
147
+ observer.disconnect();
148
+ if (mutationTimer) {
149
+ clearTimeout(mutationTimer);
150
+ mutationTimer = null;
151
+ }
152
+ host.classList.remove("louper-active");
153
+ setTimeout(() => {
154
+ if (!active) content.innerHTML = "";
155
+ }, FADE_OUT);
156
+ }
157
+ function onKeyDown(e) {
158
+ if (e.key === opts.hotkey && !active) activate();
159
+ }
160
+ function onKeyUp(e) {
161
+ if (e.key === opts.hotkey) deactivate();
162
+ }
163
+ function onMouseMove(e) {
164
+ mouseX = e.clientX;
165
+ mouseY = e.clientY;
166
+ if (active) scheduleRender();
167
+ }
168
+ function onScroll() {
169
+ if (!active) return;
170
+ scrollX = window.scrollX;
171
+ scrollY = window.scrollY;
172
+ scheduleRender();
173
+ }
174
+ function onWheel(e) {
175
+ if (!active) return;
176
+ e.preventDefault();
177
+ zoomLevel = Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, zoomLevel * (1 - e.deltaY * ZOOM_SENSITIVITY)));
178
+ scheduleRender();
179
+ }
180
+ if (opts.hotkey) {
181
+ window.addEventListener("keydown", onKeyDown);
182
+ window.addEventListener("keyup", onKeyUp);
183
+ window.addEventListener("blur", deactivate);
184
+ }
185
+ window.addEventListener("mousemove", onMouseMove);
186
+ window.addEventListener("scroll", onScroll, { passive: true });
187
+ window.addEventListener("wheel", onWheel, { passive: false });
188
+ function destroy() {
189
+ active = false;
190
+ observer.disconnect();
191
+ if (mutationTimer) clearTimeout(mutationTimer);
192
+ if (rafId !== null) cancelAnimationFrame(rafId);
193
+ window.removeEventListener("keydown", onKeyDown);
194
+ window.removeEventListener("keyup", onKeyUp);
195
+ window.removeEventListener("mousemove", onMouseMove);
196
+ window.removeEventListener("blur", deactivate);
197
+ window.removeEventListener("scroll", onScroll);
198
+ window.removeEventListener("wheel", onWheel);
199
+ host.remove();
200
+ }
201
+ function update(newOpts) {
202
+ opts = { ...opts, ...newOpts };
203
+ if (newOpts.zoomLevel !== void 0) zoomLevel = newOpts.zoomLevel;
204
+ styleEl.textContent = css();
205
+ if (active) scheduleRender();
206
+ }
207
+ return { destroy, update, activate, deactivate };
208
+ }
209
+ export {
210
+ DEFAULTS,
211
+ createLouper
212
+ };
213
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export interface LoupeOptions {\n /** Default: 2 */\n zoomLevel?: number\n /** Lens radius in px. Default: 150 */\n radius?: number\n /** Default: 3 */\n borderWidth?: number\n /** Default: \"white\" */\n borderColor?: string\n /** Default: \"Alt\". Set to false to disable. */\n hotkey?: 'Alt' | 'Control' | 'Shift' | 'Meta' | false\n}\n\nexport interface LoupeInstance {\n destroy: () => void\n update: (opts: Partial<LoupeOptions>) => void\n activate: () => void\n deactivate: () => void\n}\n\nexport const DEFAULTS: Required<LoupeOptions> = {\n zoomLevel: 2,\n radius: 150,\n borderWidth: 3,\n borderColor: 'white',\n hotkey: 'Alt',\n}\n\nconst MIN_ZOOM = 1\nconst MAX_ZOOM = 10\nconst ZOOM_SENSITIVITY = 0.002\nconst FADE_OUT = 100\n\nexport function createLouper(userOpts: LoupeOptions = {}): LoupeInstance {\n let opts = { ...DEFAULTS, ...userOpts }\n\n const host = document.createElement('louper-lens')\n const root = host.attachShadow({ mode: 'closed' })\n const styleEl = document.createElement('style')\n styleEl.textContent = css()\n root.appendChild(styleEl)\n const wrapper = document.createElement('div')\n wrapper.className = 'louper-clone-wrapper'\n const content = document.createElement('div')\n content.className = 'louper-clone-container'\n wrapper.appendChild(content)\n root.appendChild(wrapper)\n document.body.appendChild(host)\n\n let active = false\n let mouseX = 0, mouseY = 0, scrollX = 0, scrollY = 0\n let zoomLevel = opts.zoomLevel\n let rafId: number | null = null\n let pending = false\n let mutationTimer: ReturnType<typeof setTimeout> | null = null\n\n function css() {\n const d = opts.radius * 2\n return `\n :host {\n position: fixed; top: 0; left: 0;\n z-index: 2147483647;\n pointer-events: none;\n width: ${d}px; height: ${d}px;\n border-radius: 50%;\n border: ${opts.borderWidth}px solid ${opts.borderColor};\n box-shadow: 0 0 0 1px rgba(0,0,0,0.1);\n overflow: hidden; opacity: 0;\n transition: opacity ${FADE_OUT}ms ease-out;\n will-change: transform, opacity;\n }\n :host(.louper-active) { opacity: 1; transition: none; }\n .louper-clone-wrapper {\n position: absolute; top: 0; left: 0;\n width: 100%; height: 100%;\n overflow: hidden; border-radius: 50%;\n }\n .louper-clone-container {\n position: absolute; top: 0; left: 0;\n transform-origin: 0 0;\n }\n `\n }\n\n function render() {\n pending = false\n rafId = null\n const { radius, borderWidth } = opts\n host.style.transform = `translate(${mouseX - radius - borderWidth}px,${mouseY - radius - borderWidth}px)`\n const cx = radius - (mouseX + scrollX) * zoomLevel\n const cy = radius - (mouseY + scrollY) * zoomLevel\n content.style.transform = `scale(${zoomLevel}) translate(${cx / zoomLevel}px,${cy / zoomLevel}px)`\n }\n\n function scheduleRender() {\n if (!pending) {\n pending = true\n rafId = requestAnimationFrame(render)\n }\n }\n\n function clonePage() {\n const docEl = document.documentElement\n const copy = docEl.cloneNode(true) as HTMLElement\n copy.querySelectorAll('script').forEach(s => s.remove())\n copy.querySelectorAll('louper-lens').forEach(el => el.remove())\n\n root.querySelectorAll('[data-louper-sheet]').forEach(el => el.remove())\n for (const sheet of Array.from(document.styleSheets)) {\n try {\n if (sheet.href) {\n const link = document.createElement('link')\n link.rel = 'stylesheet'\n link.href = sheet.href\n link.setAttribute('data-louper-sheet', '')\n root.appendChild(link)\n } else if (sheet.ownerNode instanceof HTMLStyleElement) {\n const s = document.createElement('style')\n s.textContent = sheet.ownerNode.textContent\n s.setAttribute('data-louper-sheet', '')\n root.appendChild(s)\n }\n } catch {} // cross-origin\n }\n\n const srcCanvases = docEl.querySelectorAll('canvas')\n const dstCanvases = copy.querySelectorAll('canvas')\n srcCanvases.forEach((src, i) => {\n const dst = dstCanvases[i]\n if (!dst) return\n dst.width = src.width\n dst.height = src.height\n const ctx = dst.getContext('2d')\n if (ctx) try { ctx.drawImage(src, 0, 0) } catch {} // tainted\n })\n\n copy.style.margin = '0'\n copy.style.position = 'absolute'\n copy.style.top = '0'\n copy.style.left = '0'\n copy.style.width = docEl.scrollWidth + 'px'\n copy.style.height = docEl.scrollHeight + 'px'\n content.innerHTML = ''\n content.appendChild(copy)\n }\n\n const observer = new MutationObserver(() => {\n if (!active) return\n if (mutationTimer) clearTimeout(mutationTimer)\n mutationTimer = setTimeout(() => { if (active) clonePage() }, 16)\n })\n\n function activate() {\n if (active) return\n active = true\n scrollX = window.scrollX\n scrollY = window.scrollY\n zoomLevel = opts.zoomLevel\n clonePage()\n host.classList.add('louper-active')\n scheduleRender()\n observer.observe(document.body, {\n childList: true, subtree: true, characterData: true,\n attributes: true, attributeFilter: ['class', 'style', 'data-state'],\n })\n }\n\n function deactivate() {\n if (!active) return\n active = false\n observer.disconnect()\n if (mutationTimer) { clearTimeout(mutationTimer); mutationTimer = null }\n host.classList.remove('louper-active')\n setTimeout(() => { if (!active) content.innerHTML = '' }, FADE_OUT)\n }\n\n function onKeyDown(e: KeyboardEvent) { if (e.key === opts.hotkey && !active) activate() }\n function onKeyUp(e: KeyboardEvent) { if (e.key === opts.hotkey) deactivate() }\n function onMouseMove(e: MouseEvent) {\n mouseX = e.clientX\n mouseY = e.clientY\n if (active) scheduleRender()\n }\n function onScroll() {\n if (!active) return\n scrollX = window.scrollX\n scrollY = window.scrollY\n scheduleRender()\n }\n function onWheel(e: WheelEvent) {\n if (!active) return\n e.preventDefault()\n zoomLevel = Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, zoomLevel * (1 - e.deltaY * ZOOM_SENSITIVITY)))\n scheduleRender()\n }\n\n if (opts.hotkey) {\n window.addEventListener('keydown', onKeyDown)\n window.addEventListener('keyup', onKeyUp)\n window.addEventListener('blur', deactivate)\n }\n window.addEventListener('mousemove', onMouseMove)\n window.addEventListener('scroll', onScroll, { passive: true })\n window.addEventListener('wheel', onWheel, { passive: false })\n\n function destroy() {\n active = false\n observer.disconnect()\n if (mutationTimer) clearTimeout(mutationTimer)\n if (rafId !== null) cancelAnimationFrame(rafId)\n window.removeEventListener('keydown', onKeyDown)\n window.removeEventListener('keyup', onKeyUp)\n window.removeEventListener('mousemove', onMouseMove)\n window.removeEventListener('blur', deactivate)\n window.removeEventListener('scroll', onScroll)\n window.removeEventListener('wheel', onWheel)\n host.remove()\n }\n\n function update(newOpts: Partial<LoupeOptions>) {\n opts = { ...opts, ...newOpts }\n if (newOpts.zoomLevel !== undefined) zoomLevel = newOpts.zoomLevel\n styleEl.textContent = css()\n if (active) scheduleRender()\n }\n\n return { destroy, update, activate, deactivate }\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "louper",
3
+ "version": "0.1.0",
4
+ "description": "A lightweight zoom circle dev tool. Hold Alt/Option to magnify any part of your page.",
5
+ "license": "MIT",
6
+ "author": "Will Garman",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/willgarman/louper"
10
+ },
11
+ "keywords": [
12
+ "zoom",
13
+ "magnifier",
14
+ "loupe",
15
+ "devtools",
16
+ "inspect",
17
+ "ui"
18
+ ],
19
+ "type": "module",
20
+ "main": "./dist/index.cjs",
21
+ "module": "./dist/index.js",
22
+ "types": "./dist/index.d.ts",
23
+ "exports": {
24
+ ".": {
25
+ "import": {
26
+ "types": "./dist/index.d.ts",
27
+ "default": "./dist/index.js"
28
+ },
29
+ "require": {
30
+ "types": "./dist/index.d.cts",
31
+ "default": "./dist/index.cjs"
32
+ }
33
+ },
34
+ "./auto": "./dist/auto.global.js"
35
+ },
36
+ "sideEffects": [
37
+ "./dist/auto.global.js"
38
+ ],
39
+ "files": [
40
+ "dist"
41
+ ],
42
+ "scripts": {
43
+ "build": "tsup",
44
+ "dev": "tsup --watch",
45
+ "test": "vitest run",
46
+ "test:watch": "vitest"
47
+ },
48
+ "devDependencies": {
49
+ "tsup": "^8.0.0",
50
+ "typescript": "^5.4.0",
51
+ "vitest": "^2.0.0",
52
+ "jsdom": "^25.0.0"
53
+ }
54
+ }