@anonotf/connect 0.3.1 → 0.4.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
@@ -141,4 +141,4 @@ recorder.onstop = async () => {
141
141
 
142
142
  ```js
143
143
  client.disconnect();
144
- ```
144
+ ```
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@anonotf/connect",
3
- "version": "0.3.1",
4
- "description": "Client SDK for AnonOtF — calls, group calls, live streaming, chat, and voice notes.",
3
+ "version": "0.4.0",
4
+ "description": "Client SDK for AnonOtF — calls, group calls, live streaming, chat, and voice notes, without touching raw WebRTC or Socket.IO directly.",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
7
7
  "files": ["src"],
package/src/calls.js CHANGED
@@ -35,6 +35,8 @@ export class Calls {
35
35
  this._localStream = null;
36
36
  this.roomId = null; // set once a call is accepted/joined — null when idle
37
37
  this._inCall = false; // true from accept/connect onward, used to decide whether to mesh-connect to newcomers
38
+ this._screenTrack = null; // active screen-capture track, if currently sharing
39
+ this._hadCameraBeforeShare = false; // so stopScreenShare() knows whether to restore the camera or just go video-off
38
40
 
39
41
  this._socket.on('incoming-call', (data) => this._emit('incomingCall', data));
40
42
  this._socket.on('call-accepted', (data) => this._onCallAccepted(data));
@@ -140,6 +142,61 @@ export class Calls {
140
142
  }
141
143
  }
142
144
 
145
+ /**
146
+ * Starts sharing your screen instead of your camera — swaps the
147
+ * outgoing video track on every peer connection in the mesh, same
148
+ * mechanism as camera flip. Remembers whether you had a camera
149
+ * track running so stopScreenShare() can restore it afterward.
150
+ * Audio is untouched either way.
151
+ *
152
+ * Fires 'screenShareStarted'/'screenShareEnded' — the latter also
153
+ * fires automatically if the user stops sharing from the browser's
154
+ * own "Stop sharing" control rather than your UI.
155
+ */
156
+ async startScreenShare() {
157
+ if (this._screenTrack) return; // already sharing
158
+ const displayStream = await navigator.mediaDevices.getDisplayMedia({ video: true, audio: false });
159
+ const screenTrack = displayStream.getVideoTracks()[0];
160
+
161
+ this._hadCameraBeforeShare = !!this._localStream?.getVideoTracks().length;
162
+ this._screenTrack = screenTrack;
163
+
164
+ await this.replaceVideoTrack(screenTrack);
165
+ this._emit('screenShareStarted');
166
+
167
+ // Browser's native "Stop sharing" UI ends the track directly —
168
+ // this catches that case so state stays accurate even if the
169
+ // person never calls stopScreenShare() themselves.
170
+ screenTrack.onended = () => {
171
+ if (this._screenTrack === screenTrack) this.stopScreenShare();
172
+ };
173
+ }
174
+
175
+ /**
176
+ * Stops screen sharing and restores your camera (if you had one
177
+ * running before sharing started) across the whole mesh.
178
+ */
179
+ async stopScreenShare() {
180
+ if (!this._screenTrack) return;
181
+ this._screenTrack.stop();
182
+ this._screenTrack = null;
183
+
184
+ if (this._hadCameraBeforeShare) {
185
+ const cameraStream = await navigator.mediaDevices.getUserMedia({ video: true });
186
+ await this.replaceVideoTrack(cameraStream.getVideoTracks()[0]);
187
+ } else if (this._localStream) {
188
+ // Wasn't sending video before sharing (audio-only call) — go
189
+ // back to that, rather than silently turning video on.
190
+ const track = this._localStream.getVideoTracks()[0];
191
+ if (track) { this._localStream.removeTrack(track); track.stop(); }
192
+ }
193
+ this._emit('screenShareEnded');
194
+ }
195
+
196
+ get isScreenSharing() {
197
+ return !!this._screenTrack;
198
+ }
199
+
143
200
  /** Mute/unmute your own mic across the whole mesh — just disables the local track, no renegotiation needed. */
144
201
  setMicEnabled(enabled) {
145
202
  this._localStream?.getAudioTracks().forEach((t) => { t.enabled = enabled; });
@@ -285,6 +342,8 @@ export class Calls {
285
342
  _cleanupAll() {
286
343
  this._localStream?.getTracks().forEach((t) => t.stop());
287
344
  this._localStream = null;
345
+ this._screenTrack?.stop();
346
+ this._screenTrack = null;
288
347
  for (const pc of this._peers.values()) pc.close();
289
348
  this._peers.clear();
290
349
  this.roomId = null;