@gamention/pulse-core 0.3.1 → 0.3.3

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.
@@ -1,30 +1,30 @@
1
1
  var u = Object.defineProperty;
2
- var f = (h, n, t) => n in h ? u(h, n, { enumerable: !0, configurable: !0, writable: !0, value: t }) : h[n] = t;
3
- var r = (h, n, t) => f(h, typeof n != "symbol" ? n + "" : n, t);
2
+ var f = (h, a, e) => a in h ? u(h, a, { enumerable: !0, configurable: !0, writable: !0, value: e }) : h[a] = e;
3
+ var r = (h, a, e) => f(h, typeof a != "symbol" ? a + "" : a, e);
4
4
  import { DEFAULT_ENDPOINT as _, RECONNECT_BASE_DELAY_MS as m, RECONNECT_MAX_DELAY_MS as w, DEFAULT_ENV_CONFIG as d, CURSOR_THROTTLE_MS as l, PRESENCE_HEARTBEAT_MS as y } from "@gamention/pulse-shared";
5
5
  class p {
6
6
  constructor() {
7
7
  r(this, "handlers", /* @__PURE__ */ new Map());
8
8
  }
9
- on(n, t) {
10
- this.handlers.has(n) || this.handlers.set(n, /* @__PURE__ */ new Set());
11
- const e = this.handlers.get(n);
12
- return e.add(t), () => e.delete(t);
9
+ on(a, e) {
10
+ this.handlers.has(a) || this.handlers.set(a, /* @__PURE__ */ new Set());
11
+ const t = this.handlers.get(a);
12
+ return t.add(e), () => t.delete(e);
13
13
  }
14
- off(n, t) {
15
- var e;
16
- (e = this.handlers.get(n)) == null || e.delete(t);
14
+ off(a, e) {
15
+ var t;
16
+ (t = this.handlers.get(a)) == null || t.delete(e);
17
17
  }
18
- emit(n, t) {
19
- var e;
20
- (e = this.handlers.get(n)) == null || e.forEach((i) => i(t));
18
+ emit(a, e) {
19
+ var t;
20
+ (t = this.handlers.get(a)) == null || t.forEach((i) => i(e));
21
21
  }
22
22
  removeAll() {
23
23
  this.handlers.clear();
24
24
  }
25
25
  }
26
26
  class v extends p {
27
- constructor(t) {
27
+ constructor(e) {
28
28
  super();
29
29
  r(this, "ws", null);
30
30
  r(this, "endpoint");
@@ -32,7 +32,7 @@ class v extends p {
32
32
  r(this, "reconnectTimer", null);
33
33
  r(this, "_state", "disconnected");
34
34
  r(this, "permanentlyClosed", !1);
35
- this.endpoint = t ?? _;
35
+ this.endpoint = e ?? _;
36
36
  }
37
37
  get state() {
38
38
  return this._state;
@@ -40,39 +40,39 @@ class v extends p {
40
40
  connect() {
41
41
  this.ws || this.permanentlyClosed || (this._state = "connecting", this.emit("state", this._state), this.ws = new WebSocket(this.endpoint), this.ws.addEventListener("open", () => {
42
42
  this._state = "connected", this.reconnectAttempt = 0, this.emit("state", this._state);
43
- }), this.ws.addEventListener("message", (t) => {
43
+ }), this.ws.addEventListener("message", (e) => {
44
44
  try {
45
- const e = JSON.parse(t.data);
46
- this.emit("message", e);
45
+ const t = JSON.parse(e.data);
46
+ this.emit("message", t);
47
47
  } catch {
48
48
  }
49
49
  }), this.ws.addEventListener("close", () => {
50
50
  this.ws = null, this._state = "disconnected", this.emit("state", this._state), this.scheduleReconnect();
51
51
  }), this.ws.addEventListener("error", () => {
52
- var t;
53
- (t = this.ws) == null || t.close();
52
+ var e;
53
+ (e = this.ws) == null || e.close();
54
54
  }));
55
55
  }
56
56
  disconnect() {
57
- var t;
58
- this.reconnectTimer && (clearTimeout(this.reconnectTimer), this.reconnectTimer = null), this.reconnectAttempt = 0, (t = this.ws) == null || t.close(), this.ws = null, this._state = "disconnected", this.emit("state", this._state);
59
- }
60
- send(t) {
61
57
  var e;
62
- ((e = this.ws) == null ? void 0 : e.readyState) === WebSocket.OPEN && this.ws.send(JSON.stringify(t));
58
+ this.reconnectTimer && (clearTimeout(this.reconnectTimer), this.reconnectTimer = null), this.reconnectAttempt = 0, (e = this.ws) == null || e.close(), this.ws = null, this._state = "disconnected", this.emit("state", this._state);
59
+ }
60
+ send(e) {
61
+ var t;
62
+ ((t = this.ws) == null ? void 0 : t.readyState) === WebSocket.OPEN && this.ws.send(JSON.stringify(e));
63
63
  }
64
64
  permanentDisconnect() {
65
65
  this.permanentlyClosed = !0, this.disconnect();
66
66
  }
67
67
  scheduleReconnect() {
68
68
  if (this.permanentlyClosed) return;
69
- const t = Math.min(
69
+ const e = Math.min(
70
70
  m * 2 ** this.reconnectAttempt,
71
71
  w
72
72
  );
73
73
  this.reconnectAttempt++, this.reconnectTimer = setTimeout(() => {
74
74
  this.reconnectTimer = null, this.connect();
75
- }, t);
75
+ }, e);
76
76
  }
77
77
  }
78
78
  class I extends p {
@@ -103,11 +103,11 @@ class I extends p {
103
103
  return this._config;
104
104
  }
105
105
  /** Optimistically remove a comment from local state (before server round-trip). */
106
- removeComment(t) {
107
- for (const [e, i] of this._threads) {
108
- const s = i.comments.findIndex((a) => a.id === t);
106
+ removeComment(e) {
107
+ for (const [t, i] of this._threads) {
108
+ const s = i.comments.findIndex((n) => n.id === e);
109
109
  if (s !== -1) {
110
- i.comments.splice(s, 1), i.comments.length === 0 && this._threads.delete(e), this.emit("threads", this.threads);
110
+ i.comments.splice(s, 1), i.comments.length === 0 && this._threads.delete(t), this.emit("threads", this.threads);
111
111
  return;
112
112
  }
113
113
  }
@@ -122,202 +122,202 @@ class I extends p {
122
122
  return this._notifications;
123
123
  }
124
124
  get unreadCount() {
125
- return this._notifications.filter((t) => !t.read).length;
125
+ return this._notifications.filter((e) => !e.read).length;
126
126
  }
127
127
  /** Optimistically mark a single notification as read. */
128
- markNotificationRead(t) {
129
- const e = this._notifications.find((i) => i.id === t);
130
- e && !e.read && (e.read = !0, this.emit("notifications", this._notifications));
128
+ markNotificationRead(e) {
129
+ const t = this._notifications.find((i) => i.id === e);
130
+ t && !t.read && (t.read = !0, this.emit("notifications", this._notifications));
131
131
  }
132
132
  /** Optimistically mark all notifications as read. */
133
133
  markAllNotificationsRead() {
134
- let t = !1;
135
- for (const e of this._notifications)
136
- e.read || (e.read = !0, t = !0);
137
- t && this.emit("notifications", this._notifications);
134
+ let e = !1;
135
+ for (const t of this._notifications)
136
+ t.read || (t.read = !0, e = !0);
137
+ e && this.emit("notifications", this._notifications);
138
138
  }
139
139
  get activityLogs() {
140
140
  return this._activityLogs;
141
141
  }
142
- getUser(t) {
143
- return this._users.get(t);
142
+ getUser(e) {
143
+ return this._users.get(e);
144
144
  }
145
145
  /** Get all known users (from presence + comment authors). */
146
146
  get users() {
147
147
  return [...this._users.values()];
148
148
  }
149
- getReactions(t) {
150
- return this._reactions.get(t) ?? [];
149
+ getReactions(e) {
150
+ return this._reactions.get(e) ?? [];
151
151
  }
152
- getTypingUsers(t) {
153
- const e = this._typing.get(t);
154
- if (!e) return [];
152
+ getTypingUsers(e) {
153
+ const t = this._typing.get(e);
154
+ if (!t) return [];
155
155
  const i = Date.now(), s = [];
156
- for (const [a, o] of e)
157
- i - o < 3e3 && s.push(a);
156
+ for (const [n, o] of t)
157
+ i - o < 3e3 && s.push(n);
158
158
  return s;
159
159
  }
160
160
  get viewports() {
161
161
  return this._viewports;
162
162
  }
163
- getViewport(t) {
164
- return this._viewports.get(t);
163
+ getViewport(e) {
164
+ return this._viewports.get(e);
165
165
  }
166
166
  get selections() {
167
167
  return this._selections;
168
168
  }
169
169
  /** Resolve a relative URL to absolute using baseUrl. */
170
- resolveUrl(t) {
171
- return !this.baseUrl || !t || t.startsWith("http://") || t.startsWith("https://") ? t : `${this.baseUrl}${t}`;
170
+ resolveUrl(e) {
171
+ return !this.baseUrl || !e || e.startsWith("http://") || e.startsWith("https://") ? e : `${this.baseUrl}${e}`;
172
172
  }
173
- resolveAttachments(t) {
174
- return t.map((e) => ({
175
- ...e,
176
- url: this.resolveUrl(e.url),
177
- thumbnailUrl: e.thumbnailUrl ? this.resolveUrl(e.thumbnailUrl) : void 0
173
+ resolveAttachments(e) {
174
+ return e.map((t) => ({
175
+ ...t,
176
+ url: this.resolveUrl(t.url),
177
+ thumbnailUrl: t.thumbnailUrl ? this.resolveUrl(t.thumbnailUrl) : void 0
178
178
  }));
179
179
  }
180
- resolveComment(t) {
181
- return !t.attachments || t.attachments.length === 0 ? t : { ...t, attachments: this.resolveAttachments(t.attachments) };
180
+ resolveComment(e) {
181
+ return !e.attachments || e.attachments.length === 0 ? e : { ...e, attachments: this.resolveAttachments(e.attachments) };
182
182
  }
183
- resolveThread(t) {
183
+ resolveThread(e) {
184
184
  return {
185
- ...t,
186
- comments: t.comments.map((e) => this.resolveComment(e))
185
+ ...e,
186
+ comments: e.comments.map((t) => this.resolveComment(t))
187
187
  };
188
188
  }
189
- handleMessage(t) {
190
- switch (t.type) {
189
+ handleMessage(e) {
190
+ switch (e.type) {
191
191
  case "auth:ok":
192
- this._config = t.config ?? { ...d }, this._p2pConfig = t.p2pConfig ?? null, this._user = t.user, this._users.clear();
193
- for (const e of t.users) this._users.set(e.id, e);
192
+ this._config = e.config ?? { ...d }, this._p2pConfig = e.p2pConfig ?? null, this._user = e.user, this._users.clear();
193
+ for (const t of e.users) this._users.set(t.id, t);
194
194
  this._presence.clear();
195
- for (const e of t.presence)
196
- this._presence.set(e.user.id, e), this._users.set(e.user.id, e.user);
197
- this._users.set(t.user.id, t.user), this._threads.clear();
198
- for (const e of t.threads) this._threads.set(e.id, this.resolveThread(e));
199
- this._notifications = t.notifications, this._reactions.clear();
200
- for (const e of t.reactions) {
201
- const i = this._reactions.get(e.targetId) ?? [];
202
- i.push(e), this._reactions.set(e.targetId, i);
195
+ for (const t of e.presence)
196
+ this._presence.set(t.user.id, t), this._users.set(t.user.id, t.user);
197
+ this._users.set(e.user.id, e.user), this._threads.clear();
198
+ for (const t of e.threads) this._threads.set(t.id, this.resolveThread(t));
199
+ this._notifications = e.notifications, this._reactions.clear();
200
+ for (const t of e.reactions) {
201
+ const i = this._reactions.get(t.targetId) ?? [];
202
+ i.push(t), this._reactions.set(t.targetId, i);
203
203
  }
204
- this._activityLogs = [...t.activityLogs], this.emit("auth", t.user), this.emit("presence", this.presence), this.emit("threads", this.threads), this.emit("notifications", this._notifications), this.emit("reactions", null), this.emit("activity-logs", this._activityLogs);
204
+ this._activityLogs = [...e.activityLogs], this.emit("auth", e.user), this.emit("presence", this.presence), this.emit("threads", this.threads), this.emit("notifications", this._notifications), this.emit("reactions", null), this.emit("activity-logs", this._activityLogs);
205
205
  break;
206
206
  case "presence:join":
207
- this._presence.set(t.user.user.id, t.user), this._users.set(t.user.user.id, t.user.user), this.emit("presence", this.presence);
207
+ this._presence.set(e.user.user.id, e.user), this._users.set(e.user.user.id, e.user.user), this.emit("presence", this.presence);
208
208
  break;
209
209
  case "presence:leave":
210
- this._presence.delete(t.userId), this._viewports.delete(t.userId), this._selections.delete(t.userId);
211
- for (const e of this._typing.values())
212
- e.delete(t.userId);
210
+ this._presence.delete(e.userId), this._viewports.delete(e.userId), this._selections.delete(e.userId);
211
+ for (const t of this._typing.values())
212
+ t.delete(e.userId);
213
213
  this.emit("presence", this.presence);
214
214
  break;
215
215
  case "presence:update": {
216
- const e = this._presence.get(t.userId);
217
- e && (e.status = t.status, this.emit("presence", this.presence));
216
+ const t = this._presence.get(e.userId);
217
+ t && (t.status = e.status, this.emit("presence", this.presence));
218
218
  break;
219
219
  }
220
220
  case "cursor:move":
221
- this.emit("cursor", { userId: t.userId, position: t.position });
221
+ this.emit("cursor", { userId: e.userId, position: e.position });
222
222
  break;
223
223
  case "click:perform":
224
- this.emit("click", { userId: t.userId, position: t.position });
224
+ this.emit("click", { userId: e.userId, position: e.position });
225
225
  break;
226
226
  case "thread:created":
227
- this._threads.set(t.thread.id, this.resolveThread(t.thread)), this.emit("threads", this.threads);
227
+ this._threads.set(e.thread.id, this.resolveThread(e.thread)), this.emit("threads", this.threads);
228
228
  break;
229
229
  case "comment:created": {
230
- const e = this._threads.get(t.threadId);
231
- e && (e.comments.push(this.resolveComment(t.comment)), e.updatedAt = t.comment.createdAt, this.emit("threads", this.threads));
230
+ const t = this._threads.get(e.threadId);
231
+ t && (t.comments.push(this.resolveComment(e.comment)), t.updatedAt = e.comment.createdAt, this.emit("threads", this.threads));
232
232
  break;
233
233
  }
234
234
  case "comment:edited": {
235
- const e = this._threads.get(t.threadId);
236
- if (e) {
237
- const i = e.comments.findIndex((s) => s.id === t.comment.id);
238
- i !== -1 && (e.comments[i] = this.resolveComment(t.comment)), this.emit("threads", this.threads);
235
+ const t = this._threads.get(e.threadId);
236
+ if (t) {
237
+ const i = t.comments.findIndex((s) => s.id === e.comment.id);
238
+ i !== -1 && (t.comments[i] = this.resolveComment(e.comment)), this.emit("threads", this.threads);
239
239
  }
240
240
  break;
241
241
  }
242
242
  case "comment:deleted": {
243
- const e = this._threads.get(t.threadId);
244
- e && (e.comments = e.comments.filter((i) => i.id !== t.commentId), e.comments.length === 0 && this._threads.delete(t.threadId), this.emit("threads", this.threads));
243
+ const t = this._threads.get(e.threadId);
244
+ t && (t.comments = t.comments.filter((i) => i.id !== e.commentId), t.comments.length === 0 && this._threads.delete(e.threadId), this.emit("threads", this.threads));
245
245
  break;
246
246
  }
247
247
  case "thread:resolved": {
248
- const e = this._threads.get(t.threadId);
249
- e && (e.resolved = t.resolved, this.emit("threads", this.threads));
248
+ const t = this._threads.get(e.threadId);
249
+ t && (t.resolved = e.resolved, this.emit("threads", this.threads));
250
250
  break;
251
251
  }
252
252
  case "thread:deleted":
253
- this._threads.delete(t.threadId), this.emit("threads", this.threads);
253
+ this._threads.delete(e.threadId), this.emit("threads", this.threads);
254
254
  break;
255
255
  case "reaction:added": {
256
- const e = this._reactions.get(t.reaction.targetId) ?? [];
257
- e.push(t.reaction), this._reactions.set(t.reaction.targetId, e), this.emit("reactions", {
258
- targetId: t.reaction.targetId,
259
- reactions: e
256
+ const t = this._reactions.get(e.reaction.targetId) ?? [];
257
+ t.push(e.reaction), this._reactions.set(e.reaction.targetId, t), this.emit("reactions", {
258
+ targetId: e.reaction.targetId,
259
+ reactions: t
260
260
  });
261
261
  break;
262
262
  }
263
263
  case "reaction:removed": {
264
- const e = this._reactions.get(t.targetId);
265
- if (e) {
266
- const i = e.filter((s) => s.id !== t.reactionId);
267
- this._reactions.set(t.targetId, i), this.emit("reactions", {
268
- targetId: t.targetId,
264
+ const t = this._reactions.get(e.targetId);
265
+ if (t) {
266
+ const i = t.filter((s) => s.id !== e.reactionId);
267
+ this._reactions.set(e.targetId, i), this.emit("reactions", {
268
+ targetId: e.targetId,
269
269
  reactions: i
270
270
  });
271
271
  }
272
272
  break;
273
273
  }
274
274
  case "notification":
275
- this._notifications.unshift(t.notification), this.emit("notifications", this._notifications);
275
+ this._notifications.unshift(e.notification), this.emit("notifications", this._notifications);
276
276
  break;
277
277
  case "typing:indicator": {
278
- this._typing.has(t.threadId) || this._typing.set(t.threadId, /* @__PURE__ */ new Map()), this._typing.get(t.threadId).set(t.userId, Date.now()), this.emit("typing", { threadId: t.threadId, userId: t.userId });
278
+ this._typing.has(e.threadId) || this._typing.set(e.threadId, /* @__PURE__ */ new Map()), this._typing.get(e.threadId).set(e.userId, Date.now()), this.emit("typing", { threadId: e.threadId, userId: e.userId });
279
279
  break;
280
280
  }
281
281
  case "viewport:update": {
282
- this._viewports.set(t.userId, {
283
- scrollX: t.scrollX,
284
- scrollY: t.scrollY,
285
- viewportWidth: t.viewportWidth,
286
- viewportHeight: t.viewportHeight,
287
- pageWidth: t.pageWidth,
288
- pageHeight: t.pageHeight
289
- }), this.emit("viewport", { userId: t.userId });
282
+ this._viewports.set(e.userId, {
283
+ scrollX: e.scrollX,
284
+ scrollY: e.scrollY,
285
+ viewportWidth: e.viewportWidth,
286
+ viewportHeight: e.viewportHeight,
287
+ pageWidth: e.pageWidth,
288
+ pageHeight: e.pageHeight
289
+ }), this.emit("viewport", { userId: e.userId });
290
290
  break;
291
291
  }
292
292
  case "selection:update":
293
- this._selections.set(t.userId, t.selection), this.emit("selection", { userId: t.userId, selection: t.selection });
293
+ this._selections.set(e.userId, e.selection), this.emit("selection", { userId: e.userId, selection: e.selection });
294
294
  break;
295
295
  case "emoji:drop":
296
296
  this.emit("emoji-drop", {
297
- userId: t.userId,
298
- emoji: t.emoji,
299
- position: t.position
297
+ userId: e.userId,
298
+ emoji: e.emoji,
299
+ position: e.position
300
300
  });
301
301
  break;
302
302
  case "draw:stroke":
303
303
  this.emit("draw-stroke", {
304
- userId: t.userId,
305
- points: t.points,
306
- color: t.color,
307
- width: t.width
304
+ userId: e.userId,
305
+ points: e.points,
306
+ color: e.color,
307
+ width: e.width
308
308
  });
309
309
  break;
310
310
  case "draw:clear":
311
- this.emit("draw-clear", { userId: t.userId });
311
+ this.emit("draw-clear", { userId: e.userId });
312
312
  break;
313
313
  case "activity:logged":
314
- this._activityLogs.unshift(t.activityLog), this._activityLogs.length > 100 && (this._activityLogs = this._activityLogs.slice(0, 100)), this.emit("activity-logs", this._activityLogs);
314
+ this._activityLogs.unshift(e.activityLog), this._activityLogs.length > 100 && (this._activityLogs = this._activityLogs.slice(0, 100)), this.emit("activity-logs", this._activityLogs);
315
315
  break;
316
316
  case "auth:error":
317
- this.emit("auth:error", t);
317
+ this.emit("auth:error", e);
318
318
  break;
319
319
  case "error":
320
- this.emit("error", t);
320
+ this.emit("error", e);
321
321
  break;
322
322
  }
323
323
  }
@@ -326,7 +326,7 @@ class I extends p {
326
326
  }
327
327
  }
328
328
  class k extends p {
329
- constructor(t) {
329
+ constructor(e) {
330
330
  var i;
331
331
  super();
332
332
  r(this, "state");
@@ -340,9 +340,9 @@ class k extends p {
340
340
  r(this, "_p2pInstance", null);
341
341
  r(this, "p2pEnabled");
342
342
  r(this, "iceServers");
343
- this.config = t, this.state = new I(), this.state.baseUrl = (t.endpoint ?? "").replace(/^ws(s?):/, "http$1:").replace(/\/$/, "");
344
- const e = ((i = t.endpoint) == null ? void 0 : i.replace(/^http/, "ws")) ?? void 0;
345
- this.connection = new v(e), this.p2pEnabled = t.p2p ?? !1, this.iceServers = t.iceServers ?? [{ urls: "stun:stun.l.google.com:19302" }], this.connection.on("message", (s) => {
343
+ this.config = e, this.state = new I(), this.state.baseUrl = (e.endpoint ?? "").replace(/^ws(s?):/, "http$1:").replace(/\/$/, "");
344
+ const t = ((i = e.endpoint) == null ? void 0 : i.replace(/^http/, "ws")) ?? void 0;
345
+ this.connection = new v(t), this.p2pEnabled = e.p2p ?? !1, this.iceServers = e.iceServers ?? [{ urls: "stun:stun.l.google.com:19302" }], this.connection.on("message", (s) => {
346
346
  this.state.handleMessage(s), this.emit(s.type, s), s.type === "auth:error" && this.connection.permanentDisconnect(), this._p2pInstance && (s.type === "signal:offer" ? this._p2pInstance.handleSignalOffer(s.fromUserId, s.sdp) : s.type === "signal:answer" ? this._p2pInstance.handleSignalAnswer(s.fromUserId, s.sdp) : s.type === "signal:ice" ? this._p2pInstance.handleIceCandidate(s.fromUserId, s.candidate) : s.type === "p2p:sync" ? this._p2pInstance.handleP2PSync(s.fromUserId, s.update) : s.type === "presence:join" ? this._p2pInstance.onPeerJoined(s.user.user.id) : s.type === "presence:leave" && this._p2pInstance.onPeerLeft(s.userId));
347
347
  }), this.connection.on("state", (s) => {
348
348
  this.emit("connection", s), s === "connected" ? (this.authenticate(), this.startHeartbeat()) : s === "disconnected" && this.stopHeartbeat();
@@ -359,8 +359,8 @@ class k extends p {
359
359
  this.stopHeartbeat(), this.cursorTimer && (clearTimeout(this.cursorTimer), this.cursorTimer = null), this.connection.disconnect(), this.state.reset();
360
360
  }
361
361
  destroy() {
362
- var t;
363
- (t = this._p2pInstance) == null || t.destroy(), this._p2pInstance = null, this._p2p = null, this.disconnect(), this.removeAll(), this.state.removeAll(), this.connection.removeAll();
362
+ var e;
363
+ (e = this._p2pInstance) == null || e.destroy(), this._p2pInstance = null, this._p2p = null, this.disconnect(), this.removeAll(), this.state.removeAll(), this.connection.removeAll();
364
364
  }
365
365
  authenticate() {
366
366
  this.send({
@@ -370,13 +370,13 @@ class k extends p {
370
370
  room: this.config.room
371
371
  });
372
372
  }
373
- send(t) {
374
- this.connection.send(t);
373
+ send(e) {
374
+ this.connection.send(e);
375
375
  }
376
376
  // ── Cursors ──
377
- moveCursor(t) {
378
- const e = Date.now();
379
- this.pendingCursor = t, e - this.lastCursorSend >= l ? this.flushCursor() : this.cursorTimer || (this.cursorTimer = setTimeout(() => {
377
+ moveCursor(e) {
378
+ const t = Date.now();
379
+ this.pendingCursor = e, t - this.lastCursorSend >= l ? this.flushCursor() : this.cursorTimer || (this.cursorTimer = setTimeout(() => {
380
380
  this.cursorTimer = null, this.flushCursor();
381
381
  }, l));
382
382
  }
@@ -384,8 +384,8 @@ class k extends p {
384
384
  this.pendingCursor && (this.send({ type: "cursor:move", position: this.pendingCursor }), this.lastCursorSend = Date.now(), this.pendingCursor = null);
385
385
  }
386
386
  // ── Presence ──
387
- updatePresence(t) {
388
- this.send({ type: "presence:update", status: t });
387
+ updatePresence(e) {
388
+ this.send({ type: "presence:update", status: e });
389
389
  }
390
390
  startHeartbeat() {
391
391
  this.heartbeatTimer || (this.heartbeatTimer = setInterval(() => {
@@ -396,76 +396,76 @@ class k extends p {
396
396
  this.heartbeatTimer && (clearInterval(this.heartbeatTimer), this.heartbeatTimer = null);
397
397
  }
398
398
  // ── Threads & Comments ──
399
- createThread(t, e = {}) {
399
+ createThread(e, t = {}) {
400
400
  const i = crypto.randomUUID();
401
401
  return this.send({
402
402
  type: "thread:create",
403
403
  id: i,
404
- body: t,
405
- mentions: e.mentions ?? [],
406
- position: e.position ?? null,
407
- attachmentIds: e.attachmentIds
404
+ body: e,
405
+ mentions: t.mentions ?? [],
406
+ position: t.position ?? null,
407
+ attachmentIds: t.attachmentIds
408
408
  }), i;
409
409
  }
410
- reply(t, e, i = [], s) {
411
- const a = crypto.randomUUID();
412
- return this.send({ type: "comment:create", threadId: t, id: a, body: e, mentions: i, attachmentIds: s }), a;
410
+ reply(e, t, i = [], s) {
411
+ const n = crypto.randomUUID();
412
+ return this.send({ type: "comment:create", threadId: e, id: n, body: t, mentions: i, attachmentIds: s }), n;
413
413
  }
414
- editComment(t, e, i = []) {
415
- this.send({ type: "comment:edit", commentId: t, body: e, mentions: i });
414
+ editComment(e, t, i = []) {
415
+ this.send({ type: "comment:edit", commentId: e, body: t, mentions: i });
416
416
  }
417
- deleteComment(t) {
418
- this.state.removeComment(t), this.send({ type: "comment:delete", commentId: t });
417
+ deleteComment(e) {
418
+ this.state.removeComment(e), this.send({ type: "comment:delete", commentId: e });
419
419
  }
420
- resolveThread(t, e = !0) {
421
- this.send({ type: "thread:resolve", threadId: t, resolved: e });
420
+ resolveThread(e, t = !0) {
421
+ this.send({ type: "thread:resolve", threadId: e, resolved: t });
422
422
  }
423
423
  // ── Reactions ──
424
- addReaction(t, e, i) {
425
- this.send({ type: "reaction:add", targetId: t, targetType: e, emoji: i });
424
+ addReaction(e, t, i) {
425
+ this.send({ type: "reaction:add", targetId: e, targetType: t, emoji: i });
426
426
  }
427
- removeReaction(t) {
428
- this.send({ type: "reaction:remove", reactionId: t });
427
+ removeReaction(e) {
428
+ this.send({ type: "reaction:remove", reactionId: e });
429
429
  }
430
430
  // ── Notifications ──
431
- markRead(t) {
432
- this.state.markNotificationRead(t), this.send({ type: "notification:read", notificationId: t });
431
+ markRead(e) {
432
+ this.state.markNotificationRead(e), this.send({ type: "notification:read", notificationId: e });
433
433
  }
434
434
  markAllRead() {
435
435
  this.state.markAllNotificationsRead(), this.send({ type: "notification:read-all" });
436
436
  }
437
437
  // ── Clicks ──
438
- performClick(t) {
439
- this.send({ type: "click:perform", position: t });
438
+ performClick(e) {
439
+ this.send({ type: "click:perform", position: e });
440
440
  }
441
441
  // ── Typing ──
442
- sendTyping(t) {
443
- this.send({ type: "typing:start", threadId: t });
442
+ sendTyping(e) {
443
+ this.send({ type: "typing:start", threadId: e });
444
444
  }
445
445
  // ── Viewport ──
446
- updateViewport(t) {
447
- this.send({ type: "viewport:update", ...t });
446
+ updateViewport(e) {
447
+ this.send({ type: "viewport:update", ...e });
448
448
  }
449
449
  // ── Selection ──
450
- updateSelection(t) {
451
- this.send({ type: "selection:update", selection: t });
450
+ updateSelection(e) {
451
+ this.send({ type: "selection:update", selection: e });
452
452
  }
453
453
  // ── Emoji Drops ──
454
- dropEmoji(t, e) {
455
- this.send({ type: "emoji:drop", emoji: t, position: e });
454
+ dropEmoji(e, t) {
455
+ this.send({ type: "emoji:drop", emoji: e, position: t });
456
456
  }
457
457
  // ── Drawing ──
458
- drawStroke(t, e, i) {
459
- this.send({ type: "draw:stroke", points: t, color: e, width: i });
458
+ drawStroke(e, t, i) {
459
+ this.send({ type: "draw:stroke", points: e, color: t, width: i });
460
460
  }
461
461
  clearDrawing() {
462
462
  this.send({ type: "draw:clear" });
463
463
  }
464
464
  // ── File Upload ──
465
- async uploadFile(t) {
466
- const e = typeof window < "u" ? window.location.origin : "", i = (this.config.endpoint ?? e).replace(/^ws(s?):/, "http$1:"), s = new FormData();
467
- s.append("file", t);
468
- const a = await fetch(`${i}/api/v1/upload`, {
465
+ async uploadFile(e) {
466
+ const t = typeof window < "u" ? window.location.origin : "", i = (this.config.endpoint ?? t).replace(/^ws(s?):/, "http$1:"), s = new FormData();
467
+ s.append("file", e);
468
+ const n = await fetch(`${i}/api/v1/upload`, {
469
469
  method: "POST",
470
470
  headers: {
471
471
  "X-Pulse-Key": this.config.apiKey,
@@ -473,16 +473,16 @@ class k extends p {
473
473
  },
474
474
  body: s
475
475
  });
476
- if (!a.ok) {
477
- const c = await a.json().catch(() => ({ error: "Upload failed" }));
476
+ if (!n.ok) {
477
+ const c = await n.json().catch(() => ({ error: "Upload failed" }));
478
478
  throw new Error(c.error ?? "Upload failed");
479
479
  }
480
- const o = await a.json();
480
+ const o = await n.json();
481
481
  return o.url && !o.url.startsWith("http") && (o.url = `${i}${o.url}`), o.thumbnailUrl && !o.thumbnailUrl.startsWith("http") && (o.thumbnailUrl = `${i}${o.thumbnailUrl}`), o;
482
482
  }
483
483
  // ── Presence control ──
484
- setAppearOffline(t) {
485
- t ? (this.stopHeartbeat(), this.send({ type: "presence:update", status: "idle" })) : (this.startHeartbeat(), this.send({ type: "presence:update", status: "online" }));
484
+ setAppearOffline(e) {
485
+ e ? (this.stopHeartbeat(), this.send({ type: "presence:update", status: "idle" })) : (this.startHeartbeat(), this.send({ type: "presence:update", status: "online" }));
486
486
  }
487
487
  // ── P2P ──
488
488
  /** Lazy-loaded P2P manager. Returns a promise that resolves to the P2PManager instance. */
@@ -492,9 +492,9 @@ class k extends p {
492
492
  );
493
493
  }
494
494
  async initP2P() {
495
- const t = await new Promise((a, o) => {
495
+ const e = await new Promise((n, o) => {
496
496
  if (this.state.user && this.state.p2pConfig) {
497
- a({
497
+ n({
498
498
  userId: this.state.user.id,
499
499
  p2pConfig: this.state.p2pConfig
500
500
  });
@@ -505,30 +505,33 @@ class k extends p {
505
505
  o(new Error("P2P is not enabled for this environment."));
506
506
  return;
507
507
  }
508
- a({
508
+ n({
509
509
  userId: this.state.user.id,
510
510
  p2pConfig: this.state.p2pConfig
511
511
  });
512
512
  });
513
513
  });
514
- let e = this.iceServers;
514
+ let t = this.iceServers;
515
515
  try {
516
- const a = await fetch(`${this.state.baseUrl}/api/v1/turn-credentials`, {
516
+ const n = await fetch(`${this.state.baseUrl}/api/v1/turn-credentials`, {
517
517
  headers: { Authorization: `Bearer ${this.config.token}` }
518
518
  });
519
- a.ok && (e = (await a.json()).iceServers);
519
+ n.ok && (t = (await n.json()).iceServers);
520
520
  } catch {
521
521
  }
522
- const { P2PManager: i } = await import("./P2PManager-B21XQVk_.js"), s = new i({
522
+ const { P2PManager: i } = await import("./P2PManager-CwR8sbb_.js"), s = new i({
523
523
  roomId: this.config.room,
524
- userId: t.userId,
524
+ userId: e.userId,
525
525
  baseUrl: this.state.baseUrl,
526
526
  authToken: this.config.token,
527
- p2pConfig: t.p2pConfig,
528
- iceServers: e,
529
- sendWS: (a) => this.send(a)
527
+ p2pConfig: e.p2pConfig,
528
+ iceServers: t,
529
+ sendWS: (n) => this.send(n)
530
530
  });
531
- return this._p2pInstance = s, await s.initialize(), s;
531
+ this._p2pInstance = s, await s.initialize();
532
+ for (const n of this.state.presence)
533
+ n.user.id !== e.userId && s.onPeerJoined(n.user.id);
534
+ return s;
532
535
  }
533
536
  }
534
537
  export {