tuikit-atomicx-vue3 4.5.2 → 4.5.4

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 (66) hide show
  1. package/dist/baseComp/Input/InputH5.vue.d.ts +1 -1
  2. package/dist/baseComp/Input/InputPC.vue.d.ts +1 -1
  3. package/dist/baseComp/Input/InputWX.vue.d.ts +1 -1
  4. package/dist/baseComp/Input/index.d.ts +15 -15
  5. package/dist/components/BarrageInput/TextEditor/TextEditor.vue.d.ts +1 -1
  6. package/dist/components/BarrageInput/index.d.ts +6 -6
  7. package/dist/components/ChatSetting/SettingItem/SettingItem.vue.d.ts +2 -2
  8. package/dist/components/ContactList/index.d.ts +6 -6
  9. package/dist/components/ConversationList/ConversationList.vue.d.ts +190 -190
  10. package/dist/components/ConversationList/ConversationPreview/ConversationPreview.vue.d.ts +82 -82
  11. package/dist/components/ConversationList/ConversationPreview/ConversationPreviewUI.vue.d.ts +16 -16
  12. package/dist/components/ConversationList/ConversationSearch/ConversationSearch.vue.d.ts +36 -36
  13. package/dist/components/ConversationList/index.d.ts +416 -416
  14. package/dist/components/LiveView/PlayerControl/PlayerControl.js +145 -128
  15. package/dist/components/LiveView/PlayerControl/PlayerControlState.d.ts +6 -4
  16. package/dist/components/LiveView/PlayerControl/PlayerControlState.js +148 -122
  17. package/dist/components/LiveView/PlayerControl/utils/domHelpers.d.ts +2 -1
  18. package/dist/components/LiveView/PlayerControl/utils/domHelpers.js +7 -6
  19. package/dist/components/LiveView/i18n/en-US/index.d.ts +3 -0
  20. package/dist/components/LiveView/i18n/en-US/index.js +4 -1
  21. package/dist/components/LiveView/i18n/zh-CN/index.d.ts +3 -0
  22. package/dist/components/LiveView/i18n/zh-CN/index.js +4 -1
  23. package/dist/components/LiveView/index.js +78 -78
  24. package/dist/components/MessageInput/MessageInput.vue.d.ts +1 -1
  25. package/dist/components/MessageInput/TextEditor/TextEditor.vue.d.ts +1 -1
  26. package/dist/components/MessageInput/index.d.ts +30 -30
  27. package/dist/components/RoomParticipantList/useParticpantAction/useNameCardAction/index.d.ts +10 -2
  28. package/dist/components/Search/Search.vue.d.ts +12 -12
  29. package/dist/components/Search/index.d.ts +18 -18
  30. package/dist/components/StreamView/Layout/CustomLayout.vue.d.ts +2 -1
  31. package/dist/components/StreamView/Layout/FloatLayout.vue.d.ts +2 -1
  32. package/dist/components/StreamView/Layout/GridLayout.vue.d.ts +2 -1
  33. package/dist/components/StreamView/Layout/MeetingLayout.vue.d.ts +1 -1
  34. package/dist/components/StreamView/common/StreamList/index.vue.d.ts +1 -1
  35. package/dist/components/StreamView/common/StreamRegion/StreamRegionPC.vue.d.ts +2 -2
  36. package/dist/components/StreamView/index.d.ts +1 -1
  37. package/dist/components/StreamView/index.vue.d.ts +1 -1
  38. package/dist/hooks/useRoomEngine.d.ts +2 -2
  39. package/dist/hooks/useUserActions/index.d.ts +18 -2
  40. package/dist/hooks/useUserActions/useChangeNameCardAction/index.d.ts +9 -1
  41. package/dist/hooks/useUserActions/useKickUserAction.d.ts +9 -1
  42. package/dist/hooks/useUserActions/useTransferOwnerAction.d.ts +9 -1
  43. package/dist/hooks/useUserActions/useVideoAction.d.ts +9 -1
  44. package/dist/states/BarrageState/BarrageState.d.ts +2 -2
  45. package/dist/states/BattleState/BattleState.d.ts +5 -5
  46. package/dist/states/CoGuestState.d.ts +1 -1
  47. package/dist/states/LiveAudienceState.d.ts +4 -4
  48. package/dist/states/LiveListState/LiveListState.d.ts +2 -2
  49. package/dist/states/LiveSeatState/index.d.ts +6 -6
  50. package/dist/states/LiveSeatState/seatManager.d.ts +6 -6
  51. package/dist/states/RoomState/roomManager.d.ts +1 -0
  52. package/dist/states/RoomState/roomManager.js +38 -29
  53. package/dist/states/RoomState/scheduleManager.js +19 -25
  54. package/dist/states/SeatStore.d.ts +148 -16
  55. package/dist/states/UserState/userManager.d.ts +1 -1
  56. package/dist/styles/index.css +1 -1
  57. package/dist/subEntry/chat/chat.d.ts +457 -457
  58. package/dist/subEntry/live/server.d.ts +1 -1
  59. package/dist/subEntry/room/server.d.ts +1 -1
  60. package/package.json +3 -2
  61. package/src/components/LiveView/PlayerControl/PlayerControl.vue +39 -15
  62. package/src/components/LiveView/PlayerControl/PlayerControlState.ts +61 -18
  63. package/src/components/LiveView/PlayerControl/utils/domHelpers.ts +4 -4
  64. package/src/components/LiveView/i18n/en-US/index.ts +3 -0
  65. package/src/components/LiveView/i18n/zh-CN/index.ts +3 -0
  66. package/src/components/LiveView/index.vue +7 -3
