@livo-tv/sdk 1.6.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4109,227 +4109,223 @@ function StudioToolbar({
4109
4109
  const showStageModerator = session.role === "moderator" && canManageStage;
4110
4110
  const showStageGuest = session.role === "guest" && stageEnabled;
4111
4111
  const captionsLabel = !captionsAvailable ? labels.captions : !captionsReady ? labels.captionsNotReady : captionsOn ? labels.captionsOn : labels.captionsOff;
4112
- return /* @__PURE__ */ jsxRuntime.jsx("footer", { className: "flex flex-wrap items-center justify-center gap-3 border-t px-3 py-3 sm:justify-between", children: /* @__PURE__ */ jsxRuntime.jsxs(TooltipProvider, { children: [
4113
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-center gap-2", children: [
4114
- showMediaControls ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4115
- /* @__PURE__ */ jsxRuntime.jsx(
4116
- MediaSplit,
4117
- {
4118
- label: muted ? labels.unmute : labels.mute,
4119
- pressed: muted,
4120
- tone: muted ? "danger" : "default",
4121
- onClick: onToggleMute,
4122
- pickerLabel: labels.micDevice,
4123
- options: devices.audio,
4124
- selected: effective.audio,
4125
- onSelect: (id) => void onDeviceChange("audio", id),
4126
- children: muted ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MicOff, {}) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Mic, {})
4127
- }
4128
- ),
4129
- /* @__PURE__ */ jsxRuntime.jsx(
4130
- MediaSplit,
4131
- {
4132
- label: labels.camera,
4133
- pressed: cameraOn,
4134
- onClick: onToggleCamera,
4135
- pickerLabel: labels.cameraDevice,
4136
- options: devices.video,
4137
- selected: effective.video,
4138
- onSelect: (id) => void onDeviceChange("video", id),
4139
- children: cameraOn ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Video, {}) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.VideoOff, {})
4140
- }
4141
- )
4142
- ] }) : null,
4143
- showStageModerator ? /* @__PURE__ */ jsxRuntime.jsx(
4144
- ControlIcon,
4145
- {
4146
- label: selfStageStatus === "ON_STAGE" ? labels.goOffAir : labels.goOnAir,
4147
- pressed: selfStageStatus !== "ON_STAGE",
4148
- disabled: selfStageStatus === "ON_STAGE" && !canLeaveStage,
4149
- onClick: () => {
4150
- if (selfStageStatus === "ON_STAGE") {
4151
- if (!canLeaveStage) {
4152
- sonner.toast.message(labels.offAirLastVisible);
4153
- return;
4154
- }
4155
- onLeaveStage();
4156
- return;
4157
- }
4158
- onAskStage();
4159
- },
4160
- children: selfStageStatus === "ON_STAGE" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.EyeOff, {}) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Eye, {})
4161
- }
4162
- ) : null,
4163
- showStageGuest ? /* @__PURE__ */ jsxRuntime.jsx(
4164
- ControlIcon,
4165
- {
4166
- label: selfStageStatus === "ON_STAGE" ? labels.goOffAir : selfStageStatus === "REQUESTED_TO_JOIN_STAGE" ? labels.stageRequested : labels.askToGoOnAir,
4167
- pressed: selfStageStatus === "REQUESTED_TO_JOIN_STAGE",
4168
- disabled: selfStageStatus === "REQUESTED_TO_JOIN_STAGE" || selfStageStatus === "ON_STAGE" && !canLeaveStage,
4169
- onClick: () => {
4170
- if (selfStageStatus === "ON_STAGE") {
4171
- if (!canLeaveStage) {
4172
- sonner.toast.message(labels.offAirLastVisible);
4173
- return;
4174
- }
4175
- onLeaveStage();
4176
- return;
4177
- }
4178
- onAskStage();
4179
- },
4180
- children: selfStageStatus === "ON_STAGE" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.EyeOff, {}) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Eye, {})
4181
- }
4182
- ) : null,
4183
- showScreenControl ? canShareFreely ? /* @__PURE__ */ jsxRuntime.jsx(
4184
- ControlIcon,
4112
+ return /* @__PURE__ */ jsxRuntime.jsx("footer", { className: "border-t px-3 py-3", children: /* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-center gap-2", children: [
4113
+ showMediaControls ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4114
+ /* @__PURE__ */ jsxRuntime.jsx(
4115
+ MediaSplit,
4185
4116
  {
4186
- label: labels.screen,
4187
- pressed: screenOn,
4188
- disabled: screenPending || !screenAvailable && !screenOn,
4189
- pending: screenPending,
4190
- onClick: onToggleScreen,
4191
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MonitorUp, {})
4117
+ label: muted ? labels.unmute : labels.mute,
4118
+ pressed: muted,
4119
+ tone: muted ? "danger" : "default",
4120
+ onClick: onToggleMute,
4121
+ pickerLabel: labels.micDevice,
4122
+ options: devices.audio,
4123
+ selected: effective.audio,
4124
+ onSelect: (id) => void onDeviceChange("audio", id),
4125
+ children: muted ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MicOff, {}) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Mic, {})
4192
4126
  }
4193
- ) : shareGrant === "requested" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4194
- /* @__PURE__ */ jsxRuntime.jsx(
4195
- ControlIcon,
4196
- {
4197
- label: labels.shareRequested,
4198
- pressed: true,
4199
- disabled: true,
4200
- onClick: () => void 0,
4201
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MonitorUp, {})
4202
- }
4203
- ),
4204
- /* @__PURE__ */ jsxRuntime.jsx(
4205
- ControlIcon,
4206
- {
4207
- label: labels.cancelShareRequest,
4208
- onClick: onCancelShareRequest,
4209
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, {})
4210
- }
4211
- )
4212
- ] }) : /* @__PURE__ */ jsxRuntime.jsx(ControlIcon, { label: labels.askToShare, onClick: onAskToShare, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MonitorUp, {}) }) : null,
4127
+ ),
4213
4128
  /* @__PURE__ */ jsxRuntime.jsx(
4214
- ControlIcon,
4129
+ MediaSplit,
4215
4130
  {
4216
- label: captionsLabel,
4217
- pressed: captionsOn,
4218
- disabled: !captionsAvailable,
4219
- onClick: onToggleCaptions,
4220
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Captions, {})
4131
+ label: labels.camera,
4132
+ pressed: cameraOn,
4133
+ onClick: onToggleCamera,
4134
+ pickerLabel: labels.cameraDevice,
4135
+ options: devices.video,
4136
+ selected: effective.video,
4137
+ onSelect: (id) => void onDeviceChange("video", id),
4138
+ children: cameraOn ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Video, {}) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.VideoOff, {})
4221
4139
  }
