react-peer-chat 0.8.5 → 0.8.6

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
@@ -317,12 +317,13 @@ that are listed in [useChat Hook API Reference](#usechat-hook-1) along with the
317
317
  | `props` | [`DivProps`](#divprops) | No | - | Props to customize the `<Chat>` component. |
318
318
  | `children` | [`Children`](#children) | No | - | See [usage with FaC](#full-customization) |
319
319
 
320
- ### Types
320
+ ## Types
321
321
 
322
- #### Children
322
+ ### Children
323
323
 
324
324
  ```typescript
325
- import { ReactNode } from "react";
325
+ import type { ReactNode } from "react";
326
+
326
327
  type RemotePeers = { [id: string]: string };
327
328
  type Message = {
328
329
  id: string;
@@ -338,10 +339,11 @@ type ChildrenOptions = {
338
339
  type Children = (childrenOptions: ChildrenOptions) => ReactNode;
339
340
  ```
340
341
 
341
- #### DialogOptions
342
+ ### DialogOptions
342
343
 
343
344
  ```typescript
344
- import { CSSProperties } from "react";
345
+ import type { CSSProperties } from "react";
346
+
345
347
  type DialogPosition = "left" | "center" | "right";
346
348
  type DialogOptions = {
347
349
  position: DialogPosition;
@@ -349,20 +351,21 @@ type DialogOptions = {
349
351
  };
350
352
  ```
351
353
 
352
- #### DivProps
354
+ ### DivProps
353
355
 
354
356
  ```typescript
355
- import { DetailedHTMLProps, HTMLAttributes } from "react";
357
+ import type { DetailedHTMLProps, HTMLAttributes } from "react";
358
+
356
359
  type DivProps = DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
357
360
  ```
358
361
 
359
- #### ErrorHandler
362
+ ### ErrorHandler
360
363
 
361
364
  ```typescript
362
365
  type ErrorHandler = () => void;
363
366
  ```
364
367
 
365
- #### MessageEventHandler
368
+ ### MessageEventHandler
366
369
 
367
370
  ```typescript
368
371
  type Message = {
@@ -372,10 +375,10 @@ type Message = {
372
375
  type MessageEventHandler = (message: Message) => void;
373
376
  ```
374
377
 
375
- #### PeerOptions
378
+ ### PeerOptions
376
379
 
377
380
  ```typescript
378
- import { PeerOptions } from "peerjs";
381
+ import type { PeerOptions } from "peerjs";
379
382
  ```
380
383
 
381
384
  ## License
@@ -0,0 +1,203 @@
1
+ import { getStorage, setStorage } from './chunk-ZYFPSCFE.js';
2
+ import { __spreadValues, __spreadProps } from './chunk-LNEKYYG7.js';
3
+ import { useState, useRef, useMemo, useEffect } from 'react';
4
+
5
+ // src/constants.ts
6
+ var turnAccounts = [
7
+ { username: "70061a377b51f3a3d01c11e3", credential: "lHV4NYJ5Rfl5JNa9" },
8
+ { username: "13b19eb65bbf6e9f96d64b72", credential: "7R9P/+7y7Q516Etv" },
9
+ { username: "3469603f5cdc7ca4a1e891ae", credential: "/jMyLSDbbcgqpVQv" },
10
+ { username: "a7926f4dcc4a688d41f89752", credential: "ZYM8jFYeb8bQkL+N" },
11
+ { username: "0be25ab7f61d9d733ba94809", credential: "hiiSwWVch+ftt3SX" },
12
+ { username: "3c25ba948daeab04f9b66187", credential: "FQB3GQwd27Y0dPeK" }
13
+ ];
14
+ var defaults = {
15
+ config: {
16
+ iceServers: [
17
+ {
18
+ urls: ["stun:stun.l.google.com:19302", "stun:stun.relay.metered.ca:80"]
19
+ }
20
+ ].concat(
21
+ turnAccounts.map((account) => __spreadValues({
22
+ urls: [
23
+ "turn:standard.relay.metered.ca:80",
24
+ "turn:standard.relay.metered.ca:80?transport=tcp",
25
+ "turn:standard.relay.metered.ca:443",
26
+ "turns:standard.relay.metered.ca:443?transport=tcp"
27
+ ]
28
+ }, account))
29
+ )
30
+ },
31
+ peerOptions: {},
32
+ remotePeerId: []
33
+ };
34
+
35
+ // src/lib/connection.ts
36
+ function closeConnection(conn) {
37
+ conn.removeAllListeners();
38
+ conn.close();
39
+ }
40
+
41
+ // src/lib/utils.ts
42
+ var addPrefix = (str) => `rpc-${str}`;
43
+
44
+ // src/lib/react.ts
45
+ function isSetStateFunction(v) {
46
+ return typeof v === "function";
47
+ }
48
+
49
+ // src/hooks.ts
50
+ var { config: defaultConfig, peerOptions: defaultPeerOptions, remotePeerId: defaultRemotePeerId } = defaults;
51
+ function useChat({
52
+ peerId,
53
+ name = "Anonymous User",
54
+ remotePeerId = defaultRemotePeerId,
55
+ peerOptions = defaultPeerOptions,
56
+ text = true,
57
+ recoverChat = false,
58
+ audio: allowed = true,
59
+ onError = () => alert("Browser not supported! Try some other browser."),
60
+ onMicError = () => alert("Microphone not accessible!"),
61
+ onMessageSent,
62
+ onMessageReceived
63
+ }) {
64
+ const [peer, setPeer] = useState();
65
+ const [audio, setAudio] = useAudio(allowed);
66
+ const connRef = useRef({});
67
+ const localStreamRef = useRef(null);
68
+ const audioStreamRef = useRef(null);
69
+ const callsRef = useRef({});
70
+ const [messages, setMessages, addMessage] = useMessages();
71
+ const [remotePeers, setRemotePeers] = useStorage("rpc-remote-peer", {});
72
+ const { completePeerId, completeRemotePeerIds } = useMemo(() => {
73
+ const remotePeerIds = Array.isArray(remotePeerId) ? remotePeerId : [remotePeerId];
74
+ return { completePeerId: addPrefix(peerId), completeRemotePeerIds: remotePeerIds.map(addPrefix) };
75
+ }, [peerId]);
76
+ function handleConnection(conn) {
77
+ connRef.current[conn.peer] = conn;
78
+ conn.on("open", () => {
79
+ conn.on("data", ({ message, messages: messages2, remotePeerName, type }) => {
80
+ if (type === "message") receiveMessage(message);
81
+ else if (type === "init") {
82
+ setRemotePeers((prev) => __spreadProps(__spreadValues({}, prev), { [conn.peer]: remotePeerName }));
83
+ if (recoverChat) setMessages((old) => messages2.length > old.length ? messages2 : old);
84
+ }
85
+ });
86
+ conn.send({ type: "init", remotePeerName: name, messages });
87
+ });
88
+ conn.on("close", conn.removeAllListeners);
89
+ }
90
+ function handleError() {
91
+ setAudio(false);
92
+ onMicError();
93
+ }
94
+ function handleRemoteStream(remoteStream) {
95
+ if (audioStreamRef.current) audioStreamRef.current.srcObject = remoteStream;
96
+ }
97
+ function receiveMessage(message) {
98
+ addMessage(message);
99
+ onMessageReceived == null ? void 0 : onMessageReceived(message);
100
+ }
101
+ function sendMessage(message) {
102
+ addMessage(message);
103
+ Object.values(connRef.current).forEach((conn) => conn.send({ type: "message", message }));
104
+ onMessageSent == null ? void 0 : onMessageSent(message);
105
+ }
106
+ useEffect(() => {
107
+ if (!text && !audio) return;
108
+ import('peerjs').then(
109
+ ({
110
+ Peer,
111
+ util: {
112
+ supports: { audioVideo, data }
113
+ }
114
+ }) => {
115
+ if (!data || !audioVideo) return onError();
116
+ const peer2 = new Peer(completePeerId, __spreadValues({ config: defaultConfig }, peerOptions));
117
+ peer2.on("connection", handleConnection);
118
+ setPeer(peer2);
119
+ }
120
+ );
121
+ return () => {
122
+ setPeer((prev) => {
123
+ prev == null ? void 0 : prev.removeAllListeners();
124
+ prev == null ? void 0 : prev.destroy();
125
+ return void 0;
126
+ });
127
+ };
128
+ }, [completePeerId]);
129
+ useEffect(() => {
130
+ if (!text || !peer) return;
131
+ const handleOpen = () => completeRemotePeerIds.forEach((id) => handleConnection(peer.connect(id)));
132
+ if (peer.open) handleOpen();
133
+ else peer.once("open", handleOpen);
134
+ return () => {
135
+ Object.values(connRef.current).forEach(closeConnection);
136
+ connRef.current = {};
137
+ };
138
+ }, [text, peer]);
139
+ useEffect(() => {
140
+ if (!audio || !peer) return;
141
+ const setupAudio = () => navigator.mediaDevices.getUserMedia({
142
+ video: false,
143
+ audio: {
144
+ autoGainControl: false,
145
+ // Disable automatic gain control
146
+ noiseSuppression: true,
147
+ // Enable noise suppression
148
+ echoCancellation: true
149
+ // Enable echo cancellation
150
+ }
151
+ }).then((stream) => {
152
+ localStreamRef.current = stream;
153
+ completeRemotePeerIds.forEach((id) => {
154
+ const call = peer.call(id, stream);
155
+ call.on("stream", handleRemoteStream);
156
+ call.on("close", call.removeAllListeners);
157
+ callsRef.current[id] = call;
158
+ });
159
+ peer.on("call", (call) => {
160
+ call.answer(stream);
161
+ call.on("stream", handleRemoteStream);
162
+ call.on("close", call.removeAllListeners);
163
+ callsRef.current[call.peer] = call;
164
+ });
165
+ }).catch(handleError);
166
+ if (peer.open) setupAudio();
167
+ else peer.once("open", setupAudio);
168
+ return () => {
169
+ var _a;
170
+ (_a = localStreamRef.current) == null ? void 0 : _a.getTracks().forEach((track) => track.stop());
171
+ localStreamRef.current = null;
172
+ Object.values(callsRef.current).forEach(closeConnection);
173
+ callsRef.current = {};
174
+ };
175
+ }, [audio, peer]);
176
+ return { peerId: completePeerId, audioStreamRef, remotePeers, messages, sendMessage, audio, setAudio };
177
+ }
178
+ function useMessages() {
179
+ const [messages, setMessages] = useStorage("rpc-messages", []);
180
+ const addMessage = (message) => setMessages((prev) => prev.concat(message));
181
+ return [messages, setMessages, addMessage];
182
+ }
183
+ function useStorage(key, initialValue, local = false) {
184
+ const [storedValue, setStoredValue] = useState(() => {
185
+ if (typeof window === "undefined") return initialValue;
186
+ return getStorage(key, initialValue, local);
187
+ });
188
+ const setValue = (value) => {
189
+ setStoredValue((prev) => {
190
+ const next = isSetStateFunction(value) ? value(prev) : value;
191
+ setStorage(key, next, local);
192
+ return next;
193
+ });
194
+ };
195
+ return [storedValue, setValue];
196
+ }
197
+ function useAudio(allowed) {
198
+ const [audio, setAudio] = useStorage("rpc-audio", false, true);
199
+ const enabled = audio && allowed;
200
+ return [enabled, setAudio];
201
+ }
202
+
203
+ export { useAudio, useChat, useMessages, useStorage };
@@ -1,36 +1,6 @@
1
+ import { __spreadValues } from './chunk-LNEKYYG7.js';
1
2
  import React from 'react';
2
3
 
3
- var __defProp = Object.defineProperty;
4
- var __defProps = Object.defineProperties;
5
- var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
6
- var __getOwnPropSymbols = Object.getOwnPropertySymbols;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __propIsEnum = Object.prototype.propertyIsEnumerable;
9
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
10
- var __spreadValues = (a, b) => {
11
- for (var prop in b || (b = {}))
12
- if (__hasOwnProp.call(b, prop))
13
- __defNormalProp(a, prop, b[prop]);
14
- if (__getOwnPropSymbols)
15
- for (var prop of __getOwnPropSymbols(b)) {
16
- if (__propIsEnum.call(b, prop))
17
- __defNormalProp(a, prop, b[prop]);
18
- }
19
- return a;
20
- };
21
- var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
22
- var __objRest = (source, exclude) => {
23
- var target = {};
24
- for (var prop in source)
25
- if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
26
- target[prop] = source[prop];
27
- if (source != null && __getOwnPropSymbols)
28
- for (var prop of __getOwnPropSymbols(source)) {
29
- if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
30
- target[prop] = source[prop];
31
- }
32
- return target;
33
- };
34
4
  function BiSolidMessageDetail(props) {
35
5
  return /* @__PURE__ */ React.createElement("span", __spreadValues({ className: "rpc-icon-container" }, props), /* @__PURE__ */ React.createElement("svg", { viewBox: "0 0 24 24", width: "1.25rem", height: "1.25rem" }, /* @__PURE__ */ React.createElement("path", { d: "M20 2H4c-1.103 0-2 .894-2 1.992v12.016C2 17.106 2.897 18 4 18h3v4l6.351-4H20c1.103 0 2-.894 2-1.992V3.992A1.998 1.998 0 0 0 20 2zm-6 11H7v-2h7v2zm3-4H7V7h10v2z" })));
36
6
  }
@@ -55,4 +25,4 @@ function BsFillMicMuteFill(props) {
55
25
  return /* @__PURE__ */ React.createElement("span", __spreadValues({ className: "rpc-icon-container" }, props), /* @__PURE__ */ React.createElement("svg", { viewBox: "0 0 16 16", fill: "currentColor", width: "1.25rem", height: "1.25rem" }, /* @__PURE__ */ React.createElement("path", { d: "M13 8c0 .564-.094 1.107-.266 1.613l-.814-.814A4.02 4.02 0 0 0 12 8V7a.5.5 0 0 1 1 0v1zm-5 4c.818 0 1.578-.245 2.212-.667l.718.719a4.973 4.973 0 0 1-2.43.923V15h3a.5.5 0 0 1 0 1h-7a.5.5 0 0 1 0-1h3v-2.025A5 5 0 0 1 3 8V7a.5.5 0 0 1 1 0v1a4 4 0 0 0 4 4zm3-9v4.879L5.158 2.037A3.001 3.001 0 0 1 11 3z" }), /* @__PURE__ */ React.createElement("path", { d: "M9.486 10.607 5 6.12V8a3 3 0 0 0 4.486 2.607zm-7.84-9.253 12 12 .708-.708-12-12-.708.708z" })));
56
26
  }
57
27
 
58
- export { BiSolidMessageDetail, BiSolidMessageX, BsFillMicFill, BsFillMicMuteFill, GrSend, __objRest, __spreadProps, __spreadValues };
28
+ export { BiSolidMessageDetail, BiSolidMessageX, BsFillMicFill, BsFillMicMuteFill, GrSend };
@@ -0,0 +1,33 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ var __objRest = (source, exclude) => {
21
+ var target = {};
22
+ for (var prop in source)
23
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
24
+ target[prop] = source[prop];
25
+ if (source != null && __getOwnPropSymbols)
26
+ for (var prop of __getOwnPropSymbols(source)) {
27
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
28
+ target[prop] = source[prop];
29
+ }
30
+ return target;
31
+ };
32
+
33
+ export { __objRest, __spreadProps, __spreadValues };
@@ -0,0 +1,81 @@
1
+ import { useChat } from './chunk-GT3RG6VA.js';
2
+ import { BiSolidMessageX, BiSolidMessageDetail, GrSend, BsFillMicFill, BsFillMicMuteFill } from './chunk-JJPIWKLG.js';
3
+ import { __objRest, __spreadValues } from './chunk-LNEKYYG7.js';
4
+ import React, { useRef, useState, useEffect } from 'react';
5
+
6
+ // src/styles.css
7
+ function injectStyle(css) {
8
+ if (typeof document === "undefined") return;
9
+ const head = document.head || document.getElementsByTagName("head")[0];
10
+ const style = document.createElement("style");
11
+ style.type = "text/css";
12
+ if (head.firstChild) {
13
+ head.insertBefore(style, head.firstChild);
14
+ } else {
15
+ head.appendChild(style);
16
+ }
17
+ if (style.styleSheet) {
18
+ style.styleSheet.cssText = css;
19
+ } else {
20
+ style.appendChild(document.createTextNode(css));
21
+ }
22
+ }
23
+ injectStyle('.rpc-font {\n font-family:\n "Trebuchet MS",\n "Lucida Sans Unicode",\n "Lucida Grande",\n "Lucida Sans",\n Arial,\n sans-serif;\n}\n.rpc-main {\n display: flex;\n align-items: center;\n column-gap: 0.5rem;\n}\n.rpc-dialog-container {\n position: relative;\n}\n.rpc-notification {\n position: relative;\n}\n.rpc-notification .rpc-badge {\n background-color: red;\n position: absolute;\n top: 0;\n right: 0;\n width: 6px;\n aspect-ratio: 1;\n border-radius: 100%;\n}\n.rpc-dialog {\n position: absolute;\n background-color: black;\n color: white;\n padding: 0.4rem 0 0.25rem 0;\n border-radius: 0.4rem;\n font-size: small;\n}\n.rpc-position-left {\n left: 0.6rem;\n translate: -100%;\n}\n.rpc-position-center {\n left: 0.5rem;\n translate: -50%;\n}\n.rpc-position-right {\n left: 0.3rem;\n}\n.rpc-hr {\n margin: 0.25rem 0;\n border-color: rgba(255, 255, 255, 0.7);\n}\n.rpc-message-container {\n height: 7rem;\n overflow-y: scroll;\n padding-inline: 0.5rem;\n margin-bottom: 0.25rem;\n}\n.rpc-message-container::-webkit-scrollbar {\n width: 2.5px;\n}\n.rpc-message-container::-webkit-scrollbar-track {\n background: gray;\n}\n.rpc-message-container::-webkit-scrollbar-thumb {\n background-color: white;\n}\n.rpc-heading {\n text-align: center;\n font-size: medium;\n font-weight: bold;\n padding-inline: 0.5rem;\n}\n.rpc-input-container {\n display: flex;\n width: 15rem;\n max-width: 90vw;\n column-gap: 0.25rem;\n padding: 0.25rem 0.5rem;\n}\n.rpc-input {\n color: white;\n width: 100%;\n background-color: black;\n border: none;\n outline: 1px solid rgba(255, 255, 255, 0.8);\n border-radius: 0.25rem;\n padding: 0.3rem 0.25rem;\n}\n.rpc-input::placeholder {\n color: rgba(255, 255, 255, 0.7);\n}\n.rpc-input:focus {\n outline: 2px solid white;\n}\n.rpc-button {\n all: unset;\n display: flex;\n}\n.rpc-icon-container {\n display: flex;\n align-items: center;\n}\n.rpc-invert {\n filter: invert(100%);\n}\n');
24
+
25
+ // src/components.tsx
26
+ function Chat(_a) {
27
+ var _b = _a, { text = true, audio = true, onMessageReceived, dialogOptions, props = {}, children } = _b, hookProps = __objRest(_b, ["text", "audio", "onMessageReceived", "dialogOptions", "props", "children"]);
28
+ const _a2 = useChat(__spreadValues({
29
+ text,
30
+ audio,
31
+ onMessageReceived: modifiedOnMessageReceived
32
+ }, hookProps)), { peerId, audioStreamRef } = _a2, childrenOptions = __objRest(_a2, ["peerId", "audioStreamRef"]);
33
+ const { remotePeers, messages, sendMessage, audio: audioEnabled, setAudio } = childrenOptions;
34
+ const containerRef = useRef(null);
35
+ const [dialog, setDialog] = useState(false);
36
+ const dialogRef = useRef(null);
37
+ const inputRef = useRef(null);
38
+ const [notification, setNotification] = useState(false);
39
+ function modifiedOnMessageReceived(message) {
40
+ var _a3;
41
+ if (!((_a3 = dialogRef.current) == null ? void 0 : _a3.open)) setNotification(true);
42
+ onMessageReceived == null ? void 0 : onMessageReceived(message);
43
+ }
44
+ useEffect(() => {
45
+ var _a3, _b2;
46
+ if (dialog) (_a3 = dialogRef.current) == null ? void 0 : _a3.show();
47
+ else (_b2 = dialogRef.current) == null ? void 0 : _b2.close();
48
+ }, [dialog]);
49
+ useEffect(() => {
50
+ const container = containerRef.current;
51
+ if (container) container.scrollTop = container.scrollHeight;
52
+ }, [dialog, remotePeers, messages]);
53
+ return /* @__PURE__ */ React.createElement("div", __spreadValues({ className: "rpc-main rpc-font" }, props), typeof children === "function" ? children(childrenOptions) : /* @__PURE__ */ React.createElement(React.Fragment, null, text && /* @__PURE__ */ React.createElement("div", { className: "rpc-dialog-container" }, dialog ? /* @__PURE__ */ React.createElement(BiSolidMessageX, { title: "Close chat", onClick: () => setDialog(false) }) : /* @__PURE__ */ React.createElement("div", { className: "rpc-notification" }, /* @__PURE__ */ React.createElement(
54
+ BiSolidMessageDetail,
55
+ {
56
+ title: "Open chat",
57
+ onClick: () => {
58
+ setNotification(false);
59
+ setDialog(true);
60
+ }
61
+ }
62
+ ), notification && /* @__PURE__ */ React.createElement("span", { className: "rpc-badge" })), /* @__PURE__ */ React.createElement("dialog", { ref: dialogRef, className: `${dialog ? "rpc-dialog" : ""} rpc-position-${(dialogOptions == null ? void 0 : dialogOptions.position) || "center"}`, style: dialogOptions == null ? void 0 : dialogOptions.style }, /* @__PURE__ */ React.createElement("div", { className: "rpc-heading" }, "Chat"), /* @__PURE__ */ React.createElement("hr", { className: "rpc-hr" }), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { ref: containerRef, className: "rpc-message-container" }, messages.map(({ id, text: text2 }, i) => /* @__PURE__ */ React.createElement("div", { key: i }, /* @__PURE__ */ React.createElement("strong", null, id === peerId ? "You" : remotePeers[id], ": "), /* @__PURE__ */ React.createElement("span", null, text2)))), /* @__PURE__ */ React.createElement("hr", { className: "rpc-hr" }), /* @__PURE__ */ React.createElement(
63
+ "form",
64
+ {
65
+ className: "rpc-input-container",
66
+ onSubmit: (e) => {
67
+ var _a3;
68
+ e.preventDefault();
69
+ const text2 = (_a3 = inputRef.current) == null ? void 0 : _a3.value;
70
+ if (text2) {
71
+ inputRef.current.value = "";
72
+ sendMessage({ id: peerId, text: text2 });
73
+ }
74
+ }
75
+ },
76
+ /* @__PURE__ */ React.createElement("input", { ref: inputRef, className: "rpc-input rpc-font", placeholder: "Enter a message" }),
77
+ /* @__PURE__ */ React.createElement("button", { type: "submit", className: "rpc-button" }, /* @__PURE__ */ React.createElement(GrSend, { title: "Send message" }))
78
+ )))), audio && /* @__PURE__ */ React.createElement("button", { className: "rpc-button", onClick: () => setAudio(!audioEnabled) }, audioEnabled ? /* @__PURE__ */ React.createElement(BsFillMicFill, { title: "Turn mic off" }) : /* @__PURE__ */ React.createElement(BsFillMicMuteFill, { title: "Turn mic on" }))), audio && audioEnabled && /* @__PURE__ */ React.createElement("audio", { ref: audioStreamRef, autoPlay: true, style: { display: "none" } }));
79
+ }
80
+
81
+ export { Chat };
@@ -0,0 +1,22 @@
1
+ // src/lib/storage.ts
2
+ var getStorageInstance = (local = true) => local ? localStorage : sessionStorage;
3
+ var removeStorage = (key, local = true) => getStorageInstance(local).removeItem(key);
4
+ var clearChat = () => {
5
+ removeStorage("rpc-remote-peer");
6
+ removeStorage("rpc-messages");
7
+ };
8
+ var setStorage = (key, value, local = true) => getStorageInstance(local).setItem(key, JSON.stringify(value));
9
+ function getStorage(key, fallbackValue, local = true) {
10
+ const value = getStorageInstance(local).getItem(key);
11
+ if (value) {
12
+ try {
13
+ return JSON.parse(value);
14
+ } catch (e) {
15
+ removeStorage(key, local);
16
+ }
17
+ }
18
+ if (fallbackValue !== void 0) setStorage(key, fallbackValue, local);
19
+ return fallbackValue;
20
+ }
21
+
22
+ export { clearChat, getStorage, removeStorage, setStorage };
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ import { ChatProps } from './types.js';
3
+ import 'peerjs';
4
+
5
+ declare function Chat({ text, audio, onMessageReceived, dialogOptions, props, children, ...hookProps }: ChatProps): React.JSX.Element;
6
+
7
+ export { Chat as default };
@@ -0,0 +1,5 @@
1
+ export { Chat as default } from './chunks/chunk-YDZ27MG7.js';
2
+ import './chunks/chunk-GT3RG6VA.js';
3
+ import './chunks/chunk-JJPIWKLG.js';
4
+ import './chunks/chunk-ZYFPSCFE.js';
5
+ import './chunks/chunk-LNEKYYG7.js';
@@ -0,0 +1,11 @@
1
+ import { SetStateAction } from 'react';
2
+ import { UseChatProps, UseChatReturn, Message } from './types.js';
3
+ import 'peerjs';
4
+
5
+ declare function useChat({ peerId, name, remotePeerId, peerOptions, text, recoverChat, audio: allowed, onError, onMicError, onMessageSent, onMessageReceived, }: UseChatProps): UseChatReturn;
6
+ declare function useMessages(): readonly [Message[], (value: SetStateAction<Message[]>) => void, (message: Message) => void];
7
+ declare function useStorage<T>(key: string, initialValue: T, local?: boolean): readonly [T, (value: SetStateAction<T>) => void];
8
+ declare function useStorage<T>(key: string, initialValue?: T, local?: boolean): readonly [T | undefined, (value: SetStateAction<T | undefined>) => void];
9
+ declare function useAudio(allowed: boolean): readonly [boolean, (value: SetStateAction<boolean>) => void];
10
+
11
+ export { useAudio, useChat, useMessages, useStorage };
package/dist/hooks.js ADDED
@@ -0,0 +1,3 @@
1
+ export { useAudio, useChat, useMessages, useStorage } from './chunks/chunk-GT3RG6VA.js';
2
+ import './chunks/chunk-ZYFPSCFE.js';
3
+ import './chunks/chunk-LNEKYYG7.js';
package/dist/icons.js CHANGED
@@ -1 +1,2 @@
1
- export { BiSolidMessageDetail, BiSolidMessageX, BsFillMicFill, BsFillMicMuteFill, GrSend } from './chunks/chunk-NXKKI6WY.js';
1
+ export { BiSolidMessageDetail, BiSolidMessageX, BsFillMicFill, BsFillMicMuteFill, GrSend } from './chunks/chunk-JJPIWKLG.js';
2
+ import './chunks/chunk-LNEKYYG7.js';
package/dist/index.d.ts CHANGED
@@ -1,11 +1,6 @@
1
- import React from 'react';
2
- import { UseChatProps, UseChatReturn, ChatProps } from './types.js';
1
+ export { default } from './components.js';
2
+ export { useChat } from './hooks.js';
3
+ export { clearChat } from './lib/storage.js';
4
+ import 'react';
5
+ import './types.js';
3
6
  import 'peerjs';
4
-
5
- declare function useChat({ peerId, name, remotePeerId, peerOptions, text, recoverChat, audio: allowed, onError, onMicError, onMessageSent, onMessageReceived, }: UseChatProps): UseChatReturn;
6
-
7
- declare const clearChat: () => void;
8
-
9
- declare function Chat({ text, audio, onMessageReceived, dialogOptions, props, children, ...hookProps }: ChatProps): React.JSX.Element;
10
-
11
- export { clearChat, Chat as default, useChat };
package/dist/index.js CHANGED
@@ -1,298 +1,5 @@
1
- import { __spreadValues, __objRest, BiSolidMessageX, BiSolidMessageDetail, GrSend, BsFillMicFill, BsFillMicMuteFill, __spreadProps } from './chunks/chunk-NXKKI6WY.js';
2
- import React, { useState, useRef, useMemo, useEffect } from 'react';
3
-
4
- // src/constants.ts
5
- var turnAccounts = [
6
- { username: "70061a377b51f3a3d01c11e3", credential: "lHV4NYJ5Rfl5JNa9" },
7
- { username: "13b19eb65bbf6e9f96d64b72", credential: "7R9P/+7y7Q516Etv" },
8
- { username: "3469603f5cdc7ca4a1e891ae", credential: "/jMyLSDbbcgqpVQv" },
9
- { username: "a7926f4dcc4a688d41f89752", credential: "ZYM8jFYeb8bQkL+N" },
10
- { username: "0be25ab7f61d9d733ba94809", credential: "hiiSwWVch+ftt3SX" },
11
- { username: "3c25ba948daeab04f9b66187", credential: "FQB3GQwd27Y0dPeK" }
12
- ];
13
- var defaults = {
14
- config: {
15
- iceServers: [
16
- {
17
- urls: ["stun:stun.l.google.com:19302", "stun:stun.relay.metered.ca:80"]
18
- }
19
- ].concat(
20
- turnAccounts.map((account) => __spreadValues({
21
- urls: [
22
- "turn:standard.relay.metered.ca:80",
23
- "turn:standard.relay.metered.ca:80?transport=tcp",
24
- "turn:standard.relay.metered.ca:443",
25
- "turns:standard.relay.metered.ca:443?transport=tcp"
26
- ]
27
- }, account))
28
- )
29
- },
30
- peerOptions: {},
31
- remotePeerId: []
32
- };
33
-
34
- // src/lib/connection.ts
35
- function closeConnection(conn) {
36
- conn.removeAllListeners();
37
- conn.close();
38
- }
39
-
40
- // src/lib/storage.ts
41
- var getStorageInstance = (local = true) => local ? localStorage : sessionStorage;
42
- var removeStorage = (key, local = true) => getStorageInstance(local).removeItem(key);
43
- var clearChat = () => {
44
- removeStorage("rpc-remote-peer");
45
- removeStorage("rpc-messages");
46
- };
47
- var setStorage = (key, value, local = true) => getStorageInstance(local).setItem(key, JSON.stringify(value));
48
- function getStorage(key, fallbackValue, local = true) {
49
- const value = getStorageInstance(local).getItem(key);
50
- if (value) {
51
- try {
52
- return JSON.parse(value);
53
- } catch (e) {
54
- removeStorage(key, local);
55
- }
56
- }
57
- if (fallbackValue !== void 0) setStorage(key, fallbackValue, local);
58
- return fallbackValue;
59
- }
60
-
61
- // src/lib/utils.ts
62
- var addPrefix = (str) => `rpc-${str}`;
63
-
64
- // src/lib/react.ts
65
- function isSetStateFunction(v) {
66
- return typeof v === "function";
67
- }
68
-
69
- // src/hooks.ts
70
- var { config: defaultConfig, peerOptions: defaultPeerOptions, remotePeerId: defaultRemotePeerId } = defaults;
71
- function useChat({
72
- peerId,
73
- name = "Anonymous User",
74
- remotePeerId = defaultRemotePeerId,
75
- peerOptions = defaultPeerOptions,
76
- text = true,
77
- recoverChat = false,
78
- audio: allowed = true,
79
- onError = () => alert("Browser not supported! Try some other browser."),
80
- onMicError = () => alert("Microphone not accessible!"),
81
- onMessageSent,
82
- onMessageReceived
83
- }) {
84
- const [peer, setPeer] = useState();
85
- const [audio, setAudio] = useAudio(allowed);
86
- const connRef = useRef({});
87
- const localStreamRef = useRef(null);
88
- const audioStreamRef = useRef(null);
89
- const callsRef = useRef({});
90
- const [messages, setMessages, addMessage] = useMessages();
91
- const [remotePeers, setRemotePeers] = useStorage("rpc-remote-peer", {});
92
- const { completePeerId, completeRemotePeerIds } = useMemo(() => {
93
- const remotePeerIds = Array.isArray(remotePeerId) ? remotePeerId : [remotePeerId];
94
- return { completePeerId: addPrefix(peerId), completeRemotePeerIds: remotePeerIds.map(addPrefix) };
95
- }, [peerId]);
96
- function handleConnection(conn) {
97
- connRef.current[conn.peer] = conn;
98
- conn.on("open", () => {
99
- conn.on("data", ({ message, messages: messages2, remotePeerName, type }) => {
100
- if (type === "message") receiveMessage(message);
101
- else if (type === "init") {
102
- setRemotePeers((prev) => __spreadProps(__spreadValues({}, prev), { [conn.peer]: remotePeerName }));
103
- if (recoverChat) setMessages((old) => messages2.length > old.length ? messages2 : old);
104
- }
105
- });
106
- conn.send({ type: "init", remotePeerName: name, messages });
107
- });
108
- conn.on("close", conn.removeAllListeners);
109
- }
110
- function handleError() {
111
- setAudio(false);
112
- onMicError();
113
- }
114
- function handleRemoteStream(remoteStream) {
115
- if (audioStreamRef.current) audioStreamRef.current.srcObject = remoteStream;
116
- }
117
- function receiveMessage(message) {
118
- addMessage(message);
119
- onMessageReceived == null ? void 0 : onMessageReceived(message);
120
- }
121
- function sendMessage(message) {
122
- addMessage(message);
123
- Object.values(connRef.current).forEach((conn) => conn.send({ type: "message", message }));
124
- onMessageSent == null ? void 0 : onMessageSent(message);
125
- }
126
- useEffect(() => {
127
- if (!text && !audio) return;
128
- import('peerjs').then(
129
- ({
130
- Peer,
131
- util: {
132
- supports: { audioVideo, data }
133
- }
134
- }) => {
135
- if (!data || !audioVideo) return onError();
136
- const peer2 = new Peer(completePeerId, __spreadValues({ config: defaultConfig }, peerOptions));
137
- peer2.on("connection", handleConnection);
138
- setPeer(peer2);
139
- }
140
- );
141
- return () => {
142
- setPeer((prev) => {
143
- prev == null ? void 0 : prev.removeAllListeners();
144
- prev == null ? void 0 : prev.destroy();
145
- return void 0;
146
- });
147
- };
148
- }, [completePeerId]);
149
- useEffect(() => {
150
- if (!text || !peer) return;
151
- const handleOpen = () => completeRemotePeerIds.forEach((id) => handleConnection(peer.connect(id)));
152
- if (peer.open) handleOpen();
153
- else peer.once("open", handleOpen);
154
- return () => {
155
- Object.values(connRef.current).forEach(closeConnection);
156
- connRef.current = {};
157
- };
158
- }, [text, peer]);
159
- useEffect(() => {
160
- if (!audio || !peer) return;
161
- const setupAudio = () => navigator.mediaDevices.getUserMedia({
162
- video: false,
163
- audio: {
164
- autoGainControl: false,
165
- // Disable automatic gain control
166
- noiseSuppression: true,
167
- // Enable noise suppression
168
- echoCancellation: true
169
- // Enable echo cancellation
170
- }
171
- }).then((stream) => {
172
- localStreamRef.current = stream;
173
- completeRemotePeerIds.forEach((id) => {
174
- const call = peer.call(id, stream);
175
- call.on("stream", handleRemoteStream);
176
- call.on("close", call.removeAllListeners);
177
- callsRef.current[id] = call;
178
- });
179
- peer.on("call", (call) => {
180
- call.answer(stream);
181
- call.on("stream", handleRemoteStream);
182
- call.on("close", call.removeAllListeners);
183
- callsRef.current[call.peer] = call;
184
- });
185
- }).catch(handleError);
186
- if (peer.open) setupAudio();
187
- else peer.once("open", setupAudio);
188
- return () => {
189
- var _a;
190
- (_a = localStreamRef.current) == null ? void 0 : _a.getTracks().forEach((track) => track.stop());
191
- localStreamRef.current = null;
192
- Object.values(callsRef.current).forEach(closeConnection);
193
- callsRef.current = {};
194
- };
195
- }, [audio, peer]);
196
- return { peerId: completePeerId, audioStreamRef, remotePeers, messages, sendMessage, audio, setAudio };
197
- }
198
- function useMessages() {
199
- const [messages, setMessages] = useStorage("rpc-messages", []);
200
- const addMessage = (message) => setMessages((prev) => prev.concat(message));
201
- return [messages, setMessages, addMessage];
202
- }
203
- function useStorage(key, initialValue, local = false) {
204
- const [storedValue, setStoredValue] = useState(() => {
205
- if (typeof window === "undefined") return initialValue;
206
- return getStorage(key, initialValue, local);
207
- });
208
- const setValue = (value) => {
209
- setStoredValue((prev) => {
210
- const next = isSetStateFunction(value) ? value(prev) : value;
211
- setStorage(key, next, local);
212
- return next;
213
- });
214
- };
215
- return [storedValue, setValue];
216
- }
217
- function useAudio(allowed) {
218
- const [audio, setAudio] = useStorage("rpc-audio", false, true);
219
- const enabled = audio && allowed;
220
- return [enabled, setAudio];
221
- }
222
-
223
- // src/styles.css
224
- function injectStyle(css) {
225
- if (typeof document === "undefined") return;
226
- const head = document.head || document.getElementsByTagName("head")[0];
227
- const style = document.createElement("style");
228
- style.type = "text/css";
229
- if (head.firstChild) {
230
- head.insertBefore(style, head.firstChild);
231
- } else {
232
- head.appendChild(style);
233
- }
234
- if (style.styleSheet) {
235
- style.styleSheet.cssText = css;
236
- } else {
237
- style.appendChild(document.createTextNode(css));
238
- }
239
- }
240
- injectStyle('.rpc-font {\n font-family:\n "Trebuchet MS",\n "Lucida Sans Unicode",\n "Lucida Grande",\n "Lucida Sans",\n Arial,\n sans-serif;\n}\n.rpc-main {\n display: flex;\n align-items: center;\n column-gap: 0.5rem;\n}\n.rpc-dialog-container {\n position: relative;\n}\n.rpc-notification {\n position: relative;\n}\n.rpc-notification .rpc-badge {\n background-color: red;\n position: absolute;\n top: 0;\n right: 0;\n width: 6px;\n aspect-ratio: 1;\n border-radius: 100%;\n}\n.rpc-dialog {\n position: absolute;\n background-color: black;\n color: white;\n padding: 0.4rem 0 0.25rem 0;\n border-radius: 0.4rem;\n font-size: small;\n}\n.rpc-position-left {\n left: 0.6rem;\n translate: -100%;\n}\n.rpc-position-center {\n left: 0.5rem;\n translate: -50%;\n}\n.rpc-position-right {\n left: 0.3rem;\n}\n.rpc-hr {\n margin: 0.25rem 0;\n border-color: rgba(255, 255, 255, 0.7);\n}\n.rpc-message-container {\n height: 7rem;\n overflow-y: scroll;\n padding-inline: 0.5rem;\n margin-bottom: 0.25rem;\n}\n.rpc-message-container::-webkit-scrollbar {\n width: 2.5px;\n}\n.rpc-message-container::-webkit-scrollbar-track {\n background: gray;\n}\n.rpc-message-container::-webkit-scrollbar-thumb {\n background-color: white;\n}\n.rpc-heading {\n text-align: center;\n font-size: medium;\n font-weight: bold;\n padding-inline: 0.5rem;\n}\n.rpc-input-container {\n display: flex;\n width: 15rem;\n max-width: 90vw;\n column-gap: 0.25rem;\n padding: 0.25rem 0.5rem;\n}\n.rpc-input {\n color: white;\n width: 100%;\n background-color: black;\n border: none;\n outline: 1px solid rgba(255, 255, 255, 0.8);\n border-radius: 0.25rem;\n padding: 0.3rem 0.25rem;\n}\n.rpc-input::placeholder {\n color: rgba(255, 255, 255, 0.7);\n}\n.rpc-input:focus {\n outline: 2px solid white;\n}\n.rpc-button {\n all: unset;\n display: flex;\n}\n.rpc-icon-container {\n display: flex;\n align-items: center;\n}\n.rpc-invert {\n filter: invert(100%);\n}\n');
241
-
242
- // src/index.tsx
243
- function Chat(_a) {
244
- var _b = _a, { text = true, audio = true, onMessageReceived, dialogOptions, props = {}, children } = _b, hookProps = __objRest(_b, ["text", "audio", "onMessageReceived", "dialogOptions", "props", "children"]);
245
- const _a2 = useChat(__spreadValues({
246
- text,
247
- audio,
248
- onMessageReceived: modifiedOnMessageReceived
249
- }, hookProps)), { peerId, audioStreamRef } = _a2, childrenOptions = __objRest(_a2, ["peerId", "audioStreamRef"]);
250
- const { remotePeers, messages, sendMessage, audio: audioEnabled, setAudio } = childrenOptions;
251
- const containerRef = useRef(null);
252
- const [dialog, setDialog] = useState(false);
253
- const dialogRef = useRef(null);
254
- const inputRef = useRef(null);
255
- const [notification, setNotification] = useState(false);
256
- function modifiedOnMessageReceived(message) {
257
- var _a3;
258
- if (!((_a3 = dialogRef.current) == null ? void 0 : _a3.open)) setNotification(true);
259
- onMessageReceived == null ? void 0 : onMessageReceived(message);
260
- }
261
- useEffect(() => {
262
- var _a3, _b2;
263
- if (dialog) (_a3 = dialogRef.current) == null ? void 0 : _a3.show();
264
- else (_b2 = dialogRef.current) == null ? void 0 : _b2.close();
265
- }, [dialog]);
266
- useEffect(() => {
267
- const container = containerRef.current;
268
- if (container) container.scrollTop = container.scrollHeight;
269
- }, [dialog, remotePeers, messages]);
270
- return /* @__PURE__ */ React.createElement("div", __spreadValues({ className: "rpc-main rpc-font" }, props), typeof children === "function" ? children(childrenOptions) : /* @__PURE__ */ React.createElement(React.Fragment, null, text && /* @__PURE__ */ React.createElement("div", { className: "rpc-dialog-container" }, dialog ? /* @__PURE__ */ React.createElement(BiSolidMessageX, { title: "Close chat", onClick: () => setDialog(false) }) : /* @__PURE__ */ React.createElement("div", { className: "rpc-notification" }, /* @__PURE__ */ React.createElement(
271
- BiSolidMessageDetail,
272
- {
273
- title: "Open chat",
274
- onClick: () => {
275
- setNotification(false);
276
- setDialog(true);
277
- }
278
- }
279
- ), notification && /* @__PURE__ */ React.createElement("span", { className: "rpc-badge" })), /* @__PURE__ */ React.createElement("dialog", { ref: dialogRef, className: `${dialog ? "rpc-dialog" : ""} rpc-position-${(dialogOptions == null ? void 0 : dialogOptions.position) || "center"}`, style: dialogOptions == null ? void 0 : dialogOptions.style }, /* @__PURE__ */ React.createElement("div", { className: "rpc-heading" }, "Chat"), /* @__PURE__ */ React.createElement("hr", { className: "rpc-hr" }), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { ref: containerRef, className: "rpc-message-container" }, messages.map(({ id, text: text2 }, i) => /* @__PURE__ */ React.createElement("div", { key: i }, /* @__PURE__ */ React.createElement("strong", null, id === peerId ? "You" : remotePeers[id], ": "), /* @__PURE__ */ React.createElement("span", null, text2)))), /* @__PURE__ */ React.createElement("hr", { className: "rpc-hr" }), /* @__PURE__ */ React.createElement(
280
- "form",
281
- {
282
- className: "rpc-input-container",
283
- onSubmit: (e) => {
284
- var _a3;
285
- e.preventDefault();
286
- const text2 = (_a3 = inputRef.current) == null ? void 0 : _a3.value;
287
- if (text2) {
288
- inputRef.current.value = "";
289
- sendMessage({ id: peerId, text: text2 });
290
- }
291
- }
292
- },
293
- /* @__PURE__ */ React.createElement("input", { ref: inputRef, className: "rpc-input rpc-font", placeholder: "Enter a message" }),
294
- /* @__PURE__ */ React.createElement("button", { type: "submit", className: "rpc-button" }, /* @__PURE__ */ React.createElement(GrSend, { title: "Send message" }))
295
- )))), audio && /* @__PURE__ */ React.createElement("button", { className: "rpc-button", onClick: () => setAudio(!audioEnabled) }, audioEnabled ? /* @__PURE__ */ React.createElement(BsFillMicFill, { title: "Turn mic off" }) : /* @__PURE__ */ React.createElement(BsFillMicMuteFill, { title: "Turn mic on" }))), audio && audioEnabled && /* @__PURE__ */ React.createElement("audio", { ref: audioStreamRef, autoPlay: true, style: { display: "none" } }));
296
- }
297
-
298
- export { clearChat, Chat as default, useChat };
1
+ export { Chat as default } from './chunks/chunk-YDZ27MG7.js';
2
+ export { useChat } from './chunks/chunk-GT3RG6VA.js';
3
+ import './chunks/chunk-JJPIWKLG.js';
4
+ export { clearChat } from './chunks/chunk-ZYFPSCFE.js';
5
+ import './chunks/chunk-LNEKYYG7.js';
@@ -0,0 +1,6 @@
1
+ declare const removeStorage: (key: string, local?: boolean) => void;
2
+ declare const clearChat: () => void;
3
+ declare const setStorage: (key: string, value: unknown, local?: boolean) => void;
4
+ declare function getStorage<T>(key: string, fallbackValue?: T, local?: boolean): T | undefined;
5
+
6
+ export { clearChat, getStorage, removeStorage, setStorage };
@@ -0,0 +1,2 @@
1
+ export { clearChat, getStorage, removeStorage, setStorage } from '../chunks/chunk-ZYFPSCFE.js';
2
+ import '../chunks/chunk-LNEKYYG7.js';
package/dist/types.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { PeerOptions, DataConnection, MediaConnection } from 'peerjs';
2
2
  export { PeerOptions } from 'peerjs';
