visbug-editor 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -312
- package/dist/visbug-editor.browser.js +13 -0
- package/dist/visbug-editor.browser.js.map +1 -0
- package/dist/visbug-editor.cjs.js +1 -1
- package/dist/visbug-editor.cjs.js.map +1 -0
- package/dist/visbug-editor.esm.js +1 -1
- package/dist/visbug-editor.esm.js.map +1 -0
- package/dist/visbug-editor.umd.js +1 -1
- package/dist/visbug-editor.umd.js.map +1 -0
- package/package.json +7 -8
- package/types/index.d.ts +6 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# visbug-editor
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Framework-agnostic visual editing library extracted from [VisBug](https://github.com/GoogleChromeLabs/ProjectVisBug).
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/visbug-editor)
|
|
6
6
|
[](https://opensource.org/licenses/Apache-2.0)
|
|
@@ -18,27 +18,11 @@ All operations include full undo/redo support with smart change batching.
|
|
|
18
18
|
|
|
19
19
|
## Features
|
|
20
20
|
|
|
21
|
-
**
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
|
|
27
|
-
**Built-in Components**
|
|
28
|
-
- Selection overlay with resize handles (8 grips: corners + edges)
|
|
29
|
-
- Hover indicators with element labels
|
|
30
|
-
- Visual feedback for all interactions
|
|
31
|
-
- Dynamic overlay updates when elements change
|
|
32
|
-
|
|
33
|
-
**History Management**
|
|
34
|
-
- Full undo/redo support
|
|
35
|
-
- Smart change batching and merging
|
|
36
|
-
- Event-based change notifications
|
|
37
|
-
|
|
38
|
-
**Framework Agnostic**
|
|
39
|
-
- Works with vanilla JS, React, Vue, Angular, etc.
|
|
40
|
-
- Shadow DOM or regular DOM mode
|
|
41
|
-
- TypeScript definitions included
|
|
21
|
+
- **Tools**: Position (drag/arrow keys), Text (contenteditable), Font (typography), Image (drag-drop replacement)
|
|
22
|
+
- **Components**: Selection overlay with resize handles, hover indicators, visual feedback
|
|
23
|
+
- **History**: Full undo/redo with smart change batching
|
|
24
|
+
- **Flexible**: Works with vanilla JS, React, Vue, Angular; Shadow DOM or regular DOM
|
|
25
|
+
- **TypeScript**: Complete type definitions included
|
|
42
26
|
|
|
43
27
|
## Installation
|
|
44
28
|
|
|
@@ -57,21 +41,21 @@ yarn add visbug-editor
|
|
|
57
41
|
### Basic Usage
|
|
58
42
|
|
|
59
43
|
```javascript
|
|
60
|
-
import { VisBugEditor } from
|
|
44
|
+
import { VisBugEditor } from "visbug-editor";
|
|
61
45
|
|
|
62
46
|
const editor = new VisBugEditor({
|
|
63
|
-
container: document.getElementById(
|
|
64
|
-
initialTool:
|
|
65
|
-
onToolChange: (tool) => console.log(
|
|
66
|
-
onSelectionChange: (elements) => console.log(
|
|
47
|
+
container: document.getElementById("editable-area"),
|
|
48
|
+
initialTool: "position",
|
|
49
|
+
onToolChange: (tool) => console.log("Tool changed:", tool),
|
|
50
|
+
onSelectionChange: (elements) => console.log("Selected:", elements),
|
|
67
51
|
onChange: ({ canUndo, canRedo }) => {
|
|
68
|
-
console.log(
|
|
69
|
-
}
|
|
52
|
+
console.log("Can undo:", canUndo, "Can redo:", canRedo);
|
|
53
|
+
},
|
|
70
54
|
});
|
|
71
55
|
|
|
72
56
|
// Switch tools
|
|
73
|
-
editor.activateTool(
|
|
74
|
-
editor.activateTool(
|
|
57
|
+
editor.activateTool("text");
|
|
58
|
+
editor.activateTool("font");
|
|
75
59
|
|
|
76
60
|
// Undo/Redo
|
|
77
61
|
editor.undo();
|
|
@@ -79,7 +63,7 @@ editor.redo();
|
|
|
79
63
|
|
|
80
64
|
// Get/Set content
|
|
81
65
|
const html = editor.getContent();
|
|
82
|
-
editor.setContent(
|
|
66
|
+
editor.setContent("<h1>New content</h1>");
|
|
83
67
|
|
|
84
68
|
// Clean up
|
|
85
69
|
editor.destroy();
|
|
@@ -90,15 +74,15 @@ editor.destroy();
|
|
|
90
74
|
Full TypeScript support with complete type definitions:
|
|
91
75
|
|
|
92
76
|
```typescript
|
|
93
|
-
import { VisBugEditor, VisBugEditorOptions } from
|
|
77
|
+
import { VisBugEditor, VisBugEditorOptions } from "visbug-editor";
|
|
94
78
|
|
|
95
79
|
const options: VisBugEditorOptions = {
|
|
96
|
-
container: document.getElementById(
|
|
97
|
-
mode:
|
|
98
|
-
initialTool:
|
|
80
|
+
container: document.getElementById("app")!,
|
|
81
|
+
mode: "shadowDOM",
|
|
82
|
+
initialTool: "position",
|
|
99
83
|
onToolChange: (tool: string) => {
|
|
100
|
-
console.log(
|
|
101
|
-
}
|
|
84
|
+
console.log("Tool:", tool);
|
|
85
|
+
},
|
|
102
86
|
};
|
|
103
87
|
|
|
104
88
|
const editor = new VisBugEditor(options);
|
|
@@ -106,24 +90,12 @@ const editor = new VisBugEditor(options);
|
|
|
106
90
|
|
|
107
91
|
## Container Concept
|
|
108
92
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
```html
|
|
112
|
-
<div id="my-container">
|
|
113
|
-
<!-- This h1 is editable -->
|
|
114
|
-
<h1>Editable Heading</h1>
|
|
115
|
-
|
|
116
|
-
<!-- This paragraph is editable -->
|
|
117
|
-
<p>You can edit this text</p>
|
|
118
|
-
|
|
119
|
-
<!-- This image is swappable -->
|
|
120
|
-
<img src="image.jpg" />
|
|
121
|
-
</div>
|
|
122
|
-
```
|
|
93
|
+
The container's **children** become editable, not the container itself:
|
|
123
94
|
|
|
124
95
|
```javascript
|
|
125
96
|
const editor = new VisBugEditor({
|
|
126
|
-
container: document.getElementById(
|
|
97
|
+
container: document.getElementById("my-container"),
|
|
98
|
+
// Children of my-container are now editable
|
|
127
99
|
});
|
|
128
100
|
```
|
|
129
101
|
|
|
@@ -135,53 +107,57 @@ The container acts as the "editing canvas" boundary.
|
|
|
135
107
|
|
|
136
108
|
```typescript
|
|
137
109
|
interface VisBugEditorOptions {
|
|
138
|
-
// Required
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
// Optional
|
|
142
|
-
mode?: 'shadowDOM' | 'div'; // Default: 'shadowDOM'
|
|
143
|
-
initialTool?: 'position' | 'text' | 'font'; // Default: 'position'
|
|
144
|
-
|
|
145
|
-
// Callbacks
|
|
110
|
+
container: HTMLElement; // Required: editing canvas
|
|
111
|
+
mode?: "inside"; // Where to append UI elements
|
|
112
|
+
initialTool?: "position" | "text" | "font";
|
|
146
113
|
onToolChange?: (tool: string) => void;
|
|
147
114
|
onSelectionChange?: (elements: HTMLElement[]) => void;
|
|
148
115
|
onChange?: (state: { canUndo: boolean; canRedo: boolean }) => void;
|
|
149
116
|
onImageUpload?: (file: File) => Promise<string>;
|
|
150
|
-
|
|
151
|
-
// Customization
|
|
152
117
|
styles?: Record<string, string>;
|
|
153
|
-
|
|
154
|
-
// Behavior
|
|
155
|
-
clearHistoryOnSetContent?: boolean; // Default: true
|
|
118
|
+
clearHistoryOnSetContent?: boolean;
|
|
156
119
|
}
|
|
157
120
|
```
|
|
158
121
|
|
|
159
|
-
###
|
|
122
|
+
### Mode Option
|
|
160
123
|
|
|
161
|
-
|
|
162
|
-
- `activateTool(toolName)` - Switch to a different tool ('position', 'text', 'font')
|
|
163
|
-
- `getCurrentTool()` - Get the currently active tool
|
|
124
|
+
The `mode` option controls where editor UI elements (labels, handles, overlays) are appended:
|
|
164
125
|
|
|
165
|
-
**
|
|
166
|
-
- `selectElement(element)` - Select a single element
|
|
167
|
-
- `selectElements(elements)` - Select multiple elements
|
|
168
|
-
- `getSelectedElements()` - Get currently selected elements
|
|
169
|
-
- `clearSelection()` - Clear current selection
|
|
126
|
+
**Undefined (default)** - Append to `document.body`
|
|
170
127
|
|
|
171
|
-
|
|
172
|
-
-
|
|
173
|
-
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
128
|
+
- UI overlays can extend beyond container boundaries
|
|
129
|
+
- Standard behavior for full-page editing
|
|
130
|
+
- Use when container might have `overflow: hidden` or positioning constraints
|
|
131
|
+
|
|
132
|
+
```javascript
|
|
133
|
+
const editor = new VisBugEditor({
|
|
134
|
+
container: document.getElementById("app"),
|
|
135
|
+
// mode undefined - UI appends to document.body
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**'inside'** - Append to the container element
|
|
140
|
+
|
|
141
|
+
- UI stays within container bounds
|
|
142
|
+
- Useful for isolated editing areas or embedded editors
|
|
143
|
+
- Good for multiple editors on the same page
|
|
144
|
+
|
|
145
|
+
```javascript
|
|
146
|
+
const editor = new VisBugEditor({
|
|
147
|
+
container: document.getElementById("app"),
|
|
148
|
+
mode: "inside", // UI appends to container
|
|
149
|
+
});
|
|
150
|
+
```
|
|
178
151
|
|
|
179
|
-
|
|
180
|
-
- `getContent()` - Get clean HTML without editor UI
|
|
181
|
-
- `setContent(html)` - Set content and optionally clear history
|
|
152
|
+
### Key Methods
|
|
182
153
|
|
|
183
|
-
|
|
184
|
-
|
|
154
|
+
| Method | Purpose |
|
|
155
|
+
| ---------------------------------------- | ---------------------------------------- |
|
|
156
|
+
| `activateTool(name)` | Switch tools: 'position', 'text', 'font' |
|
|
157
|
+
| `undo()` / `redo()` | Undo/redo changes |
|
|
158
|
+
| `getContent()` / `setContent(html)` | Get/set HTML |
|
|
159
|
+
| `selectElement(el)` / `clearSelection()` | Manage selection |
|
|
160
|
+
| `destroy()` | Cleanup |
|
|
185
161
|
|
|
186
162
|
## Tools
|
|
187
163
|
|
|
@@ -204,287 +180,86 @@ Click on any element to edit its text content inline using contenteditable. Pres
|
|
|
204
180
|
Typography controls with keyboard shortcuts:
|
|
205
181
|
|
|
206
182
|
**Font Size**
|
|
183
|
+
|
|
207
184
|
- `Cmd/Ctrl + Up` - Increase font size
|
|
208
185
|
- `Cmd/Ctrl + Down` - Decrease font size
|
|
209
186
|
|
|
210
187
|
**Letter Spacing (Kerning)**
|
|
188
|
+
|
|
211
189
|
- `Cmd/Ctrl + Shift + Up` - Increase letter spacing
|
|
212
190
|
- `Cmd/Ctrl + Shift + Down` - Decrease letter spacing
|
|
213
191
|
|
|
214
192
|
**Line Height (Leading)**
|
|
193
|
+
|
|
215
194
|
- `Alt + Up` - Increase line height
|
|
216
195
|
- `Alt + Down` - Decrease line height
|
|
217
196
|
|
|
218
197
|
**Font Weight**
|
|
198
|
+
|
|
219
199
|
- `Cmd/Ctrl + B` - Toggle bold
|
|
220
200
|
|
|
221
201
|
**Font Style**
|
|
202
|
+
|
|
222
203
|
- `Cmd/Ctrl + I` - Toggle italic
|
|
223
204
|
|
|
224
205
|
### Image Swap (Always Active)
|
|
225
206
|
|
|
226
207
|
Drag and drop images onto any `<img>` tag or element with a background image to replace it. Supports:
|
|
208
|
+
|
|
227
209
|
- Direct image URL replacement
|
|
228
210
|
- Custom upload handler via `onImageUpload` callback
|
|
229
211
|
|
|
230
212
|
## Examples
|
|
231
213
|
|
|
232
|
-
### React
|
|
214
|
+
### React
|
|
233
215
|
|
|
234
216
|
```jsx
|
|
235
|
-
import { useEffect, useRef
|
|
236
|
-
import { VisBugEditor } from
|
|
217
|
+
import { useEffect, useRef } from "react";
|
|
218
|
+
import { VisBugEditor } from "visbug-editor";
|
|
237
219
|
|
|
238
|
-
function Editor() {
|
|
220
|
+
export default function Editor() {
|
|
239
221
|
const containerRef = useRef(null);
|
|
240
222
|
const editorRef = useRef(null);
|
|
241
|
-
const [canUndo, setCanUndo] = useState(false);
|
|
242
|
-
const [canRedo, setCanRedo] = useState(false);
|
|
243
223
|
|
|
244
224
|
useEffect(() => {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
setCanUndo(canUndo);
|
|
250
|
-
setCanRedo(canRedo);
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
return () => {
|
|
256
|
-
editorRef.current?.destroy();
|
|
257
|
-
};
|
|
225
|
+
editorRef.current = new VisBugEditor({
|
|
226
|
+
container: containerRef.current,
|
|
227
|
+
});
|
|
228
|
+
return () => editorRef.current?.destroy();
|
|
258
229
|
}, []);
|
|
259
230
|
|
|
260
231
|
return (
|
|
261
|
-
|
|
262
|
-
<button onClick={() => editorRef.current?.undo()}
|
|
263
|
-
Undo
|
|
264
|
-
</button>
|
|
265
|
-
<button onClick={() => editorRef.current?.redo()} disabled={!canRedo}>
|
|
266
|
-
Redo
|
|
267
|
-
</button>
|
|
232
|
+
<>
|
|
233
|
+
<button onClick={() => editorRef.current?.undo()}>Undo</button>
|
|
268
234
|
<div ref={containerRef}>
|
|
269
|
-
<h1>Editable
|
|
235
|
+
<h1>Editable</h1>
|
|
270
236
|
</div>
|
|
271
|
-
|
|
237
|
+
</>
|
|
272
238
|
);
|
|
273
239
|
}
|
|
274
240
|
```
|
|
275
241
|
|
|
276
|
-
### Vue
|
|
242
|
+
### Vue
|
|
277
243
|
|
|
278
244
|
```vue
|
|
279
245
|
<template>
|
|
280
|
-
<
|
|
281
|
-
|
|
282
|
-
<button @click="redo" :disabled="!canRedo">Redo</button>
|
|
283
|
-
<div ref="container">
|
|
284
|
-
<h1>Editable Content</h1>
|
|
285
|
-
</div>
|
|
286
|
-
</div>
|
|
246
|
+
<button @click="editor?.undo()">Undo</button>
|
|
247
|
+
<div ref="container"><h1>Editable</h1></div>
|
|
287
248
|
</template>
|
|
288
249
|
|
|
289
250
|
<script setup>
|
|
290
|
-
import { ref, onMounted, onUnmounted } from
|
|
291
|
-
import { VisBugEditor } from
|
|
251
|
+
import { ref, onMounted, onUnmounted } from "vue";
|
|
252
|
+
import { VisBugEditor } from "visbug-editor";
|
|
292
253
|
|
|
293
254
|
const container = ref(null);
|
|
294
|
-
const canUndo = ref(false);
|
|
295
|
-
const canRedo = ref(false);
|
|
296
255
|
let editor = null;
|
|
297
256
|
|
|
298
257
|
onMounted(() => {
|
|
299
|
-
editor = new VisBugEditor({
|
|
300
|
-
container: container.value,
|
|
301
|
-
onChange: ({ canUndo: cu, canRedo: cr }) => {
|
|
302
|
-
canUndo.value = cu;
|
|
303
|
-
canRedo.value = cr;
|
|
304
|
-
}
|
|
305
|
-
});
|
|
258
|
+
editor = new VisBugEditor({ container: container.value });
|
|
306
259
|
});
|
|
307
260
|
|
|
308
261
|
onUnmounted(() => {
|
|
309
262
|
editor?.destroy();
|
|
310
263
|
});
|
|
311
|
-
|
|
312
|
-
const undo = () => editor?.undo();
|
|
313
|
-
const redo = () => editor?.redo();
|
|
314
264
|
</script>
|
|
315
265
|
```
|
|
316
|
-
|
|
317
|
-
### Next.js Integration
|
|
318
|
-
|
|
319
|
-
```jsx
|
|
320
|
-
"use client";
|
|
321
|
-
|
|
322
|
-
import { useEffect, useRef, useState } from "react";
|
|
323
|
-
import { VisBugEditor } from "visbug-editor";
|
|
324
|
-
|
|
325
|
-
export default function EditableContent() {
|
|
326
|
-
const containerRef = useRef(null);
|
|
327
|
-
const editorRef = useRef(null);
|
|
328
|
-
const [activeTool, setActiveTool] = useState("position");
|
|
329
|
-
const [canUndo, setCanUndo] = useState(false);
|
|
330
|
-
const [canRedo, setCanRedo] = useState(false);
|
|
331
|
-
|
|
332
|
-
useEffect(() => {
|
|
333
|
-
if (containerRef.current && !editorRef.current) {
|
|
334
|
-
editorRef.current = new VisBugEditor({
|
|
335
|
-
container: containerRef.current,
|
|
336
|
-
mode: "shadowDOM",
|
|
337
|
-
initialTool: "position",
|
|
338
|
-
onToolChange: (tool) => setActiveTool(tool),
|
|
339
|
-
onChange: (state) => {
|
|
340
|
-
setCanUndo(state.canUndo);
|
|
341
|
-
setCanRedo(state.canRedo);
|
|
342
|
-
},
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
return () => {
|
|
347
|
-
editorRef.current?.destroy();
|
|
348
|
-
editorRef.current = null;
|
|
349
|
-
};
|
|
350
|
-
}, []);
|
|
351
|
-
|
|
352
|
-
return (
|
|
353
|
-
<div className="editor-wrapper">
|
|
354
|
-
<div className="toolbar">
|
|
355
|
-
<button onClick={() => editorRef.current?.activateTool("position")}>
|
|
356
|
-
Position
|
|
357
|
-
</button>
|
|
358
|
-
<button onClick={() => editorRef.current?.activateTool("text")}>
|
|
359
|
-
Text
|
|
360
|
-
</button>
|
|
361
|
-
<button onClick={() => editorRef.current?.activateTool("font")}>
|
|
362
|
-
Font
|
|
363
|
-
</button>
|
|
364
|
-
<button onClick={() => editorRef.current?.undo()} disabled={!canUndo}>
|
|
365
|
-
Undo
|
|
366
|
-
</button>
|
|
367
|
-
<button onClick={() => editorRef.current?.redo()} disabled={!canRedo}>
|
|
368
|
-
Redo
|
|
369
|
-
</button>
|
|
370
|
-
</div>
|
|
371
|
-
|
|
372
|
-
<div ref={containerRef} className="editable-area">
|
|
373
|
-
<h1>Edit Me!</h1>
|
|
374
|
-
<p>This content is editable</p>
|
|
375
|
-
</div>
|
|
376
|
-
</div>
|
|
377
|
-
);
|
|
378
|
-
}
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
### Custom Image Upload
|
|
382
|
-
|
|
383
|
-
```javascript
|
|
384
|
-
const editor = new VisBugEditor({
|
|
385
|
-
container: document.getElementById('app'),
|
|
386
|
-
onImageUpload: async (file) => {
|
|
387
|
-
// Upload to your server
|
|
388
|
-
const formData = new FormData();
|
|
389
|
-
formData.append('image', file);
|
|
390
|
-
|
|
391
|
-
const response = await fetch('/api/upload', {
|
|
392
|
-
method: 'POST',
|
|
393
|
-
body: formData
|
|
394
|
-
});
|
|
395
|
-
|
|
396
|
-
const { url } = await response.json();
|
|
397
|
-
return url; // Return the uploaded image URL
|
|
398
|
-
}
|
|
399
|
-
});
|
|
400
|
-
```
|
|
401
|
-
|
|
402
|
-
## Shadow DOM vs Div Mode
|
|
403
|
-
|
|
404
|
-
### Shadow DOM (Recommended)
|
|
405
|
-
|
|
406
|
-
**Pros:**
|
|
407
|
-
- Style isolation between editor and content
|
|
408
|
-
- DOM encapsulation
|
|
409
|
-
- No style conflicts
|
|
410
|
-
|
|
411
|
-
**Cons:**
|
|
412
|
-
- Not supported in very old browsers
|
|
413
|
-
- Slight complexity in event handling
|
|
414
|
-
|
|
415
|
-
### Div Mode (Fallback)
|
|
416
|
-
|
|
417
|
-
**Pros:**
|
|
418
|
-
- Universal browser support
|
|
419
|
-
- Simpler event handling
|
|
420
|
-
|
|
421
|
-
**Cons:**
|
|
422
|
-
- Potential style conflicts
|
|
423
|
-
- Editor styles may affect content
|
|
424
|
-
|
|
425
|
-
The library automatically falls back to div mode if Shadow DOM is not supported.
|
|
426
|
-
|
|
427
|
-
## Content Persistence
|
|
428
|
-
|
|
429
|
-
### Saving Content
|
|
430
|
-
|
|
431
|
-
```javascript
|
|
432
|
-
// Get clean HTML (without editor UI)
|
|
433
|
-
const html = editor.getContent();
|
|
434
|
-
|
|
435
|
-
// Save to backend
|
|
436
|
-
await fetch("/api/save", {
|
|
437
|
-
method: "POST",
|
|
438
|
-
body: JSON.stringify({ html }),
|
|
439
|
-
});
|
|
440
|
-
```
|
|
441
|
-
|
|
442
|
-
### Loading Content
|
|
443
|
-
|
|
444
|
-
```javascript
|
|
445
|
-
// Load from backend
|
|
446
|
-
const response = await fetch("/api/content/123");
|
|
447
|
-
const { html } = await response.json();
|
|
448
|
-
|
|
449
|
-
// Set content (clears selection and optionally history)
|
|
450
|
-
editor.setContent(html);
|
|
451
|
-
```
|
|
452
|
-
|
|
453
|
-
## Browser Support
|
|
454
|
-
|
|
455
|
-
- Chrome/Edge 80+
|
|
456
|
-
- Firefox 75+
|
|
457
|
-
- Safari 13.1+
|
|
458
|
-
|
|
459
|
-
Requires support for:
|
|
460
|
-
- ES6 Modules
|
|
461
|
-
- Custom Elements
|
|
462
|
-
- Shadow DOM (optional, falls back to regular DOM)
|
|
463
|
-
- MutationObserver
|
|
464
|
-
|
|
465
|
-
## Bundle Size
|
|
466
|
-
|
|
467
|
-
- ESM: ~144 KB (uncompressed)
|
|
468
|
-
- Browser: ~153 KB (uncompressed)
|
|
469
|
-
- CJS: ~144 KB (uncompressed)
|
|
470
|
-
- UMD: ~144 KB (uncompressed)
|
|
471
|
-
|
|
472
|
-
All builds include source maps for debugging.
|
|
473
|
-
|
|
474
|
-
## License
|
|
475
|
-
|
|
476
|
-
Apache-2.0
|
|
477
|
-
|
|
478
|
-
## Credits
|
|
479
|
-
|
|
480
|
-
Extracted from [VisBug](https://github.com/GoogleChromeLabs/ProjectVisBug) by Adam Argyle.
|
|
481
|
-
|
|
482
|
-
## Contributing
|
|
483
|
-
|
|
484
|
-
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
485
|
-
|
|
486
|
-
## Links
|
|
487
|
-
|
|
488
|
-
- [GitHub Repository](https://github.com/GoogleChromeLabs/ProjectVisBug)
|
|
489
|
-
- [Issue Tracker](https://github.com/GoogleChromeLabs/ProjectVisBug/issues)
|
|
490
|
-
- [Original VisBug](https://github.com/GoogleChromeLabs/ProjectVisBug)
|