@realtimexsco/live-chat 1.5.4 → 1.5.5

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.cjs CHANGED
@@ -4639,6 +4639,67 @@ var init_utils2 = __esm({
4639
4639
  "src/lib/utils.ts"() {
4640
4640
  }
4641
4641
  });
4642
+ function TooltipProvider({
4643
+ delayDuration = 0,
4644
+ ...props
4645
+ }) {
4646
+ return /* @__PURE__ */ jsxRuntime.jsx(
4647
+ radixUi.Tooltip.Provider,
4648
+ {
4649
+ "data-slot": "tooltip-provider",
4650
+ delayDuration,
4651
+ ...props
4652
+ }
4653
+ );
4654
+ }
4655
+ function Tooltip({
4656
+ ...props
4657
+ }) {
4658
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Tooltip.Root, { "data-slot": "tooltip", ...props });
4659
+ }
4660
+ function TooltipTrigger({
4661
+ ...props
4662
+ }) {
4663
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Tooltip.Trigger, { "data-slot": "tooltip-trigger", ...props });
4664
+ }
4665
+ function TooltipContent({
4666
+ className,
4667
+ sideOffset = 0,
4668
+ children,
4669
+ showArrow = true,
4670
+ arrowClassName,
4671
+ ...props
4672
+ }) {
4673
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Tooltip.Portal, { children: /* @__PURE__ */ jsxRuntime.jsxs(
4674
+ radixUi.Tooltip.Content,
4675
+ {
4676
+ "data-slot": "tooltip-content",
4677
+ sideOffset,
4678
+ className: cn(
4679
+ "bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
4680
+ className
4681
+ ),
4682
+ ...props,
4683
+ children: [
4684
+ children,
4685
+ showArrow ? /* @__PURE__ */ jsxRuntime.jsx(
4686
+ radixUi.Tooltip.Arrow,
4687
+ {
4688
+ className: cn(
4689
+ "bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]",
4690
+ arrowClassName
4691
+ )
4692
+ }
4693
+ ) : null
4694
+ ]
4695
+ }
4696
+ ) });
4697
+ }
4698
+ var init_tooltip = __esm({
4699
+ "src/ui/tooltip.tsx"() {
4700
+ init_utils2();
4701
+ }
4702
+ });
4642
4703
  function Avatar({
4643
4704
  className,
4644
4705
  size = "default",
@@ -4837,7 +4898,7 @@ var init_ParticipantView = __esm({
4837
4898
  }
4838
4899
  ),
4839
4900
  !isLocal && /* @__PURE__ */ jsxRuntime.jsx("audio", { ref: audioRef, autoPlay: true, playsInline: true }),
4840
- isHandRaised && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute top-2 left-2 flex size-7 items-center justify-center rounded-full bg-amber-400 text-neutral-900 shadow", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Hand, { size: 14 }) }),
4901
+ isHandRaised && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute top-2 left-2 flex size-7 items-center justify-center rounded-full bg-amber-400 text-neutral-900 shadow-lg shadow-amber-400/30 animate-[bounce_1s_ease-in-out_2]", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Hand, { size: 14 }) }),
4841
4902
  !showVideo && /* @__PURE__ */ jsxRuntime.jsxs(Avatar, { size: "lg", className: "size-20", children: [
4842
4903
  /* @__PURE__ */ jsxRuntime.jsx(AvatarImage, { src: fallbackImage, alt: name }),
4843
4904
  /* @__PURE__ */ jsxRuntime.jsx(AvatarFallback, { className: "bg-neutral-700 text-xl font-medium text-white", children: name.charAt(0).toUpperCase() })
@@ -4894,7 +4955,7 @@ var init_AudioCallView = __esm({
4894
4955
  }
4895
4956
  ),
4896
4957
  isMuted && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -right-1 -bottom-1 flex size-9 items-center justify-center rounded-full bg-neutral-700 ring-4 ring-neutral-900", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MicOff, { size: 16, className: "text-white/80" }) }),
4897
- isRemoteHandRaised && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -left-1 -top-1 flex size-9 items-center justify-center rounded-full bg-amber-400 text-neutral-900 ring-4 ring-neutral-900", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Hand, { size: 16 }) })
4958
+ isRemoteHandRaised && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -left-1 -top-1 flex size-9 items-center justify-center rounded-full bg-amber-400 text-neutral-900 shadow-lg shadow-amber-400/30 ring-4 ring-neutral-900 animate-[bounce_1s_ease-in-out_2]", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Hand, { size: 16 }) })
4898
4959
  ] }),
4899
4960
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xl font-semibold tracking-tight", children: name })
4900
4961
  ] });
@@ -4918,13 +4979,13 @@ var init_ScreenShareView = __esm({
4918
4979
  el.srcObject = null;
4919
4980
  }
4920
4981
  }, [screenShareOn, screenShareStream]);
4921
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, children: [
4922
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 px-1 pb-2 text-xs font-medium text-white/70", children: [
4923
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MonitorUp, { size: 14 }),
4982
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex flex-1 items-center justify-center overflow-hidden rounded-2xl bg-black ring-1 ring-white/10 shadow-2xl", children: [
4983
+ /* @__PURE__ */ jsxRuntime.jsx("video", { ref: videoRef, playsInline: true, muted: isLocal, className: "max-h-full max-w-full object-contain" }),
4984
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-3 left-3 flex items-center gap-1.5 rounded-full bg-black/70 px-3 py-1.5 text-xs font-medium text-white shadow backdrop-blur-sm", children: [
4985
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MonitorUp, { size: 13, className: "text-indigo-300" }),
4924
4986
  isLocal ? "You are presenting" : `${displayName || "Someone"} is presenting`
4925
- ] }),
4926
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative flex flex-1 items-center justify-center overflow-hidden rounded-xl bg-black", children: /* @__PURE__ */ jsxRuntime.jsx("video", { ref: videoRef, playsInline: true, muted: isLocal, className: "max-h-full max-w-full object-contain" }) })
4927
- ] });
4987
+ ] })
4988
+ ] }) });
4928
4989
  };
4929
4990
  ScreenShareView_default = ScreenShareView;
4930
4991
  }
@@ -4964,22 +5025,46 @@ var init_popover = __esm({
4964
5025
  init_utils2();
4965
5026
  }
4966
5027
  });
4967
- var circleBtn, idleBtnClass, activeBtnClass, QUICK_REACTIONS, CallControls, CallControls_default;
5028
+ var circleBtn, idleBtnClass, activeBtnClass, ControlButton, QUICK_REACTIONS, CallControls, CallControls_default;
4968
5029
  var init_CallControls = __esm({
4969
5030
  "src/call/CallControls.tsx"() {
4970
5031
  init_button();
4971
5032
  init_popover();
5033
+ init_tooltip();
4972
5034
  init_utils2();
4973
5035
  init_call_store();
4974
5036
  circleBtn = "size-13 rounded-full text-white transition-all duration-150 disabled:opacity-40 hover:scale-105 active:scale-95";
4975
5037
  idleBtnClass = "bg-white/10 hover:bg-white/20";
4976
5038
  activeBtnClass = "bg-white text-neutral-900 hover:bg-white/90";
5039
+ ControlButton = ({
5040
+ icon,
5041
+ label,
5042
+ active,
5043
+ activeClassName = activeBtnClass,
5044
+ className,
5045
+ onClick
5046
+ }) => /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
5047
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
5048
+ Button,
5049
+ {
5050
+ variant: "ghost",
5051
+ size: "icon",
5052
+ "aria-label": label,
5053
+ onClick,
5054
+ className: cn(circleBtn, active ? activeClassName : idleBtnClass, className),
5055
+ children: icon
5056
+ }
5057
+ ) }),
5058
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "top", className: "z-[1000000]", children: label })
5059
+ ] });
4977
5060
  QUICK_REACTIONS = ["\u{1F44D}", "\u2764\uFE0F", "\u{1F602}", "\u{1F62E}", "\u{1F44F}", "\u{1F389}"];