@@ -14,6 +14,6 @@ export default class RTCLoginServer {
14
14
  * @param { TUIInitParam } params
15
15
  */
16
16
  onNotifyEvent(eventName: string, subKey: string): void;
17
- login(): Promise<any>;
17
+ login(): Promise<unknown>;
18
18
  logout(): void;
19
19
  }
@@ -14,6 +14,6 @@ export default class RTCLoginServer {
14
14
  * @param { TUIInitParam } params
15
15
  */
16
16
  onNotifyEvent(eventName: string, subKey: string): void;
17
- login(): Promise<any>;
17
+ login(): Promise<unknown>;
18
18
  logout(): void;
19
19
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tuikit-atomicx-vue3",
3
- "version": "4.5.2",
3
+ "version": "4.5.4",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -35,6 +35,7 @@
35
35
  "scripts": {
36
36
  "dev": "vite build --watch",
37
37
  "build": "vite build",
38
+ "build:local": "vite build",
38
39
  "build:watch": "vite build --mode development -w",
39
40
  "lint": "eslint .",
40
41
  "check-chinese": "node scripts/check-chinese-simple.js",
@@ -55,7 +56,7 @@
55
56
  "@tencentcloud/chat": "^3.5.8",
56
57
  "@tencentcloud/chat-uikit-engine": "~2.5.7",
57
58
  "@tencentcloud/tui-core": "latest",
58
- "@tencentcloud/tuiroom-engine-js": "~3.5.0",
59
+ "@tencentcloud/tuiroom-engine-js": "~3.5.1",
59
60
  "@tencentcloud/uikit-base-component-vue3": "1.3.2",
60
61
  "vue": "^3.4.0"
61
62
  },
@@ -10,14 +10,20 @@
10
10
  ]"
11
11
  >
12
12
  <div class="control-buttons">
13
- <span class="control-btn play-pause-btn" :title="isPlaying ? t('Pause') : t('Play')" @click="handlePlayPause">
14
- <IconPause size="20" v-if="isPlaying" />
15
- <IconPlay size="20" v-else />
13
+ <span
14
+ v-if="!isSafari || !isTcPlayer"
15
+ class="control-btn play-pause-btn"
16
+ :class="{disabled: isPictureInPicture}"
17
+ :title="isPlaying ? t('Pause') : t('Play')"
18
+ @click="handlePlayPause"
19
+ >
20
+ <IconPause v-if="isPlaying" size="20" />
21
+ <IconPlay v-else size="20" />
16
22
  </span>
17
- <div class="center-controls"></div>
23
+ <div class="center-controls" />
18
24
  <div class="right-controls">
19
25
  <MultiResolution />
20
- <span class="control-btn audio-control-btn">
26
+ <span v-if="!isSafari || !isTcPlayer" class="control-btn audio-control-btn">
21
27
  <AudioControl
22
28
  class="audio-control-icon"
23
29
  :icon-size="20"
@@ -29,6 +35,7 @@
29
35
  </span>
30
36
  <span
31
37
  class="control-btn"