4222
- ),
4223
- /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenu, { children: [
4224
- /* @__PURE__ */ jsxRuntime.jsx(
4225
- DropdownMenuTrigger,
4226
- {
4227
- render: /* @__PURE__ */ jsxRuntime.jsx(
4228
- Button,
4229
- {
4230
- type: "button",
4231
- size: "icon-lg",
4232
- variant: "secondary",
4233
- "aria-label": labels.more
4234
- }
4235
- ),
4236
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MoreVertical, {})
4140
+ )
4141
+ ] }) : null,
4142
+ showStageModerator ? /* @__PURE__ */ jsxRuntime.jsx(
4143
+ ControlIcon,
4144
+ {
4145
+ label: selfStageStatus === "ON_STAGE" ? labels.goOffAir : labels.goOnAir,
4146
+ pressed: selfStageStatus !== "ON_STAGE",
4147
+ disabled: selfStageStatus === "ON_STAGE" && !canLeaveStage,
4148
+ onClick: () => {
4149
+ if (selfStageStatus === "ON_STAGE") {
4150
+ if (!canLeaveStage) {
4151
+ sonner.toast.message(labels.offAirLastVisible);
4152
+ return;
4153
+ }
4154
+ onLeaveStage();
4155
+ return;
4237
4156
  }
4238
- ),
4239
- /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenuContent, { align: "center", className: "min-w-44", children: [
4240
- fullscreenSupported ? /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenuItem, { onClick: () => void enterFullscreen(), children: [
4241
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Maximize, {}),
4242
- labels.fullscreen
4243
- ] }) : null,
4244
- pipSupported ? /* @__PURE__ */ jsxRuntime.jsxs(
4245
- DropdownMenuItem,
4246
- {
4247
- onClick: () => {
4248
- if (pipOpen) onClosePip();
4249
- else onOpenPip();
4250
- },
4251
- children: [
4252
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PictureInPicture2, {}),
4253
- pipOpen ? labels.closePip : labels.openPip
4254
- ]
4157
+ onAskStage();
4158
+ },
4159
+ children: selfStageStatus === "ON_STAGE" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.EyeOff, {}) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Eye, {})
4160
+ }
4161
+ ) : null,
4162
+ showStageGuest ? /* @__PURE__ */ jsxRuntime.jsx(
4163
+ ControlIcon,
4164
+ {
4165
+ label: selfStageStatus === "ON_STAGE" ? labels.goOffAir : selfStageStatus === "REQUESTED_TO_JOIN_STAGE" ? labels.stageRequested : labels.askToGoOnAir,
4166
+ pressed: selfStageStatus === "REQUESTED_TO_JOIN_STAGE",
4167
+ disabled: selfStageStatus === "REQUESTED_TO_JOIN_STAGE" || selfStageStatus === "ON_STAGE" && !canLeaveStage,
4168
+ onClick: () => {
4169
+ if (selfStageStatus === "ON_STAGE") {
4170
+ if (!canLeaveStage) {
4171
+ sonner.toast.message(labels.offAirLastVisible);
4172
+ return;
4255
4173
  }
4256
- ) : null,
4257
- /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenuItem, { onClick: onOpenSettings, children: [
4258
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Settings, {}),
4259
- labels.settings
4260
- ] })
4261
- ] })
4262
- ] })
4263
- ] }),
4264
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-center gap-2", children: [
4174
+ onLeaveStage();
4175
+ return;
4176
+ }
4177
+ onAskStage();
4178
+ },
4179
+ children: selfStageStatus === "ON_STAGE" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.EyeOff, {}) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Eye, {})
4180
+ }
4181
+ ) : null,
4182
+ showScreenControl ? canShareFreely ? /* @__PURE__ */ jsxRuntime.jsx(
4183
+ ControlIcon,
4184
+ {
4185
+ label: labels.screen,
4186
+ pressed: screenOn,
4187
+ disabled: screenPending || !screenAvailable && !screenOn,
4188
+ pending: screenPending,
4189
+ onClick: onToggleScreen,
4190
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MonitorUp, {})
4191
+ }
4192
+ ) : shareGrant === "requested" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4265
4193
  /* @__PURE__ */ jsxRuntime.jsx(
4266
4194
  ControlIcon,
4267
4195
  {
4268
- label: unreadChat > 0 ? `${labels.chat} (${unreadChat})` : labels.chat,
4269
- pressed: chatOpen,
4270
- badge: unreadChat,
4271
- onClick: onToggleChat,
4272
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquare, {})
4196
+ label: labels.shareRequested,
4197
+ pressed: true,
4198
+ disabled: true,
4199
+ onClick: () => void 0,
4200
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MonitorUp, {})
4273
4201
  }
