helldots 0.3.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/LICENSE +21 -0
- package/README.md +227 -0
- package/dist/helldots.esm.js +1019 -0
- package/dist/helldots.esm.js.map +7 -0
- package/dist/helldots.umd.js +1032 -0
- package/dist/index.d.ts +199 -0
- package/package.json +75 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 xKeCo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# HellDots
|
|
2
|
+
|
|
3
|
+
Drop-in comment overlay for web apps. Your team clicks anywhere on a page,
|
|
4
|
+
leaves a comment anchored to that element, and HellDots captures the context
|
|
5
|
+
needed to act on it — a screenshot, the browser, the viewport, the DOM path.
|
|
6
|
+
|
|
7
|
+
Comments survive reloads and re-anchor themselves after the page changes.
|
|
8
|
+
Nothing is sent anywhere: you own the data through callbacks, or let the
|
|
9
|
+
widget persist to `localStorage`.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install helldots
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
import { createCommentOverlay } from "helldots";
|
|
19
|
+
|
|
20
|
+
createCommentOverlay({
|
|
21
|
+
user: { name: "Ana" },
|
|
22
|
+
persistence: "localStorage",
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
That's it. A toolbar appears at the bottom of the page. `Alt`+`C` (or
|
|
27
|
+
`Option`+`C` on macOS) toggles comment mode; click anywhere to leave a
|
|
28
|
+
comment, or drag to select a region and attach a screenshot of it.
|
|
29
|
+
|
|
30
|
+
### Wiring it to your own backend
|
|
31
|
+
|
|
32
|
+
Skip `persistence` and use the callbacks instead:
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
import { createCommentOverlay } from "helldots";
|
|
36
|
+
|
|
37
|
+
const overlay = createCommentOverlay({
|
|
38
|
+
user: { name: currentUser.name },
|
|
39
|
+
onCommentCreated: (comment) => api.post("/comments", comment),
|
|
40
|
+
onReplyAdded: (comment, reply) =>
|
|
41
|
+
api.post(`/comments/${comment.id}/replies`, reply),
|
|
42
|
+
onCommentStatusChanged: (comment) =>
|
|
43
|
+
api.patch(`/comments/${comment.id}`, comment),
|
|
44
|
+
onCommentUpdated: (comment) => api.patch(`/comments/${comment.id}`, comment),
|
|
45
|
+
onCommentDeleted: (id) => api.delete(`/comments/${id}`),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Restore on load
|
|
49
|
+
const stored = await api.get("/comments");
|
|
50
|
+
overlay.loadComments(stored);
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Every comment is plain JSON — pass `serializeComments()` output straight to
|
|
54
|
+
your API and hand it back to `loadComments()` later.
|
|
55
|
+
|
|
56
|
+
### Server-rendered apps
|
|
57
|
+
|
|
58
|
+
Importing the package on the server is safe — nothing touches the DOM at
|
|
59
|
+
import time. Just call `createCommentOverlay` from the client only:
|
|
60
|
+
|
|
61
|
+
```jsx
|
|
62
|
+
// Next.js, Remix, Astro…
|
|
63
|
+
import { useEffect } from "react";
|
|
64
|
+
import { createCommentOverlay } from "helldots";
|
|
65
|
+
|
|
66
|
+
export function Comments({ user }) {
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
const overlay = createCommentOverlay({ user, persistence: "localStorage" });
|
|
69
|
+
return () => overlay.cleanup();
|
|
70
|
+
}, [user]);
|
|
71
|
+
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## What gets captured
|
|
77
|
+
|
|
78
|
+
When someone leaves a comment, HellDots records more than the text:
|
|
79
|
+
|
|
80
|
+
**A screenshot of the page as they saw it.** Taken automatically, JPEG at half
|
|
81
|
+
scale (~30–100 KB). The widget's own UI is hidden during the capture, so the
|
|
82
|
+
toolbar never ends up inside the image. Dragging a region additionally attaches
|
|
83
|
+
a full-resolution PNG crop of exactly what was selected.
|
|
84
|
+
|
|
85
|
+
**The environment it was reported from** — URL, viewport size, screen
|
|
86
|
+
resolution, device pixel ratio, browser, OS and language. A bug reported at
|
|
87
|
+
390×844 on iOS Safari says so, without anyone having to ask.
|
|
88
|
+
|
|
89
|
+
**Where on the page it was.** A CSS selector, a DOM path, and a structural
|
|
90
|
+
fingerprint of the element. If the page later changes and the element moves,
|
|
91
|
+
the comment re-anchors to it. If it disappears entirely, the comment is marked
|
|
92
|
+
orphaned rather than silently dropped.
|
|
93
|
+
|
|
94
|
+
Set `autoScreenshot: false` to skip the capture — the render costs a moment
|
|
95
|
+
on every comment, and some apps would rather not pay it.
|
|
96
|
+
|
|
97
|
+
## Triage
|
|
98
|
+
|
|
99
|
+
Comments carry an optional type, priority and free-form tags. All three start
|
|
100
|
+
neutral: the person reporting can classify, or not.
|
|
101
|
+
|
|
102
|
+
| Field | Values |
|
|
103
|
+
| ---------- | --------------------------------------------------------- |
|
|
104
|
+
| `type` | `bug`, `suggestion`, `question`, `improvement`, or `null` |
|
|
105
|
+
| `priority` | `high`, `medium`, `low`, or `null` |
|
|
106
|
+
| `tags` | any strings — trimmed, lowercased and de-duplicated |
|
|
107
|
+
|
|
108
|
+
The inbox filters on all of them, combined with page and status. Resolved
|
|
109
|
+
comments show how long they took, measured from creation to resolution.
|
|
110
|
+
|
|
111
|
+
```js
|
|
112
|
+
overlay.setCommentType(id, "bug");
|
|
113
|
+
overlay.setCommentPriority(id, "high");
|
|
114
|
+
overlay.setCommentTags(id, ["checkout", "ios"]);
|
|
115
|
+
overlay.setCommentStatus(id, "resolved"); // stamps the resolution time
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Passing `null` to `setCommentType` or `setCommentPriority` returns the field to
|
|
119
|
+
its neutral state. Reopening a resolved comment clears its resolution time.
|
|
120
|
+
|
|
121
|
+
## Handing a comment to a coding agent
|
|
122
|
+
|
|
123
|
+
Every comment has a **copy** button that puts a plain-text context block on the
|
|
124
|
+
clipboard, built for pasting into an AI coding assistant:
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
Page: /pricing
|
|
128
|
+
Viewport: 1440x900
|
|
129
|
+
Anchor state: anchored
|
|
130
|
+
Status: open
|
|
131
|
+
Selector: #plans > div.card:nth-child(2) > button
|
|
132
|
+
Element: <button class="cta" data-plan="pro">
|
|
133
|
+
DOM path: body > main.layout > section#plans > div.card > button.cta
|
|
134
|
+
Nearby text: "Upgrade to Pro"
|
|
135
|
+
Comment by Ana (2026-07-29T10:14:00.000Z):
|
|
136
|
+
"This button does nothing on mobile"
|
|
137
|
+
Type: bug
|
|
138
|
+
Priority: high
|
|
139
|
+
Tags: checkout, ios
|
|
140
|
+
URL: https://example.com/pricing
|
|
141
|
+
Screen: 390x844
|
|
142
|
+
Browser: Safari 17.2
|
|
143
|
+
OS: iOS 17.2
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Options
|
|
147
|
+
|
|
148
|
+
| Option | Type | Default | |
|
|
149
|
+
| ------------------ | -------------------------------- | ---------------- | --------------------------------------------------------- |
|
|
150
|
+
| `user` | `{ name: string }` | `"Anonymous"` | Author of new comments and replies |
|
|
151
|
+
| `persistence` | `"localStorage"` \| `"none"` | `"none"` | Auto save/restore, or handle it yourself via callbacks |
|
|
152
|
+
| `autoScreenshot` | `boolean` | `true` | Capture a screenshot and environment snapshot per comment |
|
|
153
|
+
| `locale` | `"en"` \| `"es"` | browser language | UI language, falling back to English |
|
|
154
|
+
| `shortcutKey` | `string` | `"c"` | Key that toggles comment mode |
|
|
155
|
+
| `shortcutModifier` | `"alt"` \| `"ctrl"` \| `"shift"` | `"alt"` | Modifier for that key |
|
|
156
|
+
| `autoInit` | `boolean` | `true` | When `false`, returns an initializer to call yourself |
|
|
157
|
+
|
|
158
|
+
### Callbacks
|
|
159
|
+
|
|
160
|
+
| Callback | Fires when |
|
|
161
|
+
| --------------------------------- | -------------------------------------------------- |
|
|
162
|
+
| `onCommentCreated(comment)` | A new comment is saved |
|
|
163
|
+
| `onReplyAdded(comment, reply)` | A reply is added to any comment |
|
|
164
|
+
| `onCommentStatusChanged(comment)` | Status moves between open / in progress / resolved |
|
|
165
|
+
| `onCommentUpdated(comment)` | Type, priority or tags change |
|
|
166
|
+
| `onCommentDeleted(id)` | A comment is removed |
|
|
167
|
+
| `onAnchorLost(comment)` | A comment could not be re-anchored on load |
|
|
168
|
+
|
|
169
|
+
## API
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
const overlay = createCommentOverlay(options);
|
|
173
|
+
|
|
174
|
+
overlay.comments; // Comment[]
|
|
175
|
+
overlay.commentMode; // boolean
|
|
176
|
+
overlay.toggleCommentMode();
|
|
177
|
+
overlay.addReply(comment, text); // → CommentReply
|
|
178
|
+
overlay.serializeComments(); // → SerializedComment[]
|
|
179
|
+
overlay.loadComments(data); // → { anchored, orphaned, inactive }
|
|
180
|
+
overlay.deleteComment(id); // → boolean
|
|
181
|
+
overlay.setCommentStatus(id, status); // → boolean
|
|
182
|
+
overlay.setCommentType(id, type); // → boolean
|
|
183
|
+
overlay.setCommentPriority(id, priority); // → boolean
|
|
184
|
+
overlay.setCommentTags(id, tags); // → boolean
|
|
185
|
+
overlay.cleanup(); // remove the widget entirely
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The setters return `false` for an unknown id or an invalid value, and make no
|
|
189
|
+
change when they do.
|
|
190
|
+
|
|
191
|
+
TypeScript definitions ship with the package — no `@types` install needed.
|
|
192
|
+
|
|
193
|
+
## Storage notes
|
|
194
|
+
|
|
195
|
+
With `persistence: "localStorage"`, every comment (screenshot included) lives
|
|
196
|
+
under a single key shared across all pages of your app. Browsers cap that at
|
|
197
|
+
roughly 5 MB, which is on the order of a hundred comments with screenshots.
|
|
198
|
+
|
|
199
|
+
When the quota is reached, HellDots sheds the _automatic_ screenshots of the
|
|
200
|
+
oldest comments and retries, so the comments themselves survive. Screenshots a
|
|
201
|
+
user deliberately attached are never discarded. If you expect heavy use, wire
|
|
202
|
+
`onCommentCreated` to your own backend instead.
|
|
203
|
+
|
|
204
|
+
## Browser support
|
|
205
|
+
|
|
206
|
+
Modern evergreen browsers. The widget renders inside a Shadow DOM, so your
|
|
207
|
+
page's CSS cannot leak into it and its styles cannot leak out.
|
|
208
|
+
|
|
209
|
+
## ESM only
|
|
210
|
+
|
|
211
|
+
This package ships ES modules only. `import` works everywhere — bundlers, Vite,
|
|
212
|
+
Next.js, native `<script type="module">`. There is no CommonJS build, so
|
|
213
|
+
`require("helldots")` will not work.
|
|
214
|
+
|
|
215
|
+
For a plain `<script>` tag with no bundler, a self-contained UMD build is on
|
|
216
|
+
the CDN:
|
|
217
|
+
|
|
218
|
+
```html
|
|
219
|
+
<script src="https://unpkg.com/helldots"></script>
|
|
220
|
+
<script>
|
|
221
|
+
HellDots.createCommentOverlay({ user: { name: "Ana" } });
|
|
222
|
+
</script>
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## License
|
|
226
|
+
|
|
227
|
+
MIT
|