3
- import { DetailedHTMLProps, HTMLAttributes, RefObject, SetStateAction, CSSProperties, ReactNode } from 'react';
3
+ import { CSSProperties, DetailedHTMLProps, HTMLAttributes, ReactNode, RefObject, SetStateAction } from 'react';
4
4
 
5
5
  type Connection = DataConnection | MediaConnection;
6
6
  type ErrorHandler = () => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-peer-chat",
3
- "version": "0.8.5",
3
+ "version": "0.8.6",
4
4
  "description": "An easy to use react component for impleting peer-to-peer chatting.",
5
5
  "license": "MIT",
6
6
  "author": "Sahil Aggarwal <aggarwalsahil2004@gmail.com>",
@@ -14,54 +14,57 @@
14
14
  "url": "https://github.com/SahilAggarwal2004/react-peer-chat/issues"
15
15
  },
16
16
  "type": "module",
17
- "files": [
18
- "dist"
19
- ],
20
17
  "exports": {
21
18
  ".": "./dist/index.js",
22
19
  "./icons": "./dist/icons.js",
23
20
  "./types": "./dist/types.js"
24
21
  },
25
22
  "main": "dist/index.js",
26
- "types": "dist/index.d.ts",
23
+ "files": [
24
+ "dist"
25
+ ],
27
26
  "sideEffects": [
28
27
  "**/*.css"
29
28
  ],
