@streamplace/components 0.7.27 → 0.7.30

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.
Files changed (45) hide show
  1. package/dist/components/chat/chat-box.js +2 -2
  2. package/dist/components/chat/chat.js +1 -1
  3. package/dist/components/chat/mod-view.js +21 -6
  4. package/dist/components/mobile-player/ui/autoplay-button.js +1 -0
  5. package/dist/components/mobile-player/ui/viewer-context-menu.js +1 -1
  6. package/dist/components/mobile-player/ui/viewer-loading-overlay.js +2 -2
  7. package/dist/components/mobile-player/use-webrtc.js +30 -0
  8. package/dist/components/ui/button.js +107 -155
  9. package/dist/components/ui/dialog.js +83 -116
  10. package/dist/components/ui/dropdown.js +41 -18
  11. package/dist/components/ui/input.js +53 -128
  12. package/dist/components/ui/primitives/button.js +0 -2
  13. package/dist/components/ui/primitives/modal.js +2 -2
  14. package/dist/components/ui/primitives/text.js +48 -8
  15. package/dist/components/ui/text.js +37 -66
  16. package/dist/components/ui/toast.js +78 -40
  17. package/dist/components/ui/view.js +28 -41
  18. package/dist/lib/theme/index.js +1 -2
  19. package/dist/lib/theme/theme.js +106 -54
  20. package/dist/lib/theme/tokens.js +94 -1
  21. package/dist/ui/index.js +2 -3
  22. package/node-compile-cache/v22.15.0-x64-efe9a9df-0/37be0eec +0 -0
  23. package/package.json +3 -2
  24. package/src/components/chat/chat-box.tsx +6 -3
  25. package/src/components/chat/chat.tsx +1 -0
  26. package/src/components/chat/mod-view.tsx +25 -5
  27. package/src/components/mobile-player/ui/autoplay-button.tsx +1 -0
  28. package/src/components/mobile-player/ui/viewer-context-menu.tsx +2 -2
  29. package/src/components/mobile-player/ui/viewer-loading-overlay.tsx +2 -2
  30. package/src/components/mobile-player/use-webrtc.tsx +31 -0
  31. package/src/components/ui/button.tsx +110 -172
  32. package/src/components/ui/dialog.tsx +96 -138
  33. package/src/components/ui/dropdown.tsx +60 -22
  34. package/src/components/ui/input.tsx +57 -144
  35. package/src/components/ui/primitives/button.tsx +0 -2
  36. package/src/components/ui/primitives/modal.tsx +0 -2
  37. package/src/components/ui/primitives/text.tsx +51 -8
  38. package/src/components/ui/text.tsx +42 -67
  39. package/src/components/ui/toast.tsx +108 -90
  40. package/src/components/ui/view.tsx +27 -41
  41. package/src/lib/theme/index.ts +0 -2
  42. package/src/lib/theme/theme.tsx +179 -72
  43. package/src/lib/theme/tokens.ts +97 -0
  44. package/src/ui/index.ts +0 -2
  45. package/tsconfig.tsbuildinfo +1 -1