4978
5061
  CallControls = ({
4979
5062
  sdk,
4980
5063
  onReaction,
4981
5064
  isHandRaised,
4982
- onToggleRaiseHand
5065
+ onToggleRaiseHand,
5066
+ isChatOpen,
5067
+ onToggleChat
4983
5068
  }) => {
4984
5069
  const {
4985
5070
  toggleMic,
@@ -4996,73 +5081,80 @@ var init_CallControls = __esm({
4996
5081
  endCall();
4997
5082
  };
4998
5083
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-5 py-6", children: [
4999
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 rounded-full bg-white/10 p-2 backdrop-blur-sm", children: [
5084
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 rounded-full bg-white/10 p-2 shadow-lg shadow-black/20 backdrop-blur-sm", children: [
5000
5085
  /* @__PURE__ */ jsxRuntime.jsx(
5001
- Button,
5086
+ ControlButton,
5002
5087
  {
5003
- variant: "ghost",
5004
- size: "icon",
5005
- className: cn(circleBtn, localMicOn ? idleBtnClass : activeBtnClass),
5006
- "aria-label": localMicOn ? "Mute microphone" : "Unmute microphone",
5007
- onClick: () => toggleMic(),
5008
- children: localMicOn ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Mic, { size: 20 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MicOff, { size: 20 })
5088
+ icon: localMicOn ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Mic, { size: 20 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MicOff, { size: 20 }),
5089
+ label: localMicOn ? "Mute microphone" : "Unmute microphone",
5090
+ active: !localMicOn,
5091
+ onClick: () => toggleMic()
5009
5092
  }
5010
5093
  ),
5011
5094
  /* @__PURE__ */ jsxRuntime.jsx(
5012
- Button,
5095
+ ControlButton,
5013
5096
  {
5014
- variant: "ghost",
5015
- size: "icon",
5016
- className: cn(circleBtn, localWebcamOn ? idleBtnClass : activeBtnClass),
5017
- "aria-label": localWebcamOn ? "Turn camera off" : "Turn camera on",
5018
- onClick: () => toggleWebcam(),
5019
- children: localWebcamOn ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Video, { size: 20 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.VideoOff, { size: 20 })
5097
+ icon: localWebcamOn ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Video, { size: 20 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.VideoOff, { size: 20 }),
5098
+ label: localWebcamOn ? "Turn off camera" : "Turn on camera",
5099
+ active: !localWebcamOn,
5100
+ onClick: () => toggleWebcam()
5020
5101
  }
5021
5102
  ),
5103
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, className: "mx-0.5 h-7 w-px shrink-0 bg-white/15" }),
5022
5104
  /* @__PURE__ */ jsxRuntime.jsx(
5023
- Button,
5105
+ ControlButton,
5024
5106
  {
5025
- variant: "ghost",
5026
- size: "icon",
5027
- className: cn(circleBtn, localScreenShareOn ? "bg-indigo-500 hover:bg-indigo-400" : idleBtnClass),
5028
- "aria-label": localScreenShareOn ? "Stop sharing your screen" : "Share your screen",
5029
- onClick: () => toggleScreenShare(),
5030
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MonitorUp, { size: 20 })
5107
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MonitorUp, { size: 20 }),
5108
+ label: localScreenShareOn ? "Stop sharing your screen" : "Share your screen",
5109
+ active: localScreenShareOn,
5110
+ activeClassName: "bg-indigo-500 hover:bg-indigo-400",
5111
+ onClick: () => toggleScreenShare()
5031
5112
  }
5032
5113
  ),
5033
5114
  /* @__PURE__ */ jsxRuntime.jsx(
5034
- Button,
5115
+ ControlButton,
5035
5116
  {
5036
- variant: "ghost",
5037
- size: "icon",
5038
- className: cn(circleBtn, isHandRaised ? "bg-amber-400 text-neutral-900 hover:bg-amber-300" : idleBtnClass),
5039
- "aria-label": isHandRaised ? "Lower hand" : "Raise hand",
5040
- onClick: onToggleRaiseHand,
5041
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Hand, { size: 20 })
5117
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Hand, { size: 20, className: isHandRaised ? "animate-[bounce_1s_ease-in-out_2]" : void 0 }),
5118
+ label: isHandRaised ? "Lower hand" : "Raise hand",
5119
+ active: isHandRaised,
5120
+ activeClassName: "bg-amber-400 text-neutral-900 hover:bg-amber-300",
5121
+ onClick: onToggleRaiseHand
5122
+ }
5123
+ ),
5124
+ /* @__PURE__ */ jsxRuntime.jsx(
5125
+ ControlButton,
5126
+ {
5127
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquare, { size: 20 }),
5128
+ label: isChatOpen ? "Close in-call chat" : "Open in-call chat",
5129
+ active: isChatOpen,
5130
+ onClick: onToggleChat
5042
5131
  }
5043
5132
  ),
5044
5133
  /* @__PURE__ */ jsxRuntime.jsxs(Popover, { children: [
5045
- /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
5046
- Button,
5047
- {
5048
- variant: "ghost",
5049
- size: "icon",
5050
- className: cn(circleBtn, idleBtnClass),
5051
- "aria-label": "Send a reaction",
5052
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Smile, { size: 20 })
5053
- }
5054
- ) }),
5134
+ /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
5135
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
5136
+ Button,
5137
+ {
5138
+ variant: "ghost",
5139
+ size: "icon",
5140
+ className: cn(circleBtn, idleBtnClass),
5141
+ "aria-label": "Send a reaction",
5142
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Smile, { size: 20 })
5143
+ }
5144
+ ) }) }),
5145
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "top", className: "z-[1000000]", children: "React" })
5146
+ ] }),
5055
5147
  /* @__PURE__ */ jsxRuntime.jsx(
5056
5148
  PopoverContent,
5057
5149
  {
5058
5150
  side: "top",
5059
5151
  align: "center",
5060
- className: "w-auto rounded-full border-white/10 bg-neutral-900 p-1.5 shadow-xl",
5152
+ className: "z-[1000000] w-auto rounded-full border-white/10 bg-neutral-900 p-1.5 shadow-xl",
5061
5153
  children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-0.5", children: QUICK_REACTIONS.map((emoji) => /* @__PURE__ */ jsxRuntime.jsx(
5062
5154
  "button",
5063
5155
  {
5064
5156
  type: "button",
5065
- className: "flex size-10 items-center justify-center rounded-full text-xl transition-transform hover:scale-125 hover:bg-white/10",
5157
+ className: "flex size-10 items-center justify-center rounded-full text-xl transition-transform hover:scale-125 hover:bg-white/10 active:scale-95",
5066
5158
  "aria-label": `React with ${emoji}`,
5067
5159
  onClick: () => onReaction(emoji),
5068
5160
  children: emoji
@@ -5073,17 +5165,20 @@ var init_CallControls = __esm({
5073
5165
  )
5074
5166
  ] })
5075
5167
  ] }),
