@pie-players/pie-tool-magnifier 0.1.1
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/Magnifier.svelte +340 -0
- package/README.md +135 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/tool-magnifier.js +3064 -0
- package/dist/tool-magnifier.js.map +1 -0
- package/index.ts +8 -0
- package/package.json +64 -0
- package/tool-magnifier.svelte +140 -0
package/Magnifier.svelte
ADDED
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
<!-- Magnifier.svelte - Modern Svelte 5 implementation of HTML magnifier -->
|
|
2
|
+
<script lang="ts">
|
|
3
|
+
import { onMount, untrack } from 'svelte';
|
|
4
|
+
|
|
5
|
+
interface MagnifierProps {
|
|
6
|
+
zoom?: number;
|
|
7
|
+
width?: number;
|
|
8
|
+
height?: number;
|
|
9
|
+
shape?: 'square' | 'circle';
|
|
10
|
+
visible?: boolean;
|
|
11
|
+
onPrepareContent?: (clonedContent: HTMLElement) => void;
|
|
12
|
+
onContentUpdated?: (bodyElement: HTMLElement) => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
zoom = 2,
|
|
17
|
+
width = 420,
|
|
18
|
+
height = 280,
|
|
19
|
+
shape = 'square',
|
|
20
|
+
visible = $bindable(false),
|
|
21
|
+
onPrepareContent,
|
|
22
|
+
onContentUpdated
|
|
23
|
+
}: MagnifierProps = $props();
|
|
24
|
+
|
|
25
|
+
// Reactive state
|
|
26
|
+
let magnifierEl = $state<HTMLDivElement | null>(null);
|
|
27
|
+
let contentEl = $state<HTMLDivElement | null>(null);
|
|
28
|
+
let clonedBody = $state<HTMLElement | null>(null);
|
|
29
|
+
let position = $state({ x: 200, y: 200 });
|
|
30
|
+
let isDragging = $state(false);
|
|
31
|
+
let dragOffset = $state({ x: 0, y: 0 });
|
|
32
|
+
|
|
33
|
+
// MutationObserver to sync content changes
|
|
34
|
+
let observer: MutationObserver | null = null;
|
|
35
|
+
let syncTimeoutHandle: number | null = null;
|
|
36
|
+
|
|
37
|
+
// Derived styles
|
|
38
|
+
let magnifierStyle = $derived(`
|
|
39
|
+
left: ${position.x}px;
|
|
40
|
+
top: ${position.y}px;
|
|
41
|
+
width: ${width}px;
|
|
42
|
+
height: ${height}px;
|
|
43
|
+
${shape === 'circle' ? 'border-radius: 50%;' : ''}
|
|
44
|
+
display: ${visible ? 'block' : 'none'};
|
|
45
|
+
`);
|
|
46
|
+
|
|
47
|
+
let contentStyle = $derived(`
|
|
48
|
+
transform: scale(${zoom});
|
|
49
|
+
left: ${-position.x * zoom - window.scrollX * zoom}px;
|
|
50
|
+
top: ${-position.y * zoom - window.scrollY * zoom}px;
|
|
51
|
+
width: ${document.body.scrollWidth}px;
|
|
52
|
+
height: ${document.body.scrollHeight}px;
|
|
53
|
+
`);
|
|
54
|
+
|
|
55
|
+
function cloneDocumentBody() {
|
|
56
|
+
const bodyOriginal = document.body;
|
|
57
|
+
const bodyCopy = bodyOriginal.cloneNode(true) as HTMLElement;
|
|
58
|
+
|
|
59
|
+
// Copy background color
|
|
60
|
+
const bgColor = getComputedStyle(bodyOriginal).backgroundColor;
|
|
61
|
+
if (bgColor && magnifierEl) {
|
|
62
|
+
magnifierEl.style.backgroundColor = bgColor;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Reset styles
|
|
66
|
+
bodyCopy.style.cursor = 'auto';
|
|
67
|
+
bodyCopy.style.paddingTop = '0px';
|
|
68
|
+
bodyCopy.setAttribute('unselectable', 'on');
|
|
69
|
+
|
|
70
|
+
// Copy canvas contents
|
|
71
|
+
const canvasOriginal = bodyOriginal.querySelectorAll('canvas');
|
|
72
|
+
const canvasCopy = bodyCopy.querySelectorAll('canvas');
|
|
73
|
+
if (canvasOriginal.length > 0 && canvasOriginal.length === canvasCopy.length) {
|
|
74
|
+
for (let i = 0; i < canvasOriginal.length; i++) {
|
|
75
|
+
const ctx = canvasCopy[i].getContext('2d');
|
|
76
|
+
if (ctx) {
|
|
77
|
+
try {
|
|
78
|
+
ctx.drawImage(canvasOriginal[i], 0, 0);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
// Silent fail - likely CORS issue
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Remove elements that shouldn't be magnified
|
|
87
|
+
const elementsToRemove = [
|
|
88
|
+
...bodyCopy.querySelectorAll('script'),
|
|
89
|
+
...bodyCopy.querySelectorAll('audio'),
|
|
90
|
+
...bodyCopy.querySelectorAll('video'),
|
|
91
|
+
...bodyCopy.querySelectorAll('.magnifier'),
|
|
92
|
+
...bodyCopy.querySelectorAll('.magnifier-clone-ignore'),
|
|
93
|
+
...bodyCopy.querySelectorAll('[data-magnifier-ignore]')
|
|
94
|
+
];
|
|
95
|
+
elementsToRemove.forEach(el => el.remove());
|
|
96
|
+
|
|
97
|
+
// Custom content preparation
|
|
98
|
+
if (onPrepareContent) {
|
|
99
|
+
onPrepareContent(bodyCopy);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return bodyCopy;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function updateContent() {
|
|
106
|
+
if (!visible || !contentEl) return;
|
|
107
|
+
|
|
108
|
+
// Clear existing content
|
|
109
|
+
contentEl.innerHTML = '';
|
|
110
|
+
|
|
111
|
+
// Clone and prepare content
|
|
112
|
+
clonedBody = cloneDocumentBody();
|
|
113
|
+
contentEl.appendChild(clonedBody);
|
|
114
|
+
|
|
115
|
+
// Sync scroll positions
|
|
116
|
+
syncScrollPositions();
|
|
117
|
+
|
|
118
|
+
// Notify content update
|
|
119
|
+
if (onContentUpdated && clonedBody) {
|
|
120
|
+
const bodyInClone = clonedBody.querySelector('body');
|
|
121
|
+
if (bodyInClone) {
|
|
122
|
+
onContentUpdated(bodyInClone as HTMLElement);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function syncScrollPositions() {
|
|
128
|
+
if (!clonedBody) return;
|
|
129
|
+
|
|
130
|
+
// Find all scrolled divs in the original document
|
|
131
|
+
const scrolledDivs = Array.from(document.querySelectorAll('div')).filter(
|
|
132
|
+
div => div.scrollTop > 0 && !magnifierEl?.contains(div)
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
// Sync scroll positions to cloned elements
|
|
136
|
+
scrolledDivs.forEach(div => {
|
|
137
|
+
const selectors: string[] = [];
|
|
138
|
+
|
|
139
|
+
if (div.id) {
|
|
140
|
+
selectors.push('#' + div.id);
|
|
141
|
+
}
|
|
142
|
+
if (div.className) {
|
|
143
|
+
selectors.push('.' + div.className.split(' ').join('.'));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
selectors.forEach(selector => {
|
|
147
|
+
const targets = clonedBody!.querySelectorAll(selector);
|
|
148
|
+
if (targets.length === 1) {
|
|
149
|
+
const target = targets[0] as HTMLElement;
|
|
150
|
+
target.scrollTop = div.scrollTop;
|
|
151
|
+
target.scrollLeft = div.scrollLeft;
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function queueContentUpdate() {
|
|
158
|
+
if (syncTimeoutHandle !== null) {
|
|
159
|
+
clearTimeout(syncTimeoutHandle);
|
|
160
|
+
}
|
|
161
|
+
syncTimeoutHandle = window.setTimeout(() => {
|
|
162
|
+
updateContent();
|
|
163
|
+
}, 100);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function startObserving() {
|
|
167
|
+
if (observer) return;
|
|
168
|
+
|
|
169
|
+
observer = new MutationObserver((mutations) => {
|
|
170
|
+
for (const mutation of mutations) {
|
|
171
|
+
// Skip mutations within the magnifier itself
|
|
172
|
+
if (magnifierEl && magnifierEl.contains(mutation.target as Node)) {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
queueContentUpdate();
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
observer.observe(document.body, {
|
|
181
|
+
childList: true,
|
|
182
|
+
subtree: true,
|
|
183
|
+
attributes: true,
|
|
184
|
+
attributeFilter: ['class', 'width', 'height', 'style'],
|
|
185
|
+
attributeOldValue: true
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function stopObserving() {
|
|
190
|
+
if (observer) {
|
|
191
|
+
observer.disconnect();
|
|
192
|
+
observer = null;
|
|
193
|
+
}
|
|
194
|
+
if (syncTimeoutHandle !== null) {
|
|
195
|
+
clearTimeout(syncTimeoutHandle);
|
|
196
|
+
syncTimeoutHandle = null;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Drag handlers
|
|
201
|
+
function handlePointerDown(e: PointerEvent) {
|
|
202
|
+
// Don't start drag on interactive elements or their children
|
|
203
|
+
const target = e.target as HTMLElement;
|
|
204
|
+
|
|
205
|
+
// Check if the target or any parent is an interactive element
|
|
206
|
+
const interactiveTags = ['INPUT', 'TEXTAREA', 'SELECT', 'BUTTON', 'A'];
|
|
207
|
+
let element: HTMLElement | null = target;
|
|
208
|
+
while (element && element !== magnifierEl) {
|
|
209
|
+
if (interactiveTags.includes(element.tagName.toUpperCase())) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
element = element.parentElement;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
isDragging = true;
|
|
216
|
+
dragOffset = {
|
|
217
|
+
x: e.clientX - position.x,
|
|
218
|
+
y: e.clientY - position.y
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
if (magnifierEl) {
|
|
222
|
+
magnifierEl.setPointerCapture(e.pointerId);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function handlePointerMove(e: PointerEvent) {
|
|
227
|
+
if (!isDragging) return;
|
|
228
|
+
|
|
229
|
+
position = {
|
|
230
|
+
x: e.clientX - dragOffset.x,
|
|
231
|
+
y: e.clientY - dragOffset.y
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function handlePointerUp(e: PointerEvent) {
|
|
236
|
+
if (isDragging && magnifierEl) {
|
|
237
|
+
magnifierEl.releasePointerCapture(e.pointerId);
|
|
238
|
+
}
|
|
239
|
+
isDragging = false;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Scroll sync handler
|
|
243
|
+
function handleScroll(e: Event) {
|
|
244
|
+
if (!visible) return;
|
|
245
|
+
|
|
246
|
+
const target = e.target as HTMLElement;
|
|
247
|
+
if (magnifierEl?.contains(target)) return;
|
|
248
|
+
|
|
249
|
+
// Queue a content update to sync scroll positions
|
|
250
|
+
queueContentUpdate();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Update content when becoming visible
|
|
254
|
+
$effect(() => {
|
|
255
|
+
if (visible) {
|
|
256
|
+
untrack(() => {
|
|
257
|
+
updateContent();
|
|
258
|
+
startObserving();
|
|
259
|
+
});
|
|
260
|
+
} else {
|
|
261
|
+
untrack(() => {
|
|
262
|
+
stopObserving();
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// Update content when zoom changes
|
|
268
|
+
$effect(() => {
|
|
269
|
+
// Track zoom dependency
|
|
270
|
+
zoom;
|
|
271
|
+
if (visible) {
|
|
272
|
+
untrack(() => {
|
|
273
|
+
updateContent();
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// Lifecycle
|
|
279
|
+
onMount(() => {
|
|
280
|
+
// Listen for scroll events
|
|
281
|
+
window.addEventListener('scroll', handleScroll, true);
|
|
282
|
+
window.addEventListener('resize', queueContentUpdate);
|
|
283
|
+
|
|
284
|
+
return () => {
|
|
285
|
+
stopObserving();
|
|
286
|
+
window.removeEventListener('scroll', handleScroll, true);
|
|
287
|
+
window.removeEventListener('resize', queueContentUpdate);
|
|
288
|
+
};
|
|
289
|
+
});
|
|
290
|
+
</script>
|
|
291
|
+
|
|
292
|
+
<div
|
|
293
|
+
bind:this={magnifierEl}
|
|
294
|
+
class="magnifier"
|
|
295
|
+
style={magnifierStyle}
|
|
296
|
+
onpointerdown={handlePointerDown}
|
|
297
|
+
onpointermove={handlePointerMove}
|
|
298
|
+
onpointerup={handlePointerUp}
|
|
299
|
+
role="region"
|
|
300
|
+
aria-label="Content magnifier"
|
|
301
|
+
>
|
|
302
|
+
<div bind:this={contentEl} class="magnifier-content" style={contentStyle}></div>
|
|
303
|
+
<div class="magnifier-glass"></div>
|
|
304
|
+
<slot />
|
|
305
|
+
</div>
|
|
306
|
+
|
|
307
|
+
<style>
|
|
308
|
+
.magnifier {
|
|
309
|
+
position: fixed;
|
|
310
|
+
overflow: hidden;
|
|
311
|
+
background-color: white;
|
|
312
|
+
border: 1px solid #555;
|
|
313
|
+
border-radius: 4px;
|
|
314
|
+
z-index: 10000;
|
|
315
|
+
cursor: move;
|
|
316
|
+
touch-action: none;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
.magnifier-content {
|
|
320
|
+
position: absolute;
|
|
321
|
+
top: 0;
|
|
322
|
+
left: 0;
|
|
323
|
+
overflow: visible;
|
|
324
|
+
display: block;
|
|
325
|
+
transform-origin: left top;
|
|
326
|
+
user-select: none;
|
|
327
|
+
pointer-events: none;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.magnifier-glass {
|
|
331
|
+
position: absolute;
|
|
332
|
+
top: 0;
|
|
333
|
+
left: 0;
|
|
334
|
+
width: 100%;
|
|
335
|
+
height: 100%;
|
|
336
|
+
opacity: 0;
|
|
337
|
+
background-color: transparent;
|
|
338
|
+
pointer-events: all;
|
|
339
|
+
}
|
|
340
|
+
</style>
|
package/README.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# PIE Magnifier Tool
|
|
2
|
+
|
|
3
|
+
A draggable magnification tool that allows students to zoom in on any content on the assessment page. This tool is an accessibility accommodation for students with visual impairments.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Multiple Zoom Levels**: 1.5x, 2x, and 3x magnification
|
|
8
|
+
- **Draggable**: Can be moved anywhere on the screen using mouse or keyboard
|
|
9
|
+
- **Content Cloning**: Creates a live clone of page content with accurate scroll positions
|
|
10
|
+
- **Keyboard Accessible**: Full keyboard navigation support
|
|
11
|
+
- **Zero DOM Mutation**: Does not modify the original content
|
|
12
|
+
|
|
13
|
+
## Architecture
|
|
14
|
+
|
|
15
|
+
This tool follows PIE's custom element (Web Component) architecture:
|
|
16
|
+
|
|
17
|
+
- Built with Svelte 5
|
|
18
|
+
- Registered as a custom element `<pie-tool-magnifier>`
|
|
19
|
+
- Integrates with PIE's ToolCoordinator for z-index management
|
|
20
|
+
- Uses Moveable.js for drag functionality
|
|
21
|
+
|
|
22
|
+
## Comparison to Production Implementations
|
|
23
|
+
|
|
24
|
+
### Similarities
|
|
25
|
+
|
|
26
|
+
- **Visual Design**: Same zoom levels (1.5x, 2x, 3x), button layout, and frame dimensions
|
|
27
|
+
- **Core Functionality**: DOM cloning, scroll position preservation, mouse tracking
|
|
28
|
+
- **UX Patterns**: Frozen view on mouse leave, continuous update on drag
|
|
29
|
+
|
|
30
|
+
### Differences
|
|
31
|
+
|
|
32
|
+
- **Framework**: Svelte 5 (with runes) instead of Vue 3 (Composition API)
|
|
33
|
+
- **Architecture**: Web Component (custom element) instead of Vue SFC
|
|
34
|
+
- **Dependencies**: Uses Moveable.js directly instead of VueUse composables
|
|
35
|
+
- **Coordination**: Integrates with PIE's ToolCoordinator for z-index management
|
|
36
|
+
- **Styling**: Uses PIE CSS variables for consistency
|
|
37
|
+
- **DOM Mutation**: Zero mutation approach (clones only, never modifies original)
|
|
38
|
+
|
|
39
|
+
### Key Architectural Improvements
|
|
40
|
+
|
|
41
|
+
1. **Web Component Standard**: Framework-agnostic, can be used in any context
|
|
42
|
+
2. **Modern Svelte 5**: Uses runes (`$state`, `$derived`, `$effect`) for reactive state
|
|
43
|
+
3. **Cleaner Separation**: Tool coordination is handled by external coordinator, not internal store
|
|
44
|
+
4. **Accessibility First**: ARIA labels, keyboard navigation, screen reader announcements
|
|
45
|
+
|
|
46
|
+
## Usage
|
|
47
|
+
|
|
48
|
+
### As a Custom Element
|
|
49
|
+
|
|
50
|
+
```html
|
|
51
|
+
<pie-tool-magnifier
|
|
52
|
+
visible="true"
|
|
53
|
+
tool-id="magnifier"
|
|
54
|
+
magnify-root-selector="body"
|
|
55
|
+
></pie-tool-magnifier>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### With Coordinator
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { ToolMagnifier } from '@pie-api-aws/pieoneer-tool-magnifier';
|
|
62
|
+
import { ToolCoordinator } from '$lib/assessment-toolkit';
|
|
63
|
+
|
|
64
|
+
const coordinator = new ToolCoordinator();
|
|
65
|
+
|
|
66
|
+
// The tool will auto-register when visible=true
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Props
|
|
70
|
+
|
|
71
|
+
- `visible` (Boolean): Show/hide the tool
|
|
72
|
+
- `toolId` (String): Unique identifier for this tool instance (default: 'magnifier')
|
|
73
|
+
- `coordinator` (Object): ToolCoordinator instance for z-index management
|
|
74
|
+
- `magnifyRootSelector` (String): CSS selector for root element to magnify (default: 'body')
|
|
75
|
+
|
|
76
|
+
## Keyboard Navigation
|
|
77
|
+
|
|
78
|
+
- **Arrow Keys**: Move the magnifier frame (10px steps)
|
|
79
|
+
- **1 / 2 / 3**: Set zoom level to 1.5x / 2x / 3x
|
|
80
|
+
- **+ / =**: Cycle to next zoom level
|
|
81
|
+
- **-**: Cycle to previous zoom level
|
|
82
|
+
|
|
83
|
+
## Accessibility Features
|
|
84
|
+
|
|
85
|
+
- Full keyboard navigation
|
|
86
|
+
- ARIA labels and roles
|
|
87
|
+
- Screen reader announcements for state changes
|
|
88
|
+
- Focus visible indicators
|
|
89
|
+
- High contrast focus rings
|
|
90
|
+
|
|
91
|
+
## Implementation Notes
|
|
92
|
+
|
|
93
|
+
### Content Cloning
|
|
94
|
+
|
|
95
|
+
The tool creates a deep clone of the content specified by `magnifyRootSelector` and:
|
|
96
|
+
|
|
97
|
+
1. Preserves scroll positions of all scrollable elements
|
|
98
|
+
2. Removes elements with `.magnifier-clone-ignore` class or `[data-magnifier-ignore]` attribute
|
|
99
|
+
3. Applies scaling and positioning transforms
|
|
100
|
+
4. Updates on scroll events (debounced)
|
|
101
|
+
|
|
102
|
+
### Performance
|
|
103
|
+
|
|
104
|
+
- Uses `requestAnimationFrame` for smooth updates
|
|
105
|
+
- Debounces scroll event handlers (100ms)
|
|
106
|
+
- Cleans up all listeners and RAF loops on unmount
|
|
107
|
+
- Only updates during drag or mouse-inside states
|
|
108
|
+
|
|
109
|
+
### Z-Index Management
|
|
110
|
+
|
|
111
|
+
The tool uses PIE's ZIndexLayer system:
|
|
112
|
+
|
|
113
|
+
- Tool frame: `ZIndexLayer.MODAL` (2002)
|
|
114
|
+
- Moveable controls: `ZIndexLayer.CONTROL` (2003)
|
|
115
|
+
|
|
116
|
+
### Preventing Self-Magnification
|
|
117
|
+
|
|
118
|
+
The tool adds `.magnifier-clone-ignore` class and `[data-magnifier-ignore]` attribute to its own frame to prevent recursive magnification.
|
|
119
|
+
|
|
120
|
+
## Browser Compatibility
|
|
121
|
+
|
|
122
|
+
- Chrome/Edge 90+
|
|
123
|
+
- Firefox 90+
|
|
124
|
+
- Safari 14+
|
|
125
|
+
|
|
126
|
+
Requires:
|
|
127
|
+
- Custom Elements (Web Components)
|
|
128
|
+
- CSS Transform
|
|
129
|
+
- RequestAnimationFrame
|
|
130
|
+
- DOMMatrix
|
|
131
|
+
|
|
132
|
+
## Related Documentation
|
|
133
|
+
|
|
134
|
+
- [PIE Tools Architecture](../../docs/tools-high-level-architecture.md)
|
|
135
|
+
- [ToolCoordinator](../../tools/tool-coordinator.ts)
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|