38
+ :class="{disabled: !isPlaying && !isPictureInPicture}"
32
39
  :title="isPictureInPicture ? t('Exit Picture in Picture') : t('Picture in Picture')"
33
40
  @click="handlePictureInPicture"
34
41
  >
@@ -58,11 +65,10 @@ import {
58
65
  TUIToast,
59
66
  TOAST_TYPE,
60
67
  } from '@tencentcloud/uikit-base-component-vue3';
61
- import { usePlayerControlState } from './PlayerControlState';
68
+ import { isMobile } from '../../../utils';
62
69
  import AudioControl from './AudioControl.vue';
63
70
  import MultiResolution from './MultiResolution.vue';
64
- import { isMobile } from '../../../utils';
65
- import { waitForVideoMounted } from './utils/domHelpers';
71
+ import { usePlayerControlState } from './PlayerControlState';
66
72
 
67
73
  const {
68
74
  isMuted,
@@ -79,6 +85,8 @@ const {
79
85
  setVolume,
80
86
  setMute,
81
87
  cleanup,
88
+ isSafari,
89
+ isTcPlayer,
82
90
  } = usePlayerControlState();
83
91
 
84
92
  const props = defineProps<{
@@ -93,6 +101,14 @@ const hideTimeout = ref<number | null>(null);
93
101
  const AUTO_HIDE_DELAY = 3000; // ms
94
102
 
95
103
  const handlePlayPause = () => {
104
+ if (isPictureInPicture.value) {
105
+ TUIToast({
106
+ type: TOAST_TYPE.WARNING,
107
+ message: t('Not allow to "Pause" in picture-in-picture mode'),
108
+ });
109
+ return;
110
+ }
111
+
96
112
  if (isPlaying.value) {
97
113
  pause();
98
114
  } else {
@@ -101,6 +117,14 @@ const handlePlayPause = () => {
101
117
  };
102
118
 
103
119
  const handlePictureInPicture = async () => {
120
+ if (!isPlaying.value && !isPictureInPicture.value) {
121
+ TUIToast({
122
+ type: TOAST_TYPE.WARNING,
123
+ message: t('Not allow to "Picture in Picture" in non-playing mode'),
124
+ });
125
+ return;
126
+ }
127
+
104
128
  let flag = false;
105
129
  if (isPictureInPicture.value) {
106
130
  flag = await exitPictureInPicture();
@@ -184,13 +208,9 @@ const removeParentMouseListener = () => {
184
208
  const touchStartCoords = ref<{ x: number; y: number } | null>(null);
185
209
 
186
210
  // Touch distance calculation
187
- const calculateTouchDistance = (start: { x: number; y: number }, end: Touch) => {
188
- return Math.sqrt(Math.pow(end.clientX - start.x, 2) + Math.pow(end.clientY - start.y, 2));
189
- };
211
+ const calculateTouchDistance = (start: { x: number; y: number }, end: Touch) => Math.sqrt((end.clientX - start.x) ** 2 + (end.clientY - start.y) ** 2);
190
212
 
191
- const isPlayerControlTarget = (target: Node) => {
192
- return playerControlRef.value?.contains(target) || false;
193
- };
213
+ const isPlayerControlTarget = (target: Node) => playerControlRef.value?.contains(target) || false;
194
214
 
195
215
  const isLiveCoreViewTarget = (target: Node) => {
196
216
  const container = document.getElementById('live-core-view-container');
@@ -224,7 +244,6 @@ const handleScreenTouchStart = (event: TouchEvent) => {
224
244
  const handleScreenTouchMove = (event: TouchEvent) => {
225
245
  if (playerControlRef.value && playerControlRef.value.contains(event.target as Node)) {
226
246
  stopAutoHideControl();
227
- return;
228
247
  }
229
248
  };
230
249
 
@@ -395,6 +414,11 @@ onBeforeUnmount(() => {
395
414
  height: 20px;
396
415
  fill: currentColor;
397
416
  }
417
+
418
+ &.disabled {
419
+ cursor: not-allowed;
420
+ opacity: 0.4;
421
+ }
398
422
  }
399
423
 
400
424
  .play-pause-btn {
@@ -1,17 +1,20 @@
1
1
  import type { Ref } from 'vue';
2
2
  import { computed, ref, watch } from 'vue';
3
3
  import { TUIVideoQuality } from '@tencentcloud/tuiroom-engine-js';
4
+ import { TUIMessageBox, useUIKit } from '@tencentcloud/uikit-base-component-vue3';
4
5
  import useRoomEngine from '../../../hooks/useRoomEngine';
5
- import { useLiveSeatState } from '../../../states/LiveSeatState';
6
6
  import { useLiveListState } from '../../../states/LiveListState';
7
+ import { useLiveSeatState } from '../../../states/LiveSeatState';
7
8
  import {
8
9
  getDeviceType,
9
10
  shouldRotateToLandscapeForFullscreen,
10
11
  hadLandscapeRotationToUndo,
12
+ isSafariBrowser,
11
13
  } from './utils/deviceDetection';
12
14
  import {
13
15
  DOMElementGetter,
14
16
  EventListenerManager,
17
+ waitForVideoMounted,
15
18
  } from './utils/domHelpers';
16
19
  import {
17
20
  FullscreenManager,
@@ -20,7 +23,7 @@ import {
20
23
  StyleManager,
21
24
  } from './utils/fullscreenManager';
22
25
  import type {
23
- FullscreenResult
26
+ FullscreenResult,
24
27
  } from './utils/fullscreenManager';
25
28
 
26
29
  // Player fill mode enum
@@ -48,6 +51,8 @@ export interface PlayerControlState {
48
51
  isPictureInPicture: Ref<boolean>;
49
52
  currentVolume: Ref<number>;
50
53
  isMuted: Ref<boolean>;
54
+ isSafari: Ref<boolean>;
55
+ isTcPlayer: Ref<boolean>;
51
56
 
52
57
  // Resolution state properties
53
58
  resolutionList: Ref<Resolution[]>;
@@ -88,25 +93,21 @@ const VOLUME_CONSTANTS = {
88
93
  MUTE_VOLUME: 0,
89
94
  };
90
95
 
91
- const TIMING_CONSTANTS = {
92
- // Delay in milliseconds before resuming playback after leaving picture-in-picture
93
- // Allows browser to handle state transitions naturally
94
- PIP_RESUME_DELAY: 300,
95
- };
96
-
97
96
  const ARRAY_CONSTANTS = {
98
97
  // Index of the first element in an array
99
98
  FIRST_INDEX: 0,
100
99
  };
101
100
 
102
101
  // State management
103
- const isPlaying = ref(true);
102
+ const isPlaying = ref(false);
104
103
  const currentFillMode = ref<FillMode>(FillMode.CONTAIN);
105
104
  const isFullscreen = ref(false);
106
105
  const isLandscapeStyleMode = ref(false);
107
106
  const isPictureInPicture = ref(false);
108
107
  const currentVolume = ref(VOLUME_CONSTANTS.DEFAULT_VOLUME);
109
108
  const isMuted = ref(false); // Mute state - synced across all rooms
109
+ const isSafari = ref(isSafariBrowser());
110
+ const isTcPlayer = ref(false);
110
111
 
111
112
  // Internal storage for volume restoration (not reactive)
112
113
  let restoreVolume = VOLUME_CONSTANTS.DEFAULT_VOLUME;
@@ -116,6 +117,7 @@ const resolutionList = ref<Resolution[]>([]);
116
117
  const currentResolution = ref<Resolution | undefined>();
117
118
 
118
119
  const roomEngine = useRoomEngine();
120
+ const { t } = useUIKit();
119
121
 
120
122
  /**
121
123
  * Player control state management hook
@@ -159,12 +161,31 @@ export function usePlayerControlState(): PlayerControlState {
159
161
  };
160
162
 
161
163
  const resume = async (): Promise<boolean> => withErrorHandling(async () => {
162
- await roomEngine.instance?.callExperimentalAPI(JSON.stringify({
163
- api: 'resume',
164
- params: {},
165
- }));
166
- isPlaying.value = true;
167
- return true;
164
+ try {
165
+ await roomEngine.instance?.callExperimentalAPI(JSON.stringify({
166
+ api: 'resume',
167
+ params: {},
168
+ }));
169
+ isPlaying.value = true;
170
+ return true;
171
+ } catch (error: any) {
172
+ // Handle browser autoplay policy restriction
173
+ if (error?.name === 'NotAllowedError' && (error?.message?.includes('user agent') || error?.message?.includes('denied permission'))) {
174
+ TUIMessageBox.alert({
175
+ content: t('Content is ready. Click the button to start playback'),
176
+ confirmText: t('Play'),
177
+ callback: async () => {
178
+ await roomEngine.instance?.callExperimentalAPI(JSON.stringify({
179
+ api: 'resume',
180
+ params: {},
181
+ }));
182
+ isPlaying.value = true;
183
+ },
184
+ });
185
+ return false;
186
+ }
187
+ throw error;
188
+ }
168
189
  }, 'Resume playback', false);
169
190
 
170
191
  const pause = async (): Promise<boolean> => withErrorHandling(async () => {
@@ -443,7 +464,6 @@ export function usePlayerControlState(): PlayerControlState {
443
464
 
444
465
  const handleLeavePictureInPicture = () => {
445
466
  isPictureInPicture.value = false;
446
- setTimeout(resume, TIMING_CONSTANTS.PIP_RESUME_DELAY);
447
467
  };
448
468
 
449
469
  eventManager.addListener('enterpictureinpicture', video, 'enterpictureinpicture', handleEnterPictureInPicture);
@@ -462,19 +482,34 @@ export function usePlayerControlState(): PlayerControlState {
462
482
  setupVideoEventListeners();
463
483
  OrientationManager.addOrientationListener(orientationListenerId, handleOrientationChange);
464
484
 
485
+ const updateIsTcPlayer = async () => {
486
+ try {
487
+ await waitForVideoMounted();
488
+ } finally {
489
+ const hasTcPlayer = DOMElementGetter.hasTcPlayerElement();
490
+ if (hasTcPlayer !== isTcPlayer.value) {
491
+ isTcPlayer.value = hasTcPlayer;
492
+ console.log('[PlayerControl] isTcPlayer:', isTcPlayer.value);
493
+ }
494
+ }
495
+ };
496
+
465
497
  watch(
466
498
  () => currentLive.value?.liveId,
467
499
  async (newLiveId) => {
468
500
  if (newLiveId) {
469
501
  isPlaying.value = true;
502
+ isPictureInPicture.value = false;
503
+ isFullscreen.value = false;
470
504
  resolutionList.value = [];
471
505
  currentResolution.value = undefined;
472
506
  // When pulling a TRTC stream, this interface has a cache, but when using TCPlayer, there is no cache
473
507
  await setVolume(currentVolume.value);
474
508
  await initializeResolution(newLiveId, false);
509
+ updateIsTcPlayer();
475
510
 
476
511
  // Print player control state after entering room
477
- console.log('[PlayerControl] State after entering room:', {
512
+ console.log('[PlayerControl] State after entering room:', JSON.stringify({
478
513
  isPlaying: isPlaying.value,
479
514
  currentFillMode: currentFillMode.value,
480
515
  isFullscreen: isFullscreen.value,
@@ -483,11 +518,17 @@ export function usePlayerControlState(): PlayerControlState {
483
518
  currentVolume: currentVolume.value,
484
519
  resolutionList: resolutionList.value,
485
520
  currentResolution: currentResolution.value,
486
- });
521
+ }));
487
522
  }
488
523
  },
489
524
  );
490
525
 
526
+ watch(() => isPlaying.value, (newIsPlaying) => {
527
+ if (newIsPlaying) {
528
+ updateIsTcPlayer();
529
+ }
530
+ }, { immediate: true });
531
+
491
532
  // Return interface implementation
492
533
  return {
493
534
  isPlaying,
@@ -499,6 +540,8 @@ export function usePlayerControlState(): PlayerControlState {
499
540
  isMuted,
500
541
  resolutionList,
501
542
  currentResolution,
543
+ isSafari,
544
+ isTcPlayer,
502
545
  resume,
503
546
  pause,
504
547
  requestFullscreen,
@@ -73,7 +73,8 @@ export class DOMElementGetter {
73
73
  }
74
74
 
75
75
  /**
76
- * Validate if elements exist
76
+ * Validate if required elements exist for fullscreen
77
+ * Note: video element is optional because canvas rendering (TRTC SDK) doesn't use video
77
78
  */
78
79
  static validateElements(elements: { container?: HTMLElement | null; view?: HTMLElement | null; video?: HTMLVideoElement | null }): {
79
80
  isValid: boolean;
@@ -87,9 +88,8 @@ export class DOMElementGetter {
87
88
  if (elements.view === null) {
88
89
  missingElements.push('live-core-view');
89
90
  }
90
- if (elements.video === null) {
91
- missingElements.push('video');
92
- }
91
+ // Video element is not required for fullscreen (canvas rendering works without it)
92
+ // Only required for picture-in-picture feature
93
93
 
94
94
  return {
95
95
  isValid: missingElements.length === 0,
@@ -10,6 +10,7 @@ export const resource = {
10
10
  'Open Speaker': 'Open Speaker',
11
11
  'Close Speaker': 'Close Speaker',
12
12
  'The system does not support picture-in-picture mode': 'The system does not support picture-in-picture mode',
13
+ 'Content is ready. Click the button to start playback': 'Content is ready. Click the button to start playback',
13
14
  'co-Hosting': 'co-Hosting',
14
15
  'In battle': 'In battle',
15
16
  '360P': '360P',
@@ -17,4 +18,6 @@ export const resource = {
17
18
  '720P': '720P',
18
19
  '1080P': '1080P',
19
20
  'Connecting': 'Connecting',
21
+ 'Not allow to "Pause" in picture-in-picture mode': 'Not allow to "Pause" in picture-in-picture mode',
22
+ 'Not allow to "Picture in Picture" in non-playing mode': 'Not allow to "Picture in Picture" in non-playing mode',
20
23
  };
@@ -10,6 +10,7 @@ export const resource = {
10
10
  'Open Speaker': '取消静音',
11
11
  'Close Speaker': '静音',
12
12
  'The system does not support picture-in-picture mode': '系统不支持画中画模式',
13
+ 'Content is ready. Click the button to start playback': '画面已准备好,点击播放按钮开始播放',
13
14
  'co-Hosting': '连线中',
14
15
  'In battle': 'PK中',
15
16
  '360P': '低清',
@@ -17,4 +18,6 @@ export const resource = {
17
18
  '720P': '高清',
18
19
  '1080P': '超清',
19
20
  'Connecting': '连线中',
21
+ 'Not allow to "Pause" in picture-in-picture mode': '画中画模式下不允许暂停操作',
22
+ 'Not allow to "Picture in Picture" in non-playing mode': '暂停状态下不允许进入画中画模式',
20
23
  };
@@ -67,7 +67,7 @@ import type { SeatInfo, SeatUserInfo } from '../../types';
67
67
  import { isMobile } from '../../utils';
68
68
  import { usePlayerControlState } from './PlayerControl';
69
69
 
70
- const { isFullscreen, isLandscapeStyleMode } = usePlayerControlState();
70
+ const { isFullscreen, isLandscapeStyleMode, isPictureInPicture, exitPictureInPicture } = usePlayerControlState();
71
71
  const { t } = useUIKit();
72
72
  const { seatList, canvas, startPlayStream, stopPlayStream } = useLiveSeatState();
73
73
  const { currentLive } = useLiveListState();
@@ -96,6 +96,9 @@ onMounted(async () => {
96
96
  });
97
97
 
98
98
  onBeforeUnmount(async () => {
99
+ if (isPictureInPicture.value) {
100
+ exitPictureInPicture();
101
+ }
99
102
  isMounted.value = false;
100
103
  await stopPlayStream();
101
104
  isPlayedVideo.value = false;
@@ -248,8 +251,8 @@ function handleStreamListTransform() {
248
251
 
249
252
  const streamViewStyle = computed(() => {
250
253
  return {
251
- width: `${Math.floor(originStreamViewStyle.value.width) * originStreamViewStyle.value.scale}px`,
252
- height: `${Math.floor(originStreamViewStyle.value.height) * originStreamViewStyle.value.scale}px`,
254
+ width: `${Math.ceil(originStreamViewStyle.value.width) * originStreamViewStyle.value.scale}px`,
255
+ height: `${Math.ceil(originStreamViewStyle.value.height) * originStreamViewStyle.value.scale}px`,
253
256
  transform: `translate(${originStreamViewStyle.value.transformX * originStreamViewStyle.value.scale}px, ${
254
257
  originStreamViewStyle.value.transformY * originStreamViewStyle.value.scale
255
258
  }px)`,
@@ -419,6 +422,7 @@ onBeforeUnmount(() => {
419
422
  position: absolute;
420
423
  top: 0;
421
424
  left: 0;
425
+ overflow: hidden;
422
426
  }
423
427
 
424
428
  .live-core-ui {