4274
4202
  ),
4275
- session.role === "moderator" ? /* @__PURE__ */ jsxRuntime.jsx(
4276
- ControlIcon,
4277
- {
4278
- label: peopleBadge > 0 ? `${labels.people} (${peopleBadge})` : labels.people,
4279
- pressed: peopleOpen,
4280
- badge: peopleBadge,
4281
- onClick: onTogglePeople,
4282
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Users, {})
4283
- }
4284
- ) : null,
4285
4203
  /* @__PURE__ */ jsxRuntime.jsx(
4286
4204
  ControlIcon,
4287
4205
  {
4288
- label: labels.settings,
4289
- pressed: settingsOpen,
4290
- onClick: onOpenSettings,
4291
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Settings, {})
4206
+ label: labels.cancelShareRequest,
4207
+ onClick: onCancelShareRequest,
4208
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, {})
4292
4209
  }
4293
- ),
4210
+ )
4211
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(ControlIcon, { label: labels.askToShare, onClick: onAskToShare, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MonitorUp, {}) }) : null,
4212
+ /* @__PURE__ */ jsxRuntime.jsx(
4213
+ ControlIcon,
4214
+ {
4215
+ label: captionsLabel,
4216
+ pressed: captionsOn,
4217
+ disabled: !captionsAvailable,
4218
+ onClick: onToggleCaptions,
4219
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Captions, {})
4220
+ }
4221
+ ),
4222
+ /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenu, { children: [
4294
4223
  /* @__PURE__ */ jsxRuntime.jsx(
4295
- StudioSettingsDialog,
4296
- {
4297
- open: settingsOpen,
4298
- onOpenChange: onCloseSettings,
4299
- meeting,
4300
- reducedMotion,
4301
- cameraOn,
4302
- labels,
4303
- captionPrefs,
4304
- onCaptionPrefsChange,
4305
- captionsAvailable,
4306
- effectBackgrounds
4307
- }
4308
- ),
4309
- session.role === "guest" ? /* @__PURE__ */ jsxRuntime.jsx(
4310
- ConfirmAction,
4224
+ DropdownMenuTrigger,
4311
4225
  {
4312
- triggerLabel: labels.leave,
4313
- title: labels.leaveTitle,
4314
- description: labels.leaveDescription,
4315
- confirmLabel: labels.leaveConfirm,
4316
- pending,
4317
- onConfirm: onLeave,
4318
- trigger: /* @__PURE__ */ jsxRuntime.jsx(
4226
+ render: /* @__PURE__ */ jsxRuntime.jsx(
4319
4227
  Button,
4320
4228
  {
4321
4229
  type: "button",
4322
- variant: "destructive",
4323
4230
  size: "icon-lg",
4324
- disabled: pending,
4325
- "aria-label": labels.leave,
4326
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PhoneOff, {})
4231
+ variant: "secondary",
4232
+ "aria-label": labels.more
4327
4233
  }
4328
- )
4234
+ ),
4235
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MoreVertical, {})
4329
4236
  }
4330
- ) : null
4331
- ] })
4332
- ] }) });
4237
+ ),
4238
+ /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenuContent, { align: "center", className: "min-w-44", children: [
4239
+ fullscreenSupported ? /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenuItem, { onClick: () => void enterFullscreen(), children: [
4240
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Maximize, {}),
4241
+ labels.fullscreen
4242
+ ] }) : null,
4243
+ pipSupported ? /* @__PURE__ */ jsxRuntime.jsxs(
4244
+ DropdownMenuItem,
4245
+ {
4246
+ onClick: () => {
4247
+ if (pipOpen) onClosePip();
4248
+ else onOpenPip();
4249
+ },
4250
+ children: [
4251
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PictureInPicture2, {}),
4252
+ pipOpen ? labels.closePip : labels.openPip
4253
+ ]
4254
+ }
4255
+ ) : null,
4256
+ /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenuItem, { onClick: onOpenSettings, children: [
4257
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Settings, {}),
4258
+ labels.settings
4259
+ ] })
4260
+ ] })
4261
+ ] }),
4262
+ /* @__PURE__ */ jsxRuntime.jsx(
4263
+ ControlIcon,
4264
+ {
4265
+ label: unreadChat > 0 ? `${labels.chat} (${unreadChat})` : labels.chat,
4266
+ pressed: chatOpen,
4267
+ badge: unreadChat,
4268
+ onClick: onToggleChat,
4269
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquare, {})
4270
+ }
4271
+ ),
4272
+ session.role === "moderator" ? /* @__PURE__ */ jsxRuntime.jsx(
4273
+ ControlIcon,
4274
+ {
4275
+ label: peopleBadge > 0 ? `${labels.people} (${peopleBadge})` : labels.people,
4276
+ pressed: peopleOpen,
4277
+ badge: peopleBadge,
4278
+ onClick: onTogglePeople,
4279
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Users, {})
4280
+ }
4281
+ ) : null,
4282
+ /* @__PURE__ */ jsxRuntime.jsx(
4283
+ ControlIcon,
4284
+ {
4285
+ label: labels.settings,
4286
+ pressed: settingsOpen,
4287
+ onClick: onOpenSettings,
4288
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Settings, {})
4289
+ }
4290
+ ),
4291
+ /* @__PURE__ */ jsxRuntime.jsx(
4292
+ StudioSettingsDialog,
4293
+ {
4294
+ open: settingsOpen,
4295
+ onOpenChange: onCloseSettings,
4296
+ meeting,
4297
+ reducedMotion,
4298
+ cameraOn,
4299
+ labels,
4300
+ captionPrefs,
4301
+ onCaptionPrefsChange,
4302
+ captionsAvailable,
4303
+ effectBackgrounds
4304
+ }
4305
+ ),
4306
+ session.role === "guest" ? /* @__PURE__ */ jsxRuntime.jsx(
4307
+ ConfirmAction,
4308
+ {
4309
+ triggerLabel: labels.leave,
4310
+ title: labels.leaveTitle,
4311
+ description: labels.leaveDescription,
4312
+ confirmLabel: labels.leaveConfirm,
4313
+ pending,
4314
+ onConfirm: onLeave,
4315
+ trigger: /* @__PURE__ */ jsxRuntime.jsx(
4316
+ Button,
4317
+ {
4318
+ type: "button",
4319
+ variant: "destructive",
4320
+ size: "icon-lg",
4321
+ disabled: pending,
4322
+ "aria-label": labels.leave,
4323
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PhoneOff, {})
4324
+ }
4325
+ )
4326
+ }
4327
+ ) : null
4328
+ ] }) }) });
4333
4329
  }
