helldots 0.4.0 → 0.5.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/dist/index.d.ts CHANGED
@@ -60,6 +60,11 @@ export interface CommentContext {
60
60
  export type CommentId = string | number;
61
61
 
62
62
  export interface SerializedComment {
63
+ /**
64
+ * Version of this serialized shape. Stamped as 1 by serializeComments;
65
+ * optional because payloads persisted before it existed have none.
66
+ */
67
+ schemaVersion?: number;
63
68
  id: CommentId;
64
69
  text: string;
65
70
  /** ISO timestamp of the last edit; null when never edited. */
@@ -95,16 +100,70 @@ export interface SerializedComment {
95
100
  reactions?: Record<string, string[]>;
96
101
  }
97
102
 
103
+ /**
104
+ * Everything that can change, as one discriminated union. Switch on `type`
105
+ * and TypeScript narrows the rest of the fields for you.
106
+ *
107
+ * The nine specific callbacks below carry exactly the same events at
108
+ * exactly the same moments; subscribe either way, or both.
109
+ */
110
+ export type ChangeEvent =
111
+ | { type: "comment:created"; comment: SerializedComment }
112
+ | { type: "comment:edited"; comment: SerializedComment }
113
+ | { type: "comment:deleted"; id: CommentId }
114
+ | { type: "comment:status-changed"; comment: SerializedComment }
115
+ /** Type, priority or tags changed. */
116
+ | { type: "comment:updated"; comment: SerializedComment }
117
+ | { type: "comment:anchor-lost"; comment: SerializedComment }
118
+ | {
119
+ type: "reply:added";
120
+ comment: SerializedComment;
121
+ reply: CommentReply;
122
+ }
123
+ | {
124
+ type: "reply:deleted";
125
+ comment: SerializedComment;
126
+ reply: CommentReply;
127
+ }
128
+ | {
129
+ type: "reply:edited";
130
+ comment: SerializedComment;
131
+ reply: CommentReply;
132
+ };
133
+
98
134
  export interface CommentOverlayOptions {
99
135
  shortcutKey?: string;
100
136
  shortcutModifier?: "alt" | "ctrl" | "shift";
101
137
  autoInit?: boolean;
102
- /** UI language. Defaults to the browser's language when supported, else "en". */
103
- locale?: "en" | "es";
138
+ /**
139
+ * UI language. "en" and "es" ship today; any other value falls back to
140
+ * English (and a locale missing individual keys falls back per key).
141
+ * Typed as string so a host can pass a runtime-detected code without a
142
+ * cast — an unknown code degrades, it never breaks.
143
+ */
144
+ locale?: string;
104
145
  /** Auto save/restore comments. Default: "none" (host app persists via callbacks). */
105
146
  persistence?: "localStorage" | "none";
106
147
  /** Capture a viewport screenshot and environment snapshot on every new comment. Default: true. */
107
148
  autoScreenshot?: boolean;
149
+ /**
150
+ * Fetch stylesheets the renderer cannot read, so their web fonts survive
151
+ * into screenshots. Default: false.
152
+ *
153
+ * A cross-origin `<link>` (Google Fonts and friends) throws `SecurityError`
154
+ * on `cssRules`, so its `@font-face` never reaches the capture and the text
155
+ * is rendered in a fallback face. The fallback's metrics differ, which
156
+ * shifts glyphs sideways — a drag selection tight around a few letters then
157
+ * comes back holding the wrong ones. Enabling this re-fetches those sheets
158
+ * (the same URLs the page already loaded, cached per session) so the
159
+ * capture matches the page.
160
+ *
161
+ * Off by default because a comment widget making third-party requests
162
+ * should be the host's decision. Leave it off and captures of such a page
163
+ * stay misaligned; a host can also fix it at the source by self-hosting the
164
+ * font or adding `crossorigin` to the `<link>`.
165
+ */
166
+ embedCrossOriginFonts?: boolean;
108
167
  /** Identity used as the author of new comments and replies. */
109
168
  user?: { name: string };
110
169
  /**
@@ -113,6 +172,27 @@ export interface CommentOverlayOptions {
113
172
  * when the host already routes on that name.
114
173
  */
115
174
  linkParam?: string;
175
+ /**
176
+ * Called instead of a full-page load when the widget navigates to another
177
+ * page (the inbox's "view on its page" jump). Hand it your SPA router's
178
+ * push so the app's state survives; call `notifyNavigation()` after the
179
+ * route renders.
180
+ */
181
+ navigate?: (page: string) => void;
182
+ /**
183
+ * Run `notifyNavigation()` automatically on popstate (back/forward).
184
+ * Opt-in and popstate-only: pushState routing still needs an explicit
185
+ * `notifyNavigation()` call from the router's hook. Default: false.
186
+ */
187
+ autoDetectNavigation?: boolean;
188
+ /**
189
+ * Single subscription point: fires for every change, alongside whichever
190
+ * specific callback below carries the same event. Handy when a host syncs
191
+ * everything to one endpoint instead of wiring nine functions. A handler
192
+ * that throws is caught and warned about — it never rolls back the
193
+ * mutation that emitted it.
194
+ */
195
+ onChange?: (event: ChangeEvent) => void;
116
196
  /** Fired after a new comment is saved. */
117
197
  onCommentCreated?: (comment: SerializedComment) => void;
118
198
  /** Fired after a reply is added to any comment. */
@@ -146,6 +226,8 @@ export interface CommentReply {
146
226
  export interface Comment {
147
227
  id: CommentId;
148
228
  text: string;
229
+ /** ISO timestamp of the last edit; null when never edited. */
230
+ editedAt?: string | null;
149
231
  /** Live anchor element; null while the comment is orphaned or inactive. */
150
232
  container: HTMLElement | null;
151
233
  relativeX: number;
@@ -193,7 +275,15 @@ export declare class CommentOverlay {
193
275
  constructor(options?: Omit<CommentOverlayOptions, "autoInit">);
194
276
 
195
277
  toggleCommentMode(): void;
196
- addReply(comment: Comment, text: string): CommentReply;
278
+ /**
279
+ * `screenshots` are data-URLs attached to the reply. Takes the live
280
+ * comment or its id; null when an id does not resolve.
281
+ */
282
+ addReply(
283
+ comment: Comment | CommentId,
284
+ text: string,
285
+ screenshots?: string[]
286
+ ): CommentReply | null;
197
287
  deleteReply(commentId: CommentId, replyId: CommentId): boolean;
198
288
  /** Rewrites a comment's text. False when the id is unknown, the text is blank, or nothing changed. */
199
289
  editComment(id: CommentId, text: string): boolean;
@@ -207,7 +297,24 @@ export declare class CommentOverlay {
207
297
  orphaned: number;
208
298
  inactive: number;
209
299
  };
300
+ /**
301
+ * Re-syncs the widget after a client-side navigation: reclassifies every
302
+ * comment against the new pathname, re-resolves anchors against the new
303
+ * DOM, rebuilds markers and moves the inbox onto the new page. Also the
304
+ * "re-anchor now" primitive for same-path re-renders.
305
+ */
306
+ notifyNavigation(): {
307
+ anchored: number;
308
+ orphaned: number;
309
+ inactive: number;
310
+ };
210
311
  deleteComment(id: CommentId): boolean;
312
+ /**
313
+ * Removes every comment at once (markers, memory, and their persisted
314
+ * entries in localStorage mode). A bulk reset for reconciling against a
315
+ * backend before loadComments — fires no per-comment callbacks.
316
+ */
317
+ clearComments(): void;
211
318
  setCommentStatus(id: CommentId, status: CommentStatus): boolean;
212
319
  setCommentType(id: CommentId, type: CommentType | null): boolean;
213
320
  setCommentPriority(id: CommentId, priority: CommentPriority | null): boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "helldots",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Drop-in comment overlay for web apps — click anywhere to leave a comment anchored to that element, with an automatic screenshot and environment capture",
5
5
  "keywords": [
6
6
  "comments",