5076
- /* @__PURE__ */ jsxRuntime.jsx(
5077
- Button,
5078
- {
5079
- variant: "ghost",
5080
- size: "icon",
5081
- className: cn(circleBtn, "bg-red-600 shadow-lg shadow-red-600/30 hover:bg-red-500"),
5082
- "aria-label": "End call",
5083
- onClick: handleLeave,
5084
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PhoneOff, { size: 22 })
5085
- }
5086
- )
5168
+ /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
5169
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
5170
+ Button,
5171
+ {
5172
+ variant: "ghost",
5173
+ size: "icon",
5174
+ className: cn(circleBtn, "bg-red-600 shadow-lg shadow-red-600/30 hover:bg-red-500"),
5175
+ "aria-label": "End call",
5176
+ onClick: handleLeave,
5177
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PhoneOff, { size: 22 })
5178
+ }
5179
+ ) }),
5180
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "top", className: "z-[1000000]", children: "Leave call" })
5181
+ ] })
5087
5182
  ] });
5088
5183
  };
5089
5184
  CallControls_default = CallControls;
@@ -5140,15 +5235,18 @@ var init_useCallReactions = __esm({
5140
5235
  }, []);
5141
5236
  const { send, localParticipant } = sdk.useMeeting({
5142
5237
  onData: (data) => {
5238
+ console.log("[realtimex-call] data channel received:", data);
5143
5239
  try {
5144
5240
  const raw = typeof data.payload === "string" ? data.payload : new TextDecoder().decode(data.payload);
5145
5241
  const message = JSON.parse(raw);
5242
+ console.log("[realtimex-call] data channel parsed:", message, "from", data.from);
5146
5243
  if (message.type === "reaction") {
5147
5244
  addFloatingReaction(message.emoji, data.from);
5148
5245
  } else if (message.type === "raise-hand") {
5149
5246
  setRaisedHands((prev) => ({ ...prev, [data.from]: message.raised }));
5150
5247
  }
5151
- } catch {
5248
+ } catch (err) {
5249
+ console.warn("[realtimex-call] data channel payload not decodable:", err, data);
5152
5250
  }
5153
5251
  }
5154
5252
  });
@@ -5161,12 +5259,22 @@ var init_useCallReactions = __esm({
5161
5259
  return next;
5162
5260
  });
5163
5261
  }, [localParticipant?.id]);
5262
+ const sendDataMessage = React11.useCallback(
5263
+ (message) => {
5264
+ send(JSON.stringify(message)).then((ok) => {
5265
+ console.log("[realtimex-call] data channel send result:", ok, message);
5266
+ }).catch((err) => {
5267
+ console.warn("[realtimex-call] data channel send failed:", err, message);
5268
+ });
5269
+ },
5270
+ [send]
5271
+ );
5164
5272
  const sendReaction = React11.useCallback(
5165
5273
  (emoji) => {
5166
5274
  if (localParticipant?.id) addFloatingReaction(emoji, localParticipant.id);
5167
- void send(JSON.stringify({ type: "reaction", emoji }));
5275
+ sendDataMessage({ type: "reaction", emoji });
5168
5276
  },
5169
- [send, localParticipant?.id, addFloatingReaction]
5277
+ [localParticipant?.id, addFloatingReaction, sendDataMessage]
5170
5278
  );
5171
5279
  const toggleRaiseHand = React11.useCallback(() => {
5172
5280
  setIsHandRaised((prev) => {
@@ -5174,15 +5282,15 @@ var init_useCallReactions = __esm({
5174
5282
  if (localParticipant?.id) {
5175
5283
  setRaisedHands((hands) => ({ ...hands, [localParticipant.id]: next }));
5176
5284
  }
5177
- void send(JSON.stringify({ type: "raise-hand", raised: next }));
5285
+ sendDataMessage({ type: "raise-hand", raised: next });
5178
5286
  return next;
5179
5287
  });
5180
- }, [send, localParticipant?.id]);
5288
+ }, [localParticipant?.id, sendDataMessage]);
5181
5289
  return { floatingReactions, raisedHands, isHandRaised, sendReaction, toggleRaiseHand };
5182
5290
  };
5183
5291
  }
5184
5292
  });