4334
4330
  function MediaSplit({
4335
4331
  label,
@@ -4342,8 +4338,8 @@ function MediaSplit({
4342
4338
  onSelect,
4343
4339
  children
4344
4340
  }) {
4345
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
4346
- /* @__PURE__ */ jsxRuntime.jsx(
4341
+ if (options.length === 0) {
4342
+ return /* @__PURE__ */ jsxRuntime.jsx(
4347
4343
  ControlIcon,
4348
4344
  {
4349
4345
  label,
@@ -4352,8 +4348,11 @@ function MediaSplit({
4352
4348
  onClick,
4353
4349
  children
4354
4350
  }
4355
- ),
4356
- options.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenu, { children: [
4351
+ );
4352
+ }
4353
+ const iconVariant = tone === "danger" ? "destructive" : pressed ? "default" : "secondary";
4354
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-secondary inline-flex h-10 items-center rounded-full p-0.5", children: [
4355
+ /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenu, { children: [
4357
4356
  /* @__PURE__ */ jsxRuntime.jsx(
4358
4357
  DropdownMenuTrigger,
4359
4358
  {
@@ -4362,8 +4361,8 @@ function MediaSplit({
4362
4361
  {
4363
4362
  type: "button",
4364
4363
  size: "icon-xs",
4365
- variant: "secondary",
4366
- className: "-ml-1",
4364
+ variant: "ghost",
4365
+ className: "text-muted-foreground hover:text-foreground h-9 w-8 rounded-full hover:bg-transparent",
4367
4366
  "aria-label": pickerLabel
4368
4367
  }
4369
4368
  ),
@@ -4381,7 +4380,28 @@ function MediaSplit({
4381
4380
  },
4382
4381
  option.deviceId
4383
4382
  )) })
4384
- ] }) : null
4383
+ ] }),
4384
+ /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
4385
+ /* @__PURE__ */ jsxRuntime.jsx(
4386
+ TooltipTrigger,
4387
+ {
4388
+ render: /* @__PURE__ */ jsxRuntime.jsx(
4389
+ Button,
4390
+ {
4391
+ type: "button",
4392
+ size: "icon-lg",
4393
+ variant: iconVariant,
4394
+ className: iconVariant === "secondary" ? "rounded-full bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_10%)] hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_16%)]" : "rounded-full",
4395
+ "aria-label": label,
4396
+ "aria-pressed": pressed,
4397
+ onClick
4398
+ }
4399
+ ),
4400
+ children
4401
+ }
4402
+ ),
4403
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: label })
4404
+ ] })
4385
4405
  ] });