29
+ "types": "dist/index.d.ts",
30
30
  "dependencies": {
31
31
  "peerjs": "^1.5.5"
32
32
  },
33
+ "peerDependencies": {
34
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
35
+ "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
36
+ },
33
37
  "devDependencies": {
34
38
  "@release-it/conventional-changelog": "^10.0.4",
35
39
  "@types/react": "^19.2.7",
40
+ "prettier-package-json": "^2.8.0",
36
41
  "release-it": "^19.2.2",
37
42
  "tsup": "^8.5.1",
38
43
  "typescript": "^5.9.3"
39
44
  },
40
- "peerDependencies": {
41
- "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
42
- "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
43
- },
44
45
  "keywords": [
45
- "react",
46
+ "audio-chat",
46
47
  "chat",
47
48
  "component",
48
- "peer",
49
- "peerjs",
50
49
  "p2p",
50
+ "p2p-chat",
51
+ "peer",
51
52
  "peer-to-peer",
52
- "webrtc",
53
+ "peerjs",
54
+ "react",
53
55
  "react-peer-chat",
54
- "typescript",
55
- "p2p-chat",
56
56
  "text-chat",
57
+ "typescript",
57
58
  "voice-chat",
58
- "audio-chat"
59
+ "webrtc"
59
60
  ],
60
61
  "scripts": {
61
62
  "build": "pnpm i && pnpm run compile",
62
63
  "compile": "tsup",
63
64
  "dev": "tsup --watch",
64
65
  "dry-release": "release-it --ci --dry-run",
66
+ "prettier": "prettier-package-json --write package.json",
67
+ "pub": "pnpm login && pnpm publish",
65
68
  "release": "release-it --ci"
66
69
  }
67
70
  }