5185
- var MeetingView, MeetingView_default;
5293
+ var RemoteWebcamWatcher, MeetingView, MeetingView_default;
5186
5294
  var init_MeetingView = __esm({
5187
5295
  "src/call/MeetingView.tsx"() {
5188
5296
  init_ParticipantView();
@@ -5193,11 +5301,37 @@ var init_MeetingView = __esm({
5193
5301
  init_useCallReactions();
5194
5302
  init_call_store();
5195
5303
  init_utils2();
5196
- MeetingView = ({ sdk, mode, peerName, peerAvatar }) => {
5304
+ RemoteWebcamWatcher = ({
5305
+ sdk,
5306
+ participantId,
5307
+ onChange
5308
+ }) => {
5309
+ const { webcamOn } = sdk.useParticipant(participantId);
5310
+ React11.useEffect(() => {
5311
+ onChange(participantId, webcamOn);
5312
+ return () => onChange(participantId, false);
5313
+ }, [participantId, webcamOn]);
5314
+ return null;
5315
+ };
5316
+ MeetingView = ({
5317
+ sdk,
5318
+ mode,
5319
+ peerName,
5320
+ peerAvatar,
5321
+ isChatOpen,
5322
+ onToggleChat
5323
+ }) => {
5197
5324
  const endCall = exports.useCallStore((s) => s.endCall);
5198
5325
  const [hasJoined, setHasJoined] = React11.useState(false);
5199
5326
  const hasJoinedRef = React11.useRef(false);
5200
5327
  const [groupView, setGroupView] = React11.useState("grid");
5328
+ const [meetingState, setMeetingState] = React11.useState("CONNECTING");
5329
+ const [remoteWebcamOn, setRemoteWebcamOn] = React11.useState({});
5330
+ const handleRemoteWebcamChange = React11.useCallback((participantId, on) => {
5331
+ setRemoteWebcamOn(
5332
+ (prev) => prev[participantId] === on ? prev : { ...prev, [participantId]: on }
5333
+ );
5334
+ }, []);
5201
5335
  const {
5202
5336
  join,
5203
5337
  participants,
@@ -5213,7 +5347,8 @@ var init_MeetingView = __esm({
5213
5347
  onMeetingLeft: () => {
5214
5348
  if (!hasJoinedRef.current) return;
5215
5349
  endCall();
5216
- }
5350
+ },
5351
+ onMeetingStateChanged: ({ state }) => setMeetingState(state)
5217
5352
  });
5218
5353
  const { floatingReactions, raisedHands, isHandRaised, sendReaction, toggleRaiseHand } = useCallReactions(sdk);
5219
5354
  React11.useEffect(() => {
@@ -5229,149 +5364,47 @@ var init_MeetingView = __esm({
5229
5364
  (id) => id !== localParticipant?.id
5230
5365
  );
5231
5366
  const isGroup = remoteIds.length > 1;
5367
+ const webcamWatchers = remoteIds.map((id) => /* @__PURE__ */ jsxRuntime.jsx(RemoteWebcamWatcher, { sdk, participantId: id, onChange: handleRemoteWebcamChange }, id));
5232
5368
  const controls = /* @__PURE__ */ jsxRuntime.jsx(
5233
5369
  CallControls_default,
5234
5370
  {
5235
5371
  sdk,
5236
5372
  onReaction: sendReaction,
5237
5373
  isHandRaised,
5238
- onToggleRaiseHand: toggleRaiseHand
5374
+ onToggleRaiseHand: toggleRaiseHand,
5375
+ isChatOpen,
5376
+ onToggleChat
5239
5377
  }
5240
5378
  );
5379
+ const connectionStatusText = meetingState === "CONNECTING" ? "Connecting\u2026" : meetingState === "DISCONNECTED" ? "Reconnecting\u2026" : meetingState === "FAILED" ? "Connection lost" : null;
5380
+ const statusBanner = connectionStatusText && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center justify-center gap-1.5 bg-amber-500/15 px-4 py-1.5 text-xs font-medium text-amber-300", children: [
5381
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "size-1.5 animate-pulse rounded-full bg-amber-400" }),
5382
+ connectionStatusText
5383
+ ] });
5241
5384
  if (presenterId) {
5242
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex flex-1 flex-col", children: [
5243
- /* @__PURE__ */ jsxRuntime.jsx(FloatingReactions_default, { reactions: floatingReactions }),
5244
- /* @__PURE__ */ jsxRuntime.jsx(
5245
- ScreenShareView_default,
5246
- {
5247
- sdk,
5248
- presenterId,
5249
- isLocal: presenterId === localParticipant?.id,
5250
- className: "flex flex-1 flex-col overflow-hidden p-4 pb-2"
5251
- }
5252
- ),
5253
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2 overflow-x-auto px-4 pb-2", children: [
5254
- localParticipant?.id && /* @__PURE__ */ jsxRuntime.jsx(
5255
- ParticipantView_default,
5385
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5386
+ webcamWatchers,
5387
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex flex-1 flex-col", children: [
5388
+ statusBanner,
5389
+ /* @__PURE__ */ jsxRuntime.jsx(FloatingReactions_default, { reactions: floatingReactions }),
5390
+ /* @__PURE__ */ jsxRuntime.jsx(
5391
+ ScreenShareView_default,
5256
5392
  {
5257
5393
  sdk,
5258
- participantId: localParticipant.id,
5259
- isLocal: true,
5260
- isHandRaised: !!raisedHands[localParticipant.id],
5261
- className: "aspect-video h-20 min-h-0 w-32 shrink-0"
5394
+ presenterId,
5395
+ isLocal: presenterId === localParticipant?.id,
5396
+ className: "flex flex-1 flex-col overflow-hidden p-4 pb-2"
5262
5397
  }
5263
5398
  ),
5264
- remoteIds.map((id) => /* @__PURE__ */ jsxRuntime.jsx(
5265
- ParticipantView_default,
5266
- {
5267
- sdk,
5268
- participantId: id,
5269
- fallbackName: peerName,
5270
- fallbackImage: peerAvatar,
5271
- isHandRaised: !!raisedHands[id],
5272
- className: "aspect-video h-20 min-h-0 w-32 shrink-0"
5273
- },
5274
- id
5275
- ))
5276
- ] }),
5277
- controls
5278
- ] });
5279
- }
5280
- const showVideoGrid = mode === "video" || localWebcamOn;
5281
- if (!showVideoGrid) {
5282
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex flex-1 flex-col", children: [
5283
- /* @__PURE__ */ jsxRuntime.jsx(FloatingReactions_default, { reactions: floatingReactions }),
5284
- /* @__PURE__ */ jsxRuntime.jsx(
5285
- AudioCallView_default,
5286
- {
5287
- sdk,
5288
- remoteParticipantId: remoteIds[0],
5289
- peerName,
5290
- peerAvatar,
5291
- isRemoteHandRaised: !!(remoteIds[0] && raisedHands[remoteIds[0]])
5292
- }
5293
- ),
5294
- controls
5295
- ] });
5296
- }
5297
- const focusedId = groupView === "speaker" ? activeSpeakerId && activeSpeakerId !== localParticipant?.id ? activeSpeakerId : remoteIds[0] || localParticipant?.id : null;
5298
- const stripIds = focusedId ? [localParticipant?.id, ...remoteIds].filter(
5299
- (id) => !!id && id !== focusedId
5300
- ) : [];
5301
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex flex-1 flex-col", children: [
5302
- /* @__PURE__ */ jsxRuntime.jsx(FloatingReactions_default, { reactions: floatingReactions }),
5303
- isGroup && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-1 px-4 pt-2", children: [
5304
- /* @__PURE__ */ jsxRuntime.jsx(
5305
- "button",
5306
- {
5307
- type: "button",
5308
- "aria-label": "Grid view",
5309
- onClick: () => setGroupView("grid"),
5310
- className: cn(
5311
- "flex size-8 items-center justify-center rounded-lg transition-colors",
5312
- groupView === "grid" ? "bg-white/15 text-white" : "text-white/50 hover:text-white/80"
5313
- ),
5314
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.LayoutGrid, { size: 16 })
5315
- }
5316
- ),
5317
- /* @__PURE__ */ jsxRuntime.jsx(
5318
- "button",
5319
- {
5320
- type: "button",
5321
- "aria-label": "Speaker view",
5322
- onClick: () => setGroupView("speaker"),
5323
- className: cn(
5324
- "flex size-8 items-center justify-center rounded-lg transition-colors",
5325
- groupView === "speaker" ? "bg-white/15 text-white" : "text-white/50 hover:text-white/80"
5326
- ),
5327
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Rows3, { size: 16 })
5328
- }
5329
- )
5330
- ] }),
5331
- focusedId ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col gap-3 overflow-y-auto p-4", children: [
5332
- /* @__PURE__ */ jsxRuntime.jsx(
5333
- ParticipantView_default,
5334
- {
5335
- sdk,
5336
- participantId: focusedId,
5337
- isLocal: focusedId === localParticipant?.id,
5338
- fallbackName: peerName,
5339
- fallbackImage: peerAvatar,
5340
- isHandRaised: !!raisedHands[focusedId],
5341
- className: "min-h-[240px] flex-1"
5342
- }
5343
- ),
5344
- stripIds.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-2 overflow-x-auto", children: stripIds.map((id) => /* @__PURE__ */ jsxRuntime.jsx(
5345
- ParticipantView_default,
5346
- {
5347
- sdk,
5348
- participantId: id,
5349
- isLocal: id === localParticipant?.id,
5350
- fallbackName: peerName,
5351
- fallbackImage: peerAvatar,
5352
- isHandRaised: !!raisedHands[id],
5353
- className: "aspect-video h-20 min-h-0 w-32 shrink-0"
5354
- },
5355
- id
5356
- )) })
5357
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(
5358
- "div",
5359
- {
5360
- className: "grid flex-1 auto-rows-fr gap-3 overflow-y-auto p-4",
5361
- style: {
5362
- gridTemplateColumns: `repeat(${Math.min(
5363
- Math.max(remoteIds.length + 1, 1),
5364
- 3
5365
- )}, minmax(0, 1fr))`
5366
- },
5367
- children: [
5399
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2 overflow-x-auto px-4 pb-2", children: [
5368
5400
  localParticipant?.id && /* @__PURE__ */ jsxRuntime.jsx(
5369
5401
  ParticipantView_default,
5370
5402
  {
5371
5403
  sdk,
5372
5404
  participantId: localParticipant.id,
5373
5405
  isLocal: true,
5374
- isHandRaised: !!raisedHands[localParticipant.id]
5406
+ isHandRaised: !!raisedHands[localParticipant.id],
5407
+ className: "aspect-video h-20 min-h-0 w-32 shrink-0"
5375
5408
  }
5376
5409
  ),
5377
5410
  remoteIds.map((id) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -5381,14 +5414,137 @@ var init_MeetingView = __esm({
5381
5414
  participantId: id,
5382
5415
  fallbackName: peerName,
5383
5416
  fallbackImage: peerAvatar,
5384
- isHandRaised: !!raisedHands[id]
5417
+ isHandRaised: !!raisedHands[id],
5418
+ className: "aspect-video h-20 min-h-0 w-32 shrink-0"
5385
5419
  },
5386
5420
  id
5387
5421
  ))
5388
- ]
5389
- }
5390
- ),
5391
- controls
5422
+ ] }),
5423
+ controls
5424
+ ] })
5425
+ ] });
5426
+ }
5427
+ const anyRemoteWebcamOn = Object.values(remoteWebcamOn).some(Boolean);
5428
+ const showVideoGrid = mode === "video" || localWebcamOn || anyRemoteWebcamOn;
5429
+ if (!showVideoGrid) {
5430
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5431
+ webcamWatchers,
5432
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex flex-1 flex-col", children: [
5433
+ statusBanner,
5434
+ /* @__PURE__ */ jsxRuntime.jsx(FloatingReactions_default, { reactions: floatingReactions }),
5435
+ /* @__PURE__ */ jsxRuntime.jsx(
5436
+ AudioCallView_default,
5437
+ {
5438
+ sdk,
5439
+ remoteParticipantId: remoteIds[0],
5440
+ peerName,
5441
+ peerAvatar,
5442
+ isRemoteHandRaised: !!(remoteIds[0] && raisedHands[remoteIds[0]])
5443
+ }
5444
+ ),
5445
+ controls
5446
+ ] })
5447
+ ] });
5448
+ }
5449
+ const focusedId = groupView === "speaker" ? activeSpeakerId && activeSpeakerId !== localParticipant?.id ? activeSpeakerId : remoteIds[0] || localParticipant?.id : null;
5450
+ const stripIds = focusedId ? [localParticipant?.id, ...remoteIds].filter(
5451
+ (id) => !!id && id !== focusedId
5452
+ ) : [];
5453
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5454
+ webcamWatchers,
5455
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex flex-1 flex-col", children: [
5456
+ statusBanner,
5457
+ /* @__PURE__ */ jsxRuntime.jsx(FloatingReactions_default, { reactions: floatingReactions }),
5458
+ isGroup && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-1 px-4 pt-2", children: [
5459
+ /* @__PURE__ */ jsxRuntime.jsx(
5460
+ "button",
5461
+ {
5462
+ type: "button",
5463
+ "aria-label": "Grid view",
5464
+ onClick: () => setGroupView("grid"),
5465
+ className: cn(
5466
+ "flex size-8 items-center justify-center rounded-lg transition-colors",
5467
+ groupView === "grid" ? "bg-white/15 text-white" : "text-white/50 hover:text-white/80"
5468
+ ),
5469
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.LayoutGrid, { size: 16 })
5470
+ }
5471
+ ),
5472
+ /* @__PURE__ */ jsxRuntime.jsx(
5473
+ "button",
5474
+ {
5475
+ type: "button",
5476
+ "aria-label": "Speaker view",
5477
+ onClick: () => setGroupView("speaker"),
5478
+ className: cn(
5479
+ "flex size-8 items-center justify-center rounded-lg transition-colors",
5480
+ groupView === "speaker" ? "bg-white/15 text-white" : "text-white/50 hover:text-white/80"
5481
+ ),
5482
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Rows3, { size: 16 })
5483
+ }
5484
+ )
5485
+ ] }),
5486
+ focusedId ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col gap-3 overflow-y-auto p-4", children: [
5487
+ /* @__PURE__ */ jsxRuntime.jsx(
5488
+ ParticipantView_default,
5489
+ {
5490
+ sdk,
5491
+ participantId: focusedId,
5492
+ isLocal: focusedId === localParticipant?.id,
5493
+ fallbackName: peerName,
5494
+ fallbackImage: peerAvatar,
5495
+ isHandRaised: !!raisedHands[focusedId],
5496
+ className: "min-h-[240px] flex-1"
5497
+ }
5498
+ ),
5499
+ stripIds.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-2 overflow-x-auto", children: stripIds.map((id) => /* @__PURE__ */ jsxRuntime.jsx(
5500
+ ParticipantView_default,
5501
+ {
5502
+ sdk,
5503
+ participantId: id,
5504
+ isLocal: id === localParticipant?.id,
5505
+ fallbackName: peerName,
5506
+ fallbackImage: peerAvatar,
5507
+ isHandRaised: !!raisedHands[id],
5508
+ className: "aspect-video h-20 min-h-0 w-32 shrink-0"
5509
+ },
5510
+ id
5511
+ )) })
5512
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(
5513
+ "div",
5514
+ {
5515
+ className: "grid flex-1 auto-rows-fr gap-3 overflow-y-auto p-4",
5516
+ style: {
5517
+ gridTemplateColumns: `repeat(${Math.min(
5518
+ Math.max(remoteIds.length + 1, 1),
5519
+ 3
5520
+ )}, minmax(0, 1fr))`
5521
+ },
5522
+ children: [
5523
+ localParticipant?.id && /* @__PURE__ */ jsxRuntime.jsx(
5524
+ ParticipantView_default,
5525
+ {
5526
+ sdk,
5527
+ participantId: localParticipant.id,
5528
+ isLocal: true,
5529
+ isHandRaised: !!raisedHands[localParticipant.id]
5530
+ }
5531
+ ),
5532
+ remoteIds.map((id) => /* @__PURE__ */ jsxRuntime.jsx(
5533
+ ParticipantView_default,
5534
+ {
5535
+ sdk,
5536
+ participantId: id,
5537
+ fallbackName: peerName,
5538
+ fallbackImage: peerAvatar,
5539
+ isHandRaised: !!raisedHands[id]
5540
+ },
5541
+ id
5542
+ ))
5543
+ ]
5544
+ }
5545
+ ),
5546
+ controls
5547
+ ] })
5392
5548
  ] });