4386
4406
  }
4387
4407
  var POLL_MS = 2e3;
@@ -5027,10 +5047,11 @@ function StudioRoom({
5027
5047
  ]);
5028
5048
  const verifyGrantTookEffect = react.useCallback(
5029
5049
  (userId, retried = false) => {
5050
+ if (!userId) return;
5030
5051
  const id = window.setTimeout(() => {
5031
- const target = meeting.participants().find((row) => row.userId === userId);
5032
- if (!target || isOnStage(target.stageStatus)) return;
5033
- if (!retried && target.stageStatus === "ACCEPTED_TO_JOIN_STAGE") {
5052
+ const target = meeting.participants().find((row) => row.userId === userId || row.id === userId);
5053
+ if (target && isOnStage(target.stageStatus)) return;
5054
+ if (!retried) {
5034
5055
  void meeting.setStageAccess(userId, true).then(() => {
5035
5056
  verifyGrantTookEffect(userId, true);
5036
5057
  }).catch(() => {
@@ -5044,6 +5065,12 @@ function StudioRoom({
5044
5065
  },
5045
5066
  [labels.stageGrantNoEffect, meeting]
5046
5067
  );
5068
+ react.useEffect(
5069
+ () => meeting.subscribeStageGrant((userId) => {
5070
+ verifyGrantTookEffect(userId);
5071
+ }),
5072
+ [meeting, verifyGrantTookEffect]
5073
+ );
5047
5074
  const onSetStage = react.useCallback(
5048
5075
  async (tile, on) => {
5049
5076
  const userId = tile.userId;
@@ -5368,20 +5395,13 @@ function StudioRoom({
5368
5395
  peopleBadge,
5369
5396
  labels,
5370
5397
  onAdmitAs: (id, as) => {
5371
- const guest = waitlisted.find((row) => row.id === id);
5372
- void Promise.resolve(meeting.admitAs(id, as)).then(() => {
5373
- if (as === "panelist" && guest?.userId) {
5374
- verifyGrantTookEffect(guest.userId);
5375
- }
5398
+ void Promise.resolve(meeting.admitAs(id, as)).catch(() => {
5399
+ sonner.toast.error(labels.stageError);
5376
5400
  });
5377
5401
  },
5378
5402
  onAdmitAll: (as) => {
5379
- const waiting = waitlisted;
5380
- void Promise.resolve(meeting.admitAll(as)).then(() => {
5381
- if (as !== "panelist") return;
5382
- for (const guest of waiting) {
5383
- if (guest.userId) verifyGrantTookEffect(guest.userId);
5384
- }
5403
+ void Promise.resolve(meeting.admitAll(as)).catch(() => {
5404
+ sonner.toast.error(labels.stageError);
5385
5405
  });
5386
5406
  },
5387
5407
  onDeny: (id) => {
@@ -6084,6 +6104,7 @@ function activeSpeakerFrom(payload, fallback) {
6084
6104
  }
6085
6105
  var STAGE_JOIN_RETRY_MS = 1e3;
6086
6106
  var STAGE_JOIN_MAX_ATTEMPTS = 3;
6107
+ var STAGE_GRANT_JOIN_MAX_FAILURES = 8;
6087
6108
  function isStageDisabledError(error) {
6088
6109
  if (!error || typeof error !== "object") return false;
6089
6110
  const row = error;
@@ -6156,12 +6177,15 @@ function studioMeetingFromRtk(raw) {
6156
6177
  const chatListeners = /* @__PURE__ */ new Set();
6157
6178
  const participantListeners = /* @__PURE__ */ new Set();
6158
6179
  const stageListeners = /* @__PURE__ */ new Set();
6180
+ const stageGrantListeners = /* @__PURE__ */ new Set();
6159
6181
  const pinListeners = /* @__PURE__ */ new Set();
6160
6182
  const pendingPanelists = /* @__PURE__ */ new Set();
6161
6183
  const chatSend = /* @__PURE__ */ new Map();
6162
6184
  const nameOverrides = /* @__PURE__ */ new Map();
6163
6185
  let joinStageInFlight = null;
6164
6186
  let joinedStageLocally = false;
6187
+ let stageGrantPending = false;
6188
+ let stageGrantJoinFailures = 0;
6165
6189
  let lastMeetingError = null;
6166
6190
  const historyMessages = [];
6167
6191
  let speakerSinkId = null;
@@ -6260,6 +6284,21 @@ function studioMeetingFromRtk(raw) {
6260
6284
  function currentStageStatus() {
6261
6285
  return meeting.self?.stageStatus ?? meeting.stage?.status;
6262
6286
  }
6287
+ function isStageAccepted() {
6288
+ return meeting.self?.stageStatus === "ACCEPTED_TO_JOIN_STAGE" || meeting.stage?.status === "ACCEPTED_TO_JOIN_STAGE";
6289
+ }
6290
+ function clearStageGrantPending() {
6291
+ stageGrantPending = false;
6292
+ stageGrantJoinFailures = 0;
6293
+ }
6294
+ function notifyStageGrant(userId) {
6295
+ if (!userId) return;
6296
+ for (const listener of stageGrantListeners) listener(userId);
6297
+ }
6298
+ function markStageGranted() {
6299
+ stageGrantPending = true;
6300
+ void ensureStageJoined();
6301
+ }
6263
6302
  function resolveConnectionState() {
6264
6303
  const self = meeting.self;
6265
6304
  const roomState = self?.roomState ?? null;
@@ -6351,22 +6390,21 @@ function studioMeetingFromRtk(raw) {
6351
6390
  joinStageInFlight = null;
6352
6391
  }
6353
6392
  }
6354
- async function autoJoinStage() {
6393
+ async function ensureStageJoined() {
6355
6394
  if (currentStageStatus() === "ON_STAGE") {
6356
- notifyStage();
6395
+ clearStageGrantPending();
6357
6396
  return;
6358
6397
  }
6398
+ if (!stageGrantPending && !isStageAccepted()) return;
6399
+ stageGrantPending = true;
6400
+ if (stageGrantJoinFailures >= STAGE_GRANT_JOIN_MAX_FAILURES) return;
6359
6401
  try {
6360
6402
  await joinStageWithRetry();
6403
+ if (currentStageStatus() === "ON_STAGE") {
6404
+ clearStageGrantPending();
6405
+ }
6361
6406
  } catch {
6362
- notifyStage();
6363
- }
6364
- }
6365
- async function ensureStageJoined() {
6366
- if (currentStageStatus() !== "ACCEPTED_TO_JOIN_STAGE") return;
6367
- try {
6368
- await joinStageWithRetry();
6369
- } catch {
6407
+ stageGrantJoinFailures += 1;
6370
6408
  notifyStage();
6371
6409
  }
6372
6410
  }
@@ -6397,6 +6435,7 @@ function studioMeetingFromRtk(raw) {
6397
6435
  const grantId = takePendingGrantId(joined2);
6398
6436
  if (!grantId) return;
6399
6437
  await meeting.stage?.grantAccess?.([grantId]);
6438
+ notifyStageGrant(joined2?.userId || grantId);
6400
6439
  notifyStage();
6401
6440
  }
6402
6441
  async function grantPendingIfJoined(participantId, userId) {
@@ -6405,6 +6444,7 @@ function studioMeetingFromRtk(raw) {
6405
6444
  if (!grantId) return;
6406
6445
  pendingPanelists.delete(participantId);
6407
6446
  await meeting.stage?.grantAccess?.([grantId]);
6447
+ notifyStageGrant(already?.userId || grantId);
6408
6448
  notifyStage();
6409
6449
  }
6410
6450
  listen(meeting.self, "videoUpdate", rebindMedia);
@@ -6454,15 +6494,15 @@ function studioMeetingFromRtk(raw) {
6454
6494
  listen(meeting.stage, "stageAccessRequestUpdate", notifyStage);
6455
6495
  listen(meeting.stage, "newStageRequest", notifyStage);
6456
6496
  listen(meeting.stage, "stageRequestApproved", () => {
6457
- void autoJoinStage();
6497
+ markStageGranted();
6498
+ });
6499
+ listen(meeting.stage, "acceptPresentRequests", () => {
6500
+ markStageGranted();
6458
6501
  });
6459
6502
  listen(meeting.stage, "stageRequestRejected", notifyStage);
6460
- listen(meeting.stage, "stageStatusUpdate", (status) => {
6461
- if (status === "ACCEPTED_TO_JOIN_STAGE") {
6462
- void autoJoinStage();
6463
- return;
6464
- }
6465
- if (status === "ON_STAGE") {
6503
+ listen(meeting.stage, "stageStatusUpdate", () => {
6504
+ if (currentStageStatus() === "ON_STAGE") {
6505
+ clearStageGrantPending();
6466
6506
  if (joinedStageLocally) {
6467
6507
  joinedStageLocally = false;
6468
6508
  notifyStage();
@@ -6473,12 +6513,17 @@ function studioMeetingFromRtk(raw) {
6473
6513
  });
6474
6514
  return;
6475
6515
  }
6516
+ if (isStageAccepted()) {
6517
+ markStageGranted();
6518
+ return;
6519
+ }
6476
6520
  notifyStage();
6477
6521
  });
6478
6522
  return {
6479
6523
  async join() {
6480
6524
  await meeting.join?.();
6481
6525
  await joinStageIfAllowed();
6526
+ await ensureStageJoined();
6482
6527
  await applyCurrentVideoEffects();
6483
6528
  },
6484
6529
  async leave() {
@@ -6574,7 +6619,12 @@ function studioMeetingFromRtk(raw) {
6574
6619
  return;
6575
6620
  }
6576
6621
  if (on) await meeting.stage?.grantAccess?.([userId]);
6577
- else await meeting.stage?.kick?.([userId]);
6622
+ else {
6623
+ await meeting.stage?.kick?.([userId]);
6624
+ if (meeting.self?.userId === userId) {
6625
+ clearStageGrantPending();
6626
+ }
6627
+ }
6578
6628
  notifyStage();
6579
6629
  },
6580
6630
  async denyStageAccess(userId) {
@@ -6605,6 +6655,7 @@ function studioMeetingFromRtk(raw) {
6605
6655
  async leaveStage() {
6606
6656
  await meeting.stage?.leave?.();
6607
6657
  joinedStageLocally = false;
6658
+ clearStageGrantPending();
6608
6659
  notifyStage();
6609
6660
  },
6610
6661
  stageStatus() {
@@ -6636,6 +6687,12 @@ function studioMeetingFromRtk(raw) {
6636
6687
  stageListeners.delete(cb);
6637
6688
  };
6638
6689
  },
6690
+ subscribeStageGrant(cb) {
6691
+ stageGrantListeners.add(cb);
6692
+ return () => {
6693
+ stageGrantListeners.delete(cb);
6694
+ };
6695
+ },
6639
6696
  canManageStage() {
6640
6697
  return meeting.self?.permissions?.acceptStageRequests === true;
6641
6698
  },