helldots 0.10.0 → 0.12.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 +60 -0
- package/dist/helldots.esm.js +6 -6
- package/dist/helldots.esm.js.map +4 -4
- package/dist/helldots.umd.js +13 -13
- package/dist/index.d.ts +66 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -374,6 +374,64 @@ Whatever you declare here is taken at face value and stored as-is. The record
|
|
|
374
374
|
says what your application asserted about who acted; verifying that claim is
|
|
375
375
|
your backend’s job, and `onChange` carries every mutation to it.
|
|
376
376
|
|
|
377
|
+
### Who can edit and delete
|
|
378
|
+
|
|
379
|
+
By default you may edit and delete what carries your identity, and nothing
|
|
380
|
+
else. Somebody else’s comment simply has no Edit or Delete in its ⋯ menu.
|
|
381
|
+
|
|
382
|
+
The rule compares `authorId` against your `user.id`, falling back to the
|
|
383
|
+
display name when neither side has an id — so **pass `user.id` if this
|
|
384
|
+
matters to you**. Two teammates sharing a display name own each other’s
|
|
385
|
+
comments, and that is not a bug the widget can fix without one.
|
|
386
|
+
|
|
387
|
+
A host that declares no `user` at all is unaffected: every record is written
|
|
388
|
+
by the same anonymous actor, so nothing is ever hidden. Single-user setups
|
|
389
|
+
and the playground behave exactly as they did.
|
|
390
|
+
|
|
391
|
+
Override it with `can` when the rule is not yours — moderators, an owner
|
|
392
|
+
role, a read-only viewer:
|
|
393
|
+
|
|
394
|
+
```js
|
|
395
|
+
createCommentOverlay({
|
|
396
|
+
user: { name: session.name, id: session.userId },
|
|
397
|
+
can: (action, target) => {
|
|
398
|
+
if (session.role === "admin") return true;
|
|
399
|
+
if (session.role === "viewer") return false;
|
|
400
|
+
return target.authorId === session.userId;
|
|
401
|
+
},
|
|
402
|
+
});
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
`action` is one of `"edit:comment"`, `"delete:comment"`, `"edit:reply"` or
|
|
406
|
+
`"delete:reply"`. `target` carries `{ id, author, authorId }`, plus
|
|
407
|
+
`commentId` on the two reply actions. Return **literal `true`** to allow:
|
|
408
|
+
anything else denies, including the `undefined` of a branch that forgot to
|
|
409
|
+
return, and a `can` that throws denies while warning to the console. A
|
|
410
|
+
permission predicate is the wrong place to be generous.
|
|
411
|
+
|
|
412
|
+
Only those four actions are asked about. Status, type, priority, tags,
|
|
413
|
+
reactions and replying stay open to everyone — triage is shared work, and
|
|
414
|
+
all of it is reversible.
|
|
415
|
+
|
|
416
|
+
The same verdict is readable from outside, so a delete button in your own
|
|
417
|
+
chrome can ask the one rule instead of keeping a copy of it in step:
|
|
418
|
+
|
|
419
|
+
```js
|
|
420
|
+
overlay.can("delete:comment", { id, author, authorId }); // → boolean
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
**Your own calls are never refused.** `can` gates the widget’s menus and the
|
|
424
|
+
mutations that come from a click inside it; `overlay.deleteComment(id)` from
|
|
425
|
+
your code always goes through, so a moderation flow your backend has already
|
|
426
|
+
authorized is not blocked by a client-side rule it outranks.
|
|
427
|
+
|
|
428
|
+
> **This is not authorization.** HellDots runs in the page, so anyone with a
|
|
429
|
+
> console reaches the API directly no matter what `can` returns. What it
|
|
430
|
+
> removes is the accidental path — the button that should never have been
|
|
431
|
+
> offered. Real enforcement belongs on your server: check `authorId` against
|
|
432
|
+
> the session when the `comment:deleted` or `comment:edited` event reaches
|
|
433
|
+
> your backend.
|
|
434
|
+
|
|
377
435
|
## Triage
|
|
378
436
|
|
|
379
437
|
Comments carry an optional type, priority and free-form tags. All three start
|
|
@@ -565,6 +623,7 @@ OS: iOS 17.2
|
|
|
565
623
|
| Option | Type | Default | |
|
|
566
624
|
| ----------------------- | ------------------------------------ | ------------------- | ----------------------------------------------------------------- |
|
|
567
625
|
| `user` | `{ name: string, id?: string }` | `"Anonymous"` | Author of new comments and replies; `id` persists as `authorId` |
|
|
626
|
+
| `can` | `(action, target) => boolean` | own records only | Vetoes editing and deleting a comment or reply from the widget |
|
|
568
627
|
| `persistence` | `"localStorage"` \| `"none"` | `"none"` | Auto save/restore, or handle it yourself via callbacks |
|
|
569
628
|
| `autoScreenshot` | `boolean` | `true` | Capture a screenshot and environment snapshot per comment |
|
|
570
629
|
| `embedCrossOriginFonts` | `boolean` | `false` | Fetch unreadable stylesheets so their web fonts reach the capture |
|
|
@@ -735,6 +794,7 @@ overlay.setCommentTags(id, tags); // → boolean
|
|
|
735
794
|
overlay.toggleCommentReaction(id, emoji); // → boolean
|
|
736
795
|
overlay.toggleReplyReaction(commentId, replyId, emoji); // → boolean
|
|
737
796
|
overlay.setUser(user); // → boolean (null returns to the anonymous author)
|
|
797
|
+
overlay.can(action, target); // → boolean (may this actor edit/delete it?)
|
|
738
798
|
overlay.exportCommentsCsv(comments?); // → string (and downloads it)
|
|
739
799
|
overlay.exportMetricsCsv(comments?); // → string (and downloads it)
|
|
740
800
|
overlay.cleanup(); // remove the widget entirely
|