5393
5549
  };
5394
5550
  MeetingView_default = MeetingView;
@@ -5412,6 +5568,8 @@ var init_CallSession = __esm({
5412
5568
  mode,
5413
5569
  peerName,
5414
5570
  peerAvatar,
5571
+ isChatOpen,
5572
+ onToggleChat,
5415
5573
  onError
5416
5574
  }) => {
5417
5575
  const [sdk, setSdk] = React11.useState(null);
@@ -5454,7 +5612,17 @@ var init_CallSession = __esm({
5454
5612
  token,
5455
5613
  reinitialiseMeetingOnConfigChange: false,
5456
5614
  joinWithoutUserInteraction: true,
5457
- children: /* @__PURE__ */ jsxRuntime.jsx(MeetingView_default, { sdk, mode, peerName, peerAvatar })
5615
+ children: /* @__PURE__ */ jsxRuntime.jsx(
5616
+ MeetingView_default,
5617
+ {
5618
+ sdk,
5619
+ mode,
5620
+ peerName,
5621
+ peerAvatar,
5622
+ isChatOpen,
5623
+ onToggleChat
5624
+ }
5625
+ )
5458
5626
  }
5459
5627
  );
5460
5628
  };
@@ -5466,67 +5634,7 @@ var init_CallSession = __esm({
5466
5634
  init_useStore();
5467
5635
  init_api_service();
5468
5636
  init_socket_service2();
5469
-
5470
- // src/ui/tooltip.tsx
5471
- init_utils2();
5472
- function TooltipProvider({
5473
- delayDuration = 0,
5474
- ...props
5475
- }) {
5476
- return /* @__PURE__ */ jsxRuntime.jsx(
5477
- radixUi.Tooltip.Provider,
5478
- {
5479
- "data-slot": "tooltip-provider",
5480
- delayDuration,
5481
- ...props
5482
- }
5483
- );
5484
- }
5485
- function Tooltip({
5486
- ...props
5487
- }) {
5488
- return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Tooltip.Root, { "data-slot": "tooltip", ...props });
5489
- }
5490
- function TooltipTrigger({
5491
- ...props
5492
- }) {
5493
- return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Tooltip.Trigger, { "data-slot": "tooltip-trigger", ...props });
5494
- }
5495
- function TooltipContent({
5496
- className,
5497
- sideOffset = 0,
5498
- children,
5499
- showArrow = true,
5500
- arrowClassName,
5501
- ...props
5502
- }) {
5503
- return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Tooltip.Portal, { children: /* @__PURE__ */ jsxRuntime.jsxs(
5504
- radixUi.Tooltip.Content,
5505
- {
5506
- "data-slot": "tooltip-content",
5507
- sideOffset,
5508
- className: cn(
5509
- "bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
5510
- className
5511
- ),
5512
- ...props,
5513
- children: [
5514
- children,
5515
- showArrow ? /* @__PURE__ */ jsxRuntime.jsx(
5516
- radixUi.Tooltip.Arrow,
5517
- {
5518
- className: cn(
5519
- "bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]",
5520
- arrowClassName
5521
- )
5522
- }
5523
- ) : null
5524
- ]
5525
- }
5526
- ) });
5527
- }
5528
-
5529
- // src/chat/ChatContext.tsx
5637
+ init_tooltip();
5530
5638
  init_utils2();
