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 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