Methods
Everything on a CommentOverlay instance — what each returns, and when it returns false.
import type { CommentOverlay } from 'helldots';Properties
Prop
Type
Mode
toggleCommentMode
overlay.toggleCommentMode(): voidFlips comment mode. Identical to the toolbar button and the keyboard shortcut,
and it emits onCommentModeChanged the same way they do.
Comments
editComment
overlay.editComment(id: CommentId, text: string): booleanRewrites a comment's text and stamps editedAt. Returns false — changing
nothing — when the id is unknown, the text is blank, or the text is what the
comment already said.
deleteComment
overlay.deleteComment(id: CommentId): booleanRemoves the comment and its marker. Your own call is never refused by can.
clearComments
overlay.clearComments(): voidRemoves every comment at once — markers, memory, and the persisted entries in
localStorage mode. Fires no per-comment callbacks: it is a bulk reset for
reconciling against a backend before loadComments, and echoing a hundred
deletions back to a server is exactly what you do not want.
commentLink
overlay.commentLink(id: CommentId): string | nullThe shareable URL for a comment — the current page plus the
linkParam query parameter. null when the id
is unknown.
Replies
addReply
overlay.addReply(
comment: Comment | CommentId,
text: string,
screenshots?: string[],
): CommentReply | nullTakes the live comment or its id. screenshots are data URLs attached to the
reply — they are not passed through transformScreenshot, because strings
you hand in are already yours. Returns null when an id does not resolve.
It is synchronous
That is why an attachment picked in the reply composer is transformed at
pick-time rather than at send-time — addReply cannot wait on your upload.
See transformScreenshot.
editReply
overlay.editReply(commentId: CommentId, replyId: CommentId, text: string): booleanSame contract as editComment, one level down.
deleteReply
overlay.deleteReply(commentId: CommentId, replyId: CommentId): booleanA reply's id is unique only inside its thread, which is why both ids are required.
Triage
Each setter returns false for an unknown id or an invalid value, and makes no
change when it does. Re-applying a value a comment already holds is a no-op — no
event, no write.
overlay.setCommentStatus(id, status: CommentStatus): boolean
overlay.setCommentType(id, type: CommentType | null): boolean
overlay.setCommentPriority(id, priority: CommentPriority | null): boolean
overlay.setCommentTags(id, tags: string[]): booleansetCommentStatus(id, "resolved") stamps resolvedAt; moving out of resolved
clears it. setCommentTags normalises what you give it — trimmed, lowercased,
de-duplicated. Passing null to the type or priority setter returns the field to
its neutral state.
Reactions
overlay.toggleCommentReaction(id: CommentId, emoji: string): boolean
overlay.toggleReplyReaction(commentId, replyId, emoji: string): booleanBoth toggle: present, the reaction is removed; absent, it is added. The actor is
user.id ?? user.name. Returns false when the id or the emoji is unknown — the
set is fixed at 👍 👎 ❤️ 🎉 👀 🚀.
Identity & permissions
setUser
overlay.setUser(user: { name: string; id?: string } | null): booleanReplaces the identity new comments, replies and reactions are attributed to.
Everything already recorded keeps the author it was written with. null returns
to the anonymous author.
Returns false, changing nothing, for anything that is neither null nor an
object with a non-blank name.
can
overlay.can(action: PermissionAction, target: PermissionTarget): booleanThe same verdict the widget's own menus render from, exposed so a delete button in your own chrome can ask the one rule instead of keeping a copy of it in step.
overlay.can('delete:comment', { id, author, authorId });Serialisation
serializeComments
overlay.serializeComments(): SerializedComment[]A JSON-safe snapshot — no live element references, no runtime-only fields.
Stamped with schemaVersion: 1. Hand it straight to your API.
loadComments
overlay.loadComments(data: SerializedComment[]): {
anchored: number;
orphaned: number;
inactive: number;
}Replaces by id. It never removes: a comment deleted on your server stays on
screen until you clearComments() first.
Malformed records are skipped and reported through
onError(error, "load") rather than throwing.
Called before the widget has mounted — possible when a fetch resolves while the
document is still parsing — the data is held and applied at mount, and the counts
come back as zeroes because nothing has been resolved yet. Load from onReady
when the counts matter.
notifyNavigation
overlay.notifyNavigation(): {
anchored: number;
orphaned: number;
inactive: number;
}Re-syncs the widget after a client-side navigation: reclassifies every comment against the new pathname, re-resolves anchors against the new DOM, rebuilds the markers and moves the inbox onto the new page.
Also the "re-anchor now" primitive for same-path re-renders — call it after your app has replaced a route's DOM.
Metrics & exports
getMetrics
overlay.getMetrics(): CommentMetricsAggregate figures over every comment the widget holds. Unfiltered on purpose: the dashboard inside the inbox measures whatever that panel is showing, but your app has no notion of those filters.
exportCommentsCsv / exportMetricsCsv
overlay.exportCommentsCsv(comments?: SerializedComment[]): string
overlay.exportMetricsCsv(comments?: SerializedComment[]): stringDownloads the file and returns the same text, so a host that wanted to POST those rows somewhere does not have to build them a second time. Defaults to every comment. RFC 4180 with a UTF-8 BOM; screenshots stay out.
printMetricsReport
overlay.printMetricsReport(comments?: SerializedComment[], scope?: string): voidOpens the browser's print dialog on a report of the figures, which is where "save
as PDF" lives. The report is built in its own document, so what prints is the
report rather than the host page. scope is an optional label printed on it.
Teardown
cleanup
overlay.cleanup(): voidRemoves the widget entirely — markers, toolbar, listeners, shadow root. Call it from a React effect's cleanup, and on logout.