5531
5639
 
5532
5640
  // src/config/chat-endpoints.ts
@@ -6447,11 +6555,153 @@ var useCallDuration = (startedAt) => {
6447
6555
  const pad = (n) => String(n).padStart(2, "0");
6448
6556
  return hours > 0 ? `${hours}:${pad(minutes)}:${pad(seconds)}` : `${pad(minutes)}:${pad(seconds)}`;
6449
6557
  };
6558
+
6559
+ // src/call/CallChatPanel.tsx
6560
+ init_useStore();
6561
+ init_store_helpers();
6562
+ init_utils2();
6563
+ init_button();
6564
+ var CallChatPanel = ({ conversationId, isGroupCall, onClose, className }) => {
6565
+ const rawMessages = exports.useChatStore((s) => s.messagesByConversation[conversationId]);
6566
+ const loggedUserDetails = exports.useChatStore((s) => s.loggedUserDetails);
6567
+ const conversations = exports.useChatStore((s) => s.conversations);
6568
+ const emitDmSend = exports.useChatStore((s) => s.emitDmSend);
6569
+ const emitGroupMessageSend2 = exports.useChatStore((s) => s.emitGroupMessageSend);
6570
+ const [text, setText] = React11.useState("");
6571
+ const [optimistic, setOptimistic] = React11.useState([]);
6572
+ const listEndRef = React11.useRef(null);
6573
+ const loggedUserId = String(loggedUserDetails?._id || "");
6574
+ const serverMessages = React11.useMemo(() => {
6575
+ return (rawMessages || []).filter((m) => !m?.isSystemMessage && !m?.isDeletedForEveryone).map((m) => {
6576
+ const senderId = String(m.sender?._id || m.sender || "");
6577
+ return {
6578
+ id: String(m._id || m.id || ""),
6579
+ content: String(m.content || ""),
6580
+ senderName: senderId === loggedUserId ? "You" : m.sender?.name || "Unknown",
6581
+ isOwn: senderId === loggedUserId,
6582
+ timestamp: m.createdAt || m.timestamp || (/* @__PURE__ */ new Date()).toISOString()
6583
+ };
6584
+ });
6585
+ }, [rawMessages, loggedUserId]);
6586
+ React11.useEffect(() => {
6587
+ if (!optimistic.length) return;
6588
+ setOptimistic(
6589
+ (prev) => prev.filter(
6590
+ (pending) => !serverMessages.some(
6591
+ (real) => real.isOwn && real.content === pending.content
6592
+ )
6593
+ )
6594
+ );
6595
+ }, [serverMessages]);
6596
+ const messages = React11.useMemo(
6597
+ () => [...serverMessages, ...optimistic].sort(
6598
+ (a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
6599
+ ),
6600
+ [serverMessages, optimistic]
6601
+ );
6602
+ React11.useEffect(() => {
6603
+ listEndRef.current?.scrollIntoView({ block: "end" });
6604
+ }, [messages.length]);
6605
+ const handleSend = () => {
6606
+ const content = text.trim();
6607
+ if (!content) return;
6608
+ setText("");
6609
+ setOptimistic((prev) => [
6610
+ ...prev,
6611
+ {
6612
+ id: `pending-${Date.now()}`,
6613
+ content,
6614
+ senderName: "You",
6615
+ isOwn: true,
6616
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
6617
+ pending: true
6618
+ }
6619
+ ]);
6620
+ const payload = {
6621
+ tempId: `temp-${Date.now()}`,
6622
+ sender: loggedUserId,
6623
+ content,
6624
+ type: "text",
6625
+ conversationId
6626
+ };
6627
+ if (isGroupCall) {
6628
+ emitGroupMessageSend2(payload);
6629
+ } else {
6630
+ const conversation = findConversationById(conversations, conversationId);
6631
+ payload.receiver = getOtherParticipantId(conversation, loggedUserId);
6632
+ emitDmSend(payload);
6633
+ }
6634
+ };
6635
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex flex-col border-l border-white/10 bg-neutral-950/95", className), children: [
6636
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between border-b border-white/10 px-4 py-3", children: [
6637
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-white/90", children: "In-call chat" }),
6638
+ /* @__PURE__ */ jsxRuntime.jsx(
6639
+ "button",
6640
+ {
6641
+ type: "button",
6642
+ "aria-label": "Close chat panel",
6643
+ onClick: onClose,
6644
+ className: "flex size-7 items-center justify-center rounded-lg text-white/60 transition-colors hover:bg-white/10 hover:text-white",
6645
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 16 })
6646
+ }
6647
+ )
6648
+ ] }),
6649
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 space-y-3 overflow-y-auto px-4 py-3", children: [
6650
+ messages.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "pt-6 text-center text-xs text-white/40", children: "No messages yet \u2014 say hi without leaving the call." }),
6651
+ messages.map((message) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex flex-col", message.isOwn && "items-end"), children: [
6652
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-0.5 text-[11px] font-medium text-white/40", children: message.isOwn ? "You" : message.senderName }),
6653
+ /* @__PURE__ */ jsxRuntime.jsx(
6654
+ "div",
6655
+ {
6656
+ className: cn(
6657
+ "max-w-[85%] rounded-2xl px-3 py-1.5 text-sm break-words",
6658
+ message.isOwn ? "rounded-br-sm bg-(--chat-theme,#4f46e5) text-white" : "rounded-bl-sm bg-white/10 text-white/90",
6659
+ message.pending && "opacity-60"
6660
+ ),
6661
+ children: message.content
6662
+ }
6663
+ )
6664
+ ] }, message.id)),
6665
+ /* @__PURE__ */ jsxRuntime.jsx("div", { ref: listEndRef })
6666
+ ] }),
6667
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 border-t border-white/10 p-3", children: [
6668
+ /* @__PURE__ */ jsxRuntime.jsx(
6669
+ "input",
6670
+ {
6671
+ value: text,
6672
+ onChange: (e) => setText(e.target.value),
6673
+ onKeyDown: (e) => {
6674
+ if (e.key === "Enter" && !e.shiftKey) {
6675
+ e.preventDefault();
6676
+ handleSend();
6677
+ }
6678
+ },
6679
+ placeholder: "Message\u2026",
6680
+ className: "min-w-0 flex-1 rounded-full border border-white/10 bg-white/5 px-3.5 py-2 text-sm text-white placeholder:text-white/40 focus:outline-none focus:ring-1 focus:ring-white/30"
6681
+ }
6682
+ ),
6683
+ /* @__PURE__ */ jsxRuntime.jsx(
6684
+ Button,
6685
+ {
6686
+ variant: "ghost",
6687
+ size: "icon",
6688
+ "aria-label": "Send message",
6689
+ disabled: !text.trim(),
6690
+ onClick: handleSend,
6691
+ className: "size-9 shrink-0 rounded-full bg-white/10 text-white hover:bg-white/20 disabled:opacity-30",
6692
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { size: 16 })
6693
+ }
6694
+ )
6695
+ ] })
6696
+ ] });
6697
+ };
6698
+ var CallChatPanel_default = CallChatPanel;
6450
6699
  var CallSession2 = React11__namespace.default.lazy(() => Promise.resolve().then(() => (init_CallSession(), CallSession_exports)));