@@ -17,6 +17,7 @@ export function AutoplayButton() {
17
17
  .play()
18
18
  .then(() => {
19
19
  setAutoplayFailed(false);
20
+ setMuted(false);
20
21
  setUserInteraction();
21
22
  })
22
23
  .catch((err) => {
@@ -1,5 +1,5 @@
1
1
  import { useRootContext } from "@rn-primitives/dropdown-menu";
2
- import { Settings } from "lucide-react-native";
2
+ import { Menu } from "lucide-react-native";
3
3
  import { Platform, View } from "react-native";
4
4
  import { colors } from "../../../lib/theme";
5
5
  import { useLivestreamStore } from "../../../livestream-store";
@@ -57,7 +57,7 @@ export function ContextMenu({
57
57
  return (
58
58
  <DropdownMenu>
59
59
  <DropdownMenuTrigger>
60
- <Settings color={colors.gray[200]} />
60
+ <Menu color={colors.gray[200]} />
61
61
  </DropdownMenuTrigger>
62
62
  <Portal container={dropdownPortalContainer}>
63
63
  <DropdownMenuContent side="top" align="end">
@@ -15,7 +15,7 @@ import {
15
15
 
16
16
  export function ViewerLoadingOverlay() {
17
17
  const status = usePlayerStore((x) => x.status);
18
- const theme = useTheme();
18
+ const { theme, zero: zt } = useTheme();
19
19
  const opacity = useSharedValue(0);
20
20
 
21
21
  useEffect(() => {
@@ -42,7 +42,7 @@ export function ViewerLoadingOverlay() {
42
42
 
43
43
  let spinner = <Loader size="large" />;
44
44
  if (status === PlayerStatus.PAUSE) {
45
- spinner = <Play size="$12" color={theme.styles.text.primary["color"]} />;
45
+ spinner = <Play size="$12" color={theme.colors.foreground} />;
46
46
  }
47
47
 
48
48
  return (
@@ -1,4 +1,5 @@
1
1
  import { useEffect, useRef, useState } from "react";
2
+ import * as sdpTransform from "sdp-transform";
2
3
  import { PlayerStatus, usePlayerStore, useStreamKey } from "../..";
3
4
  import { RTCPeerConnection, RTCSessionDescription } from "./webrtc-primitives";
4
5
 
@@ -107,6 +108,13 @@ export async function negotiateConnectionWithClientOffer(
107
108
  offerToReceiveAudio: true,
108
109
  offerToReceiveVideo: true,
109
110
  });
111
+ if (!offer.sdp) {
112
+ throw Error("no SDP in offer");
113
+ }
114
+
115
+ const newSDP = forceStereoAudio(offer.sdp);
116
+
117
+ offer.sdp = newSDP;
110
118
  /** https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/setLocalDescription */
111
119
  await peerConnection.setLocalDescription(offer);
112
120
 
@@ -297,3 +305,26 @@ export function useWebRTCIngest({
297
305
 
298
306
  return [mediaStream, setMediaStream];
299
307
  }
308
+
309
+ export function forceStereoAudio(sdp: string): string {
310
+ const parsedSDP = sdpTransform.parse(sdp);
311
+ const audioMedia = parsedSDP.media.find((m) => m.type === "audio");
312
+ if (!audioMedia) {
313
+ throw Error("no audio media in SDP");
314
+ }
315
+ const opusCodec = audioMedia.rtp.find((c) => c.codec === "opus");
316
+ if (!opusCodec) {
317
+ throw Error("no opus codec in SDP");
318
+ }
319
+ const opusFMTP = audioMedia.fmtp.find((c) => c.payload === opusCodec.payload);
320
+ if (!opusFMTP) {
321
+ throw Error("no opus fmtp in SDP");
322
+ }
323
+ const opusParams = sdpTransform.parseParams(opusFMTP.config);
324
+ opusParams.stereo = 1;
325
+ const newParams = Object.entries(opusParams)
326
+ .map(([k, v]) => `${k}=${v}`)
327
+ .join(";");
328
+ opusFMTP.config = newParams;
329
+ return sdpTransform.write(parsedSDP);
330
+ }
@@ -1,8 +1,8 @@
1
1
  import { cva, type VariantProps } from "class-variance-authority";
2
2
  import React, { forwardRef, useMemo } from "react";
3
- import { ActivityIndicator, StyleSheet } from "react-native";
3
+ import { ActivityIndicator } from "react-native";
4
4
  import { useTheme } from "../../lib/theme/theme";
5
- import * as tokens from "../../lib/theme/tokens";
5
+ import * as zero from "../../ui";
6
6
  import { ButtonPrimitive, ButtonPrimitiveProps } from "./primitives/button";
7
7
  import { TextPrimitive } from "./primitives/text";
8
8
 
@@ -57,44 +57,117 @@ export const Button = forwardRef<any, ButtonProps>(
57
57
  },
58
58
  ref,
59
59
  ) => {
60
- const { theme } = useTheme();
60
+ const { zero: zt, icons } = useTheme();
61
61
 
62
- // Create dynamic styles based on theme
63
- const styles = useMemo(() => createStyles(theme), [theme]);
64
-
65
- // Get variant styles
62
+ // Get variant styles using theme.zero
66
63
  const buttonStyle = useMemo(() => {
67
- const variantStyle = styles[`${variant}Button` as keyof typeof styles];
68
- const sizeStyle = styles[`${size}Button` as keyof typeof styles];
69
- return [variantStyle, sizeStyle];
70
- }, [variant, size, styles]);
64
+ switch (variant) {
65
+ case "primary":
66
+ return zt.button.primary;
67
+ case "secondary":
68
+ return zt.button.secondary;
69
+ case "outline":
70
+ return zt.button.outline;
71
+ case "ghost":
72
+ return zt.button.ghost;
73
+ case "destructive":
74
+ return [zt.bg.destructive, zero.shadows.sm];
75
+ case "success":
76
+ return [zt.bg.success, zero.shadows.sm];
77
+ default:
78
+ return zt.button.primary;
79
+ }
80
+ }, [variant, zt]);
71
81
 
72
- // Get inner styles for button content
73
- const buttonInnerStyle = useMemo(() => {
74
- const sizeInnerStyle =
75
- styles[`${size}ButtonInner` as keyof typeof styles];
76
- return sizeInnerStyle;
77
- }, [size, styles]);
82
+ // Get text styles using theme.zero
83
+ const textStyle = useMemo(() => {
84
+ switch (variant) {
85
+ case "primary":
86
+ return [zt.text.primaryForeground, { fontWeight: "600" }];
87
+ case "secondary":
88
+ return [zt.text.secondaryForeground, { fontWeight: "500" }];
89
+ case "outline":
90
+ case "ghost":
91
+ return [zt.text.foreground, { fontWeight: "500" }];
92
+ case "destructive":
93
+ return [zt.text.destructiveForeground, { fontWeight: "600" }];
94
+ case "success":
95
+ return [zt.text.successForeground, { fontWeight: "600" }];
96
+ default:
97
+ return [zt.text.primaryForeground, { fontWeight: "600" }];
98
+ }
99
+ }, [variant, zt]);
78
100
 
79
- const textStyle = React.useMemo(() => {
80
- const variantTextStyle = styles[`${variant}Text` as keyof typeof styles];
81
- const sizeTextStyle = styles[`${size}Text` as keyof typeof styles];
82
- return [variantTextStyle, sizeTextStyle];
83
- }, [variant, size, styles]);
101
+ // Size styles using theme.zero
102
+ const sizeStyles = useMemo(() => {
103
+ switch (size) {
104
+ case "sm":
105
+ return {
106
+ button: [
107
+ zero.px[3],
108
+ zero.py[2],
109
+ { borderRadius: zero.borderRadius.md },
110
+ ],
111
+ inner: { gap: 4 },
112
+ text: zt.text.sm,
113
+ };
114
+ case "lg":
115
+ return {
116
+ button: [
117
+ zero.px[6],
118
+ zero.py[3],
119
+ { borderRadius: zero.borderRadius.md },
120
+ ],
121
+ inner: { gap: 8 },
122
+ text: zt.text.lg,
123
+ };
124
+ case "xl":
125
+ return {
126
+ button: [
127
+ zero.px[8],
128
+ zero.py[4],
129
+ { borderRadius: zero.borderRadius.lg },
130
+ ],
131
+ inner: { gap: 12 },
132
+ text: zt.text.xl,
133
+ };
134
+ case "pill":
135
+ return {
136
+ button: [
137
+ zero.px[4],
138
+ zero.py[2],
139
+ { borderRadius: zero.borderRadius.full },
140
+ ],
141
+ inner: { gap: 4 },
142
+ text: zt.text.sm,
143
+ };
144
+ case "md":
145
+ default:
146
+ return {
147
+ button: [
148
+ zero.px[4],
149
+ zero.py[2],
150
+ { borderRadius: zero.borderRadius.md },
151
+ ],
152
+ inner: { gap: 6 },
153
+ text: zt.text.md,
154
+ };
155
+ }
156
+ }, [size, zt]);
84
157
 
85
158
  const iconSize = React.useMemo(() => {
86
159
  switch (size) {
87
160
  case "sm":
88
- return 16;
161
+ return icons.size.sm;
89
162
  case "lg":
90
- return 20;
163
+ return icons.size.lg;
91
164
  case "xl":
92
- return 24;
165
+ return icons.size.xl;
93
166
  case "md":
94
167
  default:
95
- return 18;
168
+ return icons.size.md;
96
169
  }
97
- }, [size]);
170
+ }, [size, icons]);
98
171
 
99
172
  const spinnerSize = useMemo(() => {
100
173
  switch (size) {
@@ -113,24 +186,26 @@ export const Button = forwardRef<any, ButtonProps>(
113
186
  switch (variant) {
114
187
  case "outline":
115
188
  case "ghost":
116
- return theme.colors.primary;
189
+ return icons.color.primary;
117
190
  case "secondary":
118
- return theme.colors.secondaryForeground;
191
+ return icons.color.secondary;
119
192
  case "destructive":
120
- return theme.colors.destructiveForeground;
193
+ return icons.color.destructive;
194
+ case "success":
195
+ return icons.color.success;
121
196
  default:
122
- return theme.colors.primaryForeground;
197
+ return icons.color.default;
123
198
  }
124
- }, [variant, theme.colors]);
199
+ }, [variant, icons]);
125
200
 
126
201
  return (
127
202
  <ButtonPrimitive.Root
128
203
  ref={ref}
129
204
  disabled={disabled || loading}
130
- style={[buttonStyle, style]}
205
+ style={[buttonStyle, sizeStyles.button, style]}
131
206
  {...props}
132
207
  >
133
- <ButtonPrimitive.Content style={buttonInnerStyle}>
208
+ <ButtonPrimitive.Content style={sizeStyles.inner}>
134
209
  {loading && !leftIcon ? (
135
210
  <ButtonPrimitive.Icon position="left">
136
211
  <ActivityIndicator size={spinnerSize} color={spinnerColor} />
@@ -144,7 +219,7 @@ export const Button = forwardRef<any, ButtonProps>(
144
219
  </ButtonPrimitive.Icon>
145
220
  ) : null}
146
221
 
147
- <TextPrimitive.Root style={textStyle}>
222
+ <TextPrimitive.Root style={[textStyle, sizeStyles.text]}>
148
223
  {loading && loadingText ? loadingText : children}
149
224
  </TextPrimitive.Root>
150
225
 
@@ -168,142 +243,5 @@ export const Button = forwardRef<any, ButtonProps>(
168
243
 
169
244
  Button.displayName = "Button";
170
245
 
171
- // Create theme-based styles
172
- function createStyles(theme: any) {
173
- return StyleSheet.create({
174
- // Variant styles
175
- primaryButton: {
176
- backgroundColor: theme.colors.primary,
177
- borderWidth: 0,
178
- ...theme.shadows.sm,
179
- },
180
- primaryText: {
181
- color: theme.colors.primaryForeground,
182
- fontWeight: "600",
183
- },
184
-
185
- secondaryButton: {
186
- backgroundColor: theme.colors.secondary,
187
- borderWidth: 0,
188
- },
189
- secondaryText: {
190
- color: theme.colors.secondaryForeground,
191
- fontWeight: "500",
192
- },
193
-
194
- outlineButton: {
195
- backgroundColor: "transparent",
196
- borderWidth: 1,
197
- borderColor: theme.colors.border,
198
- },
199
- outlineText: {
200
- color: theme.colors.foreground,
201
- fontWeight: "500",
202
- },
203
-
204
- ghostButton: {
205
- backgroundColor: "transparent",
206
- borderWidth: 0,
207
- },
208
- ghostText: {
209
- color: theme.colors.foreground,
210
- fontWeight: "500",
211
- },
212
-
213
- destructiveButton: {
214
- backgroundColor: theme.colors.destructive,
215
- borderWidth: 0,
216
- ...theme.shadows.sm,
217
- },
218
- destructiveText: {
219
- color: theme.colors.destructiveForeground,
220
- fontWeight: "600",
221
- },
222
-
223
- successButton: {
224
- backgroundColor: theme.colors.success,
225
- borderWidth: 0,
226
- ...theme.shadows.sm,
227
- },
228
- successText: {
229
- color: theme.colors.successForeground,
230
- fontWeight: "600",
231
- },
232
-
233
- pillButton: {
234
- paddingHorizontal: theme.spacing[2],
235
- paddingVertical: theme.spacing[1],
236
- borderRadius: tokens.borderRadius.full,
237
- minHeight: tokens.touchTargets.minimum / 2,
238
- },
239
-
240
- pillText: {
241
- color: theme.colors.primaryForeground,
242
- fontWeight: "400",
243
- },
244
-
245
- // Size styles
246
- smButton: {
247
- paddingHorizontal: theme.spacing[3],
248
- paddingVertical: theme.spacing[2],
249
- borderRadius: tokens.borderRadius.md,
250
- minHeight: tokens.touchTargets.minimum,
251
- gap: theme.spacing[1],
252
- },
253
- smButtonInner: {
254
- gap: theme.spacing[1],
255
- },
256
- smText: {
257
- fontSize: 14,
258
- lineHeight: 16,
259
- },
260
-
261
- mdButton: {
262
- paddingHorizontal: theme.spacing[4],
263
- paddingVertical: theme.spacing[3],
264
- borderRadius: tokens.borderRadius.md,
265
- minHeight: tokens.touchTargets.minimum,
266
- gap: theme.spacing[2],
267
- },
268
- mdButtonInner: {
269
- gap: theme.spacing[2],
270
- },
271
- mdText: {
272
- fontSize: 16,
273
- lineHeight: 18,
274
- },
275
-
276
- lgButton: {
277
- paddingHorizontal: theme.spacing[6],
278
- paddingVertical: theme.spacing[4],
279
- borderRadius: tokens.borderRadius.md,
280
- minHeight: tokens.touchTargets.comfortable,
281
- gap: theme.spacing[3],
282
- },
283
- lgButtonInner: {
284
- gap: theme.spacing[3],
285
- },
286
- lgText: {
287
- fontSize: 18,
288
- lineHeight: 20,
289
- },
290
-
291
- xlButton: {
292
- paddingHorizontal: theme.spacing[8],
293
- paddingVertical: theme.spacing[5],
294
- borderRadius: tokens.borderRadius.lg,
295
- minHeight: tokens.touchTargets.large,
296
- gap: theme.spacing[4],
297
- },
298
- xlButtonInner: {
299
- gap: theme.spacing[4],
300
- },
301
- xlText: {
302
- fontSize: 20,
303
- lineHeight: 24,
304
- },
305
- });
306
- }
307
-
308
246
  // Export button variants for external use
309
247
  export { buttonVariants };