6451
6700
  var CallOverlay = () => {
6452
6701
  const uiStatus = exports.useCallStore((s) => s.uiStatus);
6453
6702
  const mode = exports.useCallStore((s) => s.mode);
6454
6703
  const callId = exports.useCallStore((s) => s.callId);
6704
+ const conversationId = exports.useCallStore((s) => s.conversationId);
6455
6705
  const meetingId = exports.useCallStore((s) => s.meetingId);
6456
6706
  const token = exports.useCallStore((s) => s.token);
6457
6707
  const error = exports.useCallStore((s) => s.error);
@@ -6463,112 +6713,111 @@ var CallOverlay = () => {
6463
6713
  const reset = exports.useCallStore((s) => s.reset);
6464
6714
  const notifyServerCallFailed = exports.useCallStore((s) => s.notifyServerCallFailed);
6465
6715
  const [sessionError, setSessionError] = React11.useState(null);
6716
+ const [isChatOpen, setIsChatOpen] = React11.useState(false);
6466
6717
  const secondsLeft = useRingCountdown(uiStatus === "ringing-out", callId);
6467
6718
  const duration = useCallDuration(callStartedAt);
6468
6719
  React11.useEffect(() => {
6469
6720
  setSessionError(null);
6721
+ setIsChatOpen(false);
6470
6722
  }, [callId]);
6471
6723
  React11.useEffect(() => {
6472
6724
  if (sessionError) notifyServerCallFailed();
6473
6725
  }, [sessionError, notifyServerCallFailed]);
6474
6726
  const isOpen = uiStatus !== "idle" && uiStatus !== "incoming";
6475
- if (!isOpen || typeof document === "undefined") return null;
6727
+ if (!isOpen) return null;
6476
6728
  const displayError = error || sessionError;
6477
6729
  const peerName = peer?.name || caller?.name || "";
6478
6730
  const peerAvatar = peer?.avatar || caller?.profileImage || void 0;
6479
6731
  const isVideo = mode === "video";
6480
- return reactDom.createPortal(
6481
- /* @__PURE__ */ jsxRuntime.jsx(
6482
- "div",
6483
- {
6484
- style: {
6485
- position: "fixed",
6486
- inset: 0,
6487
- width: "100vw",
6488
- height: "100vh",
6489
- zIndex: 2147483647
6490
- },
6491
- className: "flex flex-col overflow-hidden bg-gradient-to-b from-neutral-900 via-neutral-950 to-black text-white",
6492
- children: displayError ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col items-center justify-center gap-4 p-6 text-center", children: [
6493
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "max-w-sm text-sm text-white/80", children: displayError }),
6494
- /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: reset, children: [
6495
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 16, className: "mr-1" }),
6496
- " Close"
6497
- ] })
6498
- ] }) : uiStatus === "in-call" && meetingId && token && mode ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col", children: [
6499
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-2 px-4 py-3 text-sm text-white/70", children: [
6500
- isGroupCall && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Users, { size: 14, className: "text-white/50" }),
6501
- peerName && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-white/90", children: peerName }),
6502
- /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, className: "text-white/30", children: "\xB7" }),
6503
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tabular-nums", children: duration })
6732
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 z-40 flex flex-col overflow-hidden bg-gradient-to-b from-neutral-900 via-neutral-950 to-black text-white", children: displayError ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col items-center justify-center gap-4 p-6 text-center", children: [
6733
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "max-w-sm text-sm text-white/80", children: displayError }),
6734
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: reset, children: [
6735
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 16, className: "mr-1" }),
6736
+ " Close"
6737
+ ] })
6738
+ ] }) : uiStatus === "in-call" && meetingId && token && mode && conversationId ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col min-h-0", children: [
6739
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center justify-center gap-2 px-4 py-3 text-sm text-white/70", children: [
6740
+ isGroupCall && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Users, { size: 14, className: "text-white/50" }),
6741
+ peerName && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-white/90", children: peerName }),
6742
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, className: "text-white/30", children: "\xB7" }),
6743
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tabular-nums", children: duration })
6744
+ ] }),
6745
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 min-h-0", children: [
6746
+ /* @__PURE__ */ jsxRuntime.jsx(
6747
+ React11__namespace.default.Suspense,
6748
+ {
6749
+ fallback: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col items-center justify-center gap-3 text-white/80", children: [
6750
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "size-8 animate-spin" }),
6751
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm", children: "Loading call\u2026" })
6504
6752
  ] }),
6505
- /* @__PURE__ */ jsxRuntime.jsx(
6506
- React11__namespace.default.Suspense,
6753
+ children: /* @__PURE__ */ jsxRuntime.jsx(
6754
+ CallSession2,
6507
6755
  {
6508
- fallback: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col items-center justify-center gap-3 text-white/80", children: [
6509
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "size-8 animate-spin" }),
6510
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm", children: "Loading call\u2026" })
6511
- ] }),
6512
- children: /* @__PURE__ */ jsxRuntime.jsx(
6513
- CallSession2,
6514
- {
6515
- meetingId,
6516
- token,
6517
- mode,
6518
- peerName,
6519
- peerAvatar,
6520
- onError: setSessionError
6521
- }
6522
- )
6756
+ meetingId,
6757
+ token,
6758
+ mode,
6759
+ peerName,
6760
+ peerAvatar,
6761
+ isChatOpen,
6762
+ onToggleChat: () => setIsChatOpen((v) => !v),
6763
+ onError: setSessionError
6523
6764
  }
6524
6765
  )
6525
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col items-center justify-center gap-6 p-6 text-center", children: [
6526
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex items-center justify-center", children: [
6527
- uiStatus === "ringing-out" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
6528
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute inline-flex size-32 animate-ping rounded-full bg-white/10" }),
6529
- /* @__PURE__ */ jsxRuntime.jsx(
6530
- "span",
6531
- {
6532
- className: "absolute inline-flex size-32 animate-ping rounded-full bg-white/5",
6533
- style: { animationDelay: "0.6s" }
6534
- }
6535
- )
6536
- ] }),
6537
- peerName ? /* @__PURE__ */ jsxRuntime.jsxs(Avatar, { className: "relative size-28 ring-4 ring-white/10", children: [
6538
- /* @__PURE__ */ jsxRuntime.jsx(AvatarImage, { src: peerAvatar, alt: peerName }),
6539
- /* @__PURE__ */ jsxRuntime.jsx(AvatarFallback, { className: "bg-neutral-700 text-3xl font-medium text-white", children: peerName.charAt(0).toUpperCase() })
6540
- ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative flex size-28 items-center justify-center rounded-full bg-white/10", children: isVideo ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Video, { size: 36 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Phone, { size: 36 }) })
6541
- ] }),
6542
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
6543
- peerName && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "flex items-center justify-center gap-1.5 text-xl font-semibold tracking-tight", children: [
6544
- isGroupCall && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Users, { size: 16, className: "text-white/50" }),
6545
- peerName
6546
- ] }),
6547
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-1 text-sm text-white/60", children: [
6548
- uiStatus === "creating" && "Starting call\u2026",
6549
- uiStatus === "ringing-out" && `${isVideo ? "Video calling" : "Calling"}\u2026 \xB7 ${secondsLeft}s`,
6550
- uiStatus === "joining" && "Connecting\u2026"
6551
- ] })
6552
- ] }),
6553
- uiStatus === "ringing-out" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 flex flex-col items-center gap-2", children: [
6554
- /* @__PURE__ */ jsxRuntime.jsx(
6555
- Button,
6556
- {
6557
- variant: "ghost",
6558
- size: "icon",
6559
- className: "size-16 rounded-full bg-red-600 text-white shadow-lg shadow-red-600/30 transition-transform hover:scale-105 hover:bg-red-500 active:scale-95",
6560
- "aria-label": "Cancel call",
6561
- onClick: cancelCall,
6562
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PhoneOff, { size: 24 })
6563
- }
6564
- ),
6565
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-white/60", children: "Cancel" })
6566
- ] })
6567
- ] })
6568
- }
6569
- ),
6570
- document.body
6571
- );
6766
+ }
6767
+ ),
6768
+ isChatOpen && /* @__PURE__ */ jsxRuntime.jsx(
6769
+ CallChatPanel_default,
6770
+ {
6771
+ conversationId,
6772
+ isGroupCall,
6773
+ onClose: () => setIsChatOpen(false),
6774
+ className: "hidden w-80 shrink-0 sm:flex"
6775
+ }
6776
+ )
6777
+ ] })
6778
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col items-center justify-center gap-6 p-6 text-center", children: [
6779
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex items-center justify-center", children: [
6780
+ uiStatus === "ringing-out" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
6781
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute inline-flex size-32 animate-ping rounded-full bg-white/10" }),
6782
+ /* @__PURE__ */ jsxRuntime.jsx(
6783
+ "span",
6784
+ {
6785
+ className: "absolute inline-flex size-32 animate-ping rounded-full bg-white/5",
6786
+ style: { animationDelay: "0.6s" }
6787
+ }
6788
+ )
6789
+ ] }),
6790
+ peerName ? /* @__PURE__ */ jsxRuntime.jsxs(Avatar, { className: "relative size-28 ring-4 ring-white/10", children: [
6791
+ /* @__PURE__ */ jsxRuntime.jsx(AvatarImage, { src: peerAvatar, alt: peerName }),
6792
+ /* @__PURE__ */ jsxRuntime.jsx(AvatarFallback, { className: "bg-neutral-700 text-3xl font-medium text-white", children: peerName.charAt(0).toUpperCase() })
6793
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative flex size-28 items-center justify-center rounded-full bg-white/10", children: isVideo ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Video, { size: 36 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Phone, { size: 36 }) })
6794
+ ] }),
6795
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
6796
+ peerName && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "flex items-center justify-center gap-1.5 text-xl font-semibold tracking-tight", children: [
6797
+ isGroupCall && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Users, { size: 16, className: "text-white/50" }),
6798
+ peerName
6799
+ ] }),
6800
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-1 text-sm text-white/60", children: [
6801
+ uiStatus === "creating" && "Starting call\u2026",
6802
+ uiStatus === "ringing-out" && `${isVideo ? "Video calling" : "Calling"}\u2026 \xB7 ${secondsLeft}s`,
6803
+ uiStatus === "joining" && "Connecting\u2026"
6804
+ ] })
6805
+ ] }),
6806
+ uiStatus === "ringing-out" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 flex flex-col items-center gap-2", children: [
6807
+ /* @__PURE__ */ jsxRuntime.jsx(
6808
+ Button,
6809
+ {
6810
+ variant: "ghost",
6811
+ size: "icon",
6812
+ className: "size-16 rounded-full bg-red-600 text-white shadow-lg shadow-red-600/30 transition-transform hover:scale-105 hover:bg-red-500 active:scale-95",
6813
+ "aria-label": "Cancel call",
6814
+ onClick: cancelCall,
6815
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PhoneOff, { size: 24 })
6816
+ }
6817
+ ),
6818
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-white/60", children: "Cancel" })
6819
+ ] })
6820
+ ] }) });
6572
6821
  };
6573
6822
  var CallOverlay_default = CallOverlay;
6574
6823
 
@@ -7404,7 +7653,7 @@ var Chat = ({
7404
7653
  }),
7405
7654
  [uiConfig.components, uiConfig.classNames, uiConfig.features]
7406
7655
  );
7407
- return /* @__PURE__ */ jsxRuntime.jsx(ChatContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx(ChatCustomizationProvider, { customization, children: /* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "chat-container-root flex h-full min-h-0 w-full flex-1 flex-col overflow-hidden", children: isSessionReady ? /* @__PURE__ */ jsxRuntime.jsxs(ChatEffectiveSettingsProvider, { enabled: true, children: [
7656
+ return /* @__PURE__ */ jsxRuntime.jsx(ChatContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx(ChatCustomizationProvider, { customization, children: /* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "chat-container-root relative flex h-full min-h-0 w-full flex-1 flex-col overflow-hidden", children: isSessionReady ? /* @__PURE__ */ jsxRuntime.jsxs(ChatEffectiveSettingsProvider, { enabled: true, children: [
7408
7657
  children,
7409
7658
  /* @__PURE__ */ jsxRuntime.jsx(CallLayer_default, {})
7410
7659
  ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center text-gray-400 font-bold", children: "Initializing session..." }) }) }) }) });
@@ -7861,6 +8110,7 @@ init_avatar();
7861
8110
 
7862
8111
  // src/chat/ChatThemeToggle.tsx
7863
8112
  init_button();
8113
+ init_tooltip();
7864
8114
  init_utils2();
7865
8115
  var ChatThemeToggle = ({ className }) => {
7866
8116
  const { isDark, toggleColorMode } = useChatTheme();
@@ -7918,6 +8168,7 @@ var buildLoggedUserProfile = (loggedUserDetails) => ({
7918
8168
 
7919
8169
  // src/chat/sidebar/create-chat-dialog/CreateChatDialog.tsx
7920
8170
  init_button();
8171
+ init_tooltip();
7921
8172
  init_utils2();
7922
8173
 
7923
8174
  // src/chat/sidebar/create-chat-dialog/useCreateChatDialog.ts
@@ -8368,6 +8619,9 @@ function GroupCreateTab({
8368
8619
  ) })
8369
8620
  ] });
8370
8621
  }
8622
+
8623
+ // src/chat/components/AdminPermissionTooltip.tsx
8624
+ init_tooltip();
8371
8625
  init_utils2();
8372
8626
  function AdminPermissionTooltip({
8373
8627
  disabled,
@@ -8994,6 +9248,7 @@ var LoggedUserSettingsContent_default = LoggedUserSettingsContent;
8994
9248
 
8995
9249
  // src/chat/sidebar/LoggedUserSettingsTrigger.tsx
8996
9250
  init_button();
9251
+ init_tooltip();
8997
9252
  init_utils2();
8998
9253
  var LoggedUserSettingsTrigger = ({
8999
9254
  onClick,
@@ -10660,6 +10915,7 @@ init_call_store();
10660
10915
  init_utils2();
10661
10916
 
10662
10917
  // src/chat/header/header-right/OptionsMenuItem.tsx
10918
+ init_tooltip();
10663
10919
  init_utils2();
10664
10920
  function OptionsMenuItem({
10665
10921
  icon: Icon,
@@ -16342,6 +16598,7 @@ init_avatar();
16342
16598
 
16343
16599
  // src/chat/message/MessageReactions.tsx
16344
16600
  init_utils2();
16601
+ init_tooltip();
16345
16602
  var MessageReactions = ({
16346
16603
  groupedReactions,
16347
16604
  reactions,