@limrun/ui 0.4.0-rc.10 → 0.4.0-rc.11
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 +1 -1
- package/dist/index.css +1 -1
- package/dist/index.js +299 -308
- package/package.json +1 -1
- package/src/assets/iphone16pro_black_bg.webp +0 -0
- package/src/assets/iphone16pro_black_landscape_bg.webp +0 -0
- package/src/components/remote-control.css +19 -1
- package/src/components/remote-control.tsx +30 -32
- package/src/demo.tsx +1 -1
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -35,11 +35,28 @@
|
|
|
35
35
|
|
|
36
36
|
.rc-video {
|
|
37
37
|
position: absolute;
|
|
38
|
-
|
|
38
|
+
/* Size is controlled by inline style; defaults are for frameless mode */
|
|
39
|
+
width: 100%;
|
|
40
|
+
height: 100%;
|
|
39
41
|
outline: none;
|
|
40
42
|
pointer-events: none;
|
|
41
43
|
cursor: none;
|
|
42
44
|
background-color: black;
|
|
45
|
+
object-fit: contain;
|
|
46
|
+
object-position: center center;
|
|
47
|
+
inset: 0;
|
|
48
|
+
|
|
49
|
+
top: 50%;
|
|
50
|
+
left: 50%;
|
|
51
|
+
transform: translate(-50%, -50%);
|
|
52
|
+
right: auto;
|
|
53
|
+
bottom: auto;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.rc-video-ios-stretch {
|
|
57
|
+
/* iOS-only: intentionally stretch to fill the target screen box */
|
|
58
|
+
object-fit: fill;
|
|
59
|
+
z-index: 30;
|
|
43
60
|
}
|
|
44
61
|
|
|
45
62
|
.rc-video-frameless {
|
|
@@ -47,6 +64,7 @@
|
|
|
47
64
|
height: 100%;
|
|
48
65
|
top: auto;
|
|
49
66
|
left: auto;
|
|
67
|
+
width: 100%;
|
|
50
68
|
}
|
|
51
69
|
|
|
52
70
|
.rc-video-loading {
|
|
@@ -96,8 +96,8 @@ type DeviceConfig = {
|
|
|
96
96
|
loadingLogo: string;
|
|
97
97
|
loadingLogoSize: string;
|
|
98
98
|
videoPosition: {
|
|
99
|
-
portrait: {
|
|
100
|
-
landscape: {
|
|
99
|
+
portrait: { heightMultiplier?: number; widthMultiplier?: number; };
|
|
100
|
+
landscape: { heightMultiplier?: number; widthMultiplier?: number; };
|
|
101
101
|
};
|
|
102
102
|
frame: {
|
|
103
103
|
image: string;
|
|
@@ -118,8 +118,8 @@ const deviceConfig: Record<DevicePlatform, DeviceConfig> = {
|
|
|
118
118
|
loadingLogoSize: '20%',
|
|
119
119
|
// Video position as percentage of frame dimensions
|
|
120
120
|
videoPosition: {
|
|
121
|
-
portrait: {
|
|
122
|
-
landscape: {
|
|
121
|
+
portrait: { heightMultiplier: 0.9678 },
|
|
122
|
+
landscape: { widthMultiplier: 0.9678 },
|
|
123
123
|
},
|
|
124
124
|
},
|
|
125
125
|
android: {
|
|
@@ -132,8 +132,8 @@ const deviceConfig: Record<DevicePlatform, DeviceConfig> = {
|
|
|
132
132
|
loadingLogoSize: '40%',
|
|
133
133
|
// Video position as percentage of frame dimensions
|
|
134
134
|
videoPosition: {
|
|
135
|
-
portrait: {
|
|
136
|
-
landscape: {
|
|
135
|
+
portrait: { heightMultiplier: 0.967 },
|
|
136
|
+
landscape: { widthMultiplier: 0.962 },
|
|
137
137
|
},
|
|
138
138
|
},
|
|
139
139
|
};
|
|
@@ -819,9 +819,9 @@ export const RemoteControl = forwardRef<RemoteControlHandle, RemoteControlProps>
|
|
|
819
819
|
useEffect(() => {
|
|
820
820
|
const video = videoRef.current;
|
|
821
821
|
const frame = frameRef.current;
|
|
822
|
-
|
|
822
|
+
|
|
823
823
|
if (!video) return;
|
|
824
|
-
|
|
824
|
+
|
|
825
825
|
// If no frame, no positioning needed
|
|
826
826
|
if (!showFrame || !frame) {
|
|
827
827
|
setVideoStyle({});
|
|
@@ -831,34 +831,25 @@ export const RemoteControl = forwardRef<RemoteControlHandle, RemoteControlProps>
|
|
|
831
831
|
const updateVideoPosition = () => {
|
|
832
832
|
const frameWidth = frame.clientWidth;
|
|
833
833
|
const frameHeight = frame.clientHeight;
|
|
834
|
-
|
|
834
|
+
|
|
835
835
|
if (frameWidth === 0 || frameHeight === 0) return;
|
|
836
|
-
|
|
836
|
+
|
|
837
837
|
// Determine landscape based on video's intrinsic dimensions
|
|
838
838
|
const landscape = video.videoWidth > video.videoHeight;
|
|
839
839
|
setIsLandscape(landscape);
|
|
840
|
-
|
|
840
|
+
|
|
841
841
|
const pos = landscape ? config.videoPosition.landscape : config.videoPosition.portrait;
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
if ('height' in pos) {
|
|
853
|
-
const heightPx = (pos.height / 100) * frameHeight;
|
|
854
|
-
newStyle.height = `${heightPx}px`;
|
|
855
|
-
newStyle.borderRadius = `${frameWidth * config.videoBorderRadiusMultiplier}px`;
|
|
856
|
-
} else if ('width' in pos) {
|
|
857
|
-
const widthPx = (pos.width / 100) * frameWidth;
|
|
858
|
-
newStyle.width = `${widthPx}px`;
|
|
859
|
-
newStyle.borderRadius = `${frameHeight * config.videoBorderRadiusMultiplier}px`;
|
|
842
|
+
let newStyle: React.CSSProperties = {};
|
|
843
|
+
if (pos.heightMultiplier) {
|
|
844
|
+
newStyle.height = `${frameHeight * pos.heightMultiplier}px`;
|
|
845
|
+
// Let the other dimension follow the video stream's intrinsic aspect ratio.
|
|
846
|
+
newStyle.width = 'auto';
|
|
847
|
+
} else if (pos.widthMultiplier) {
|
|
848
|
+
newStyle.width = `${frameWidth * pos.widthMultiplier}px`;
|
|
849
|
+
// Let the other dimension follow the video stream's intrinsic aspect ratio.
|
|
850
|
+
newStyle.height = 'auto';
|
|
860
851
|
}
|
|
861
|
-
|
|
852
|
+
newStyle.borderRadius = `${landscape ? frameHeight * config.videoBorderRadiusMultiplier : frameWidth * config.videoBorderRadiusMultiplier}px`;
|
|
862
853
|
setVideoStyle(newStyle);
|
|
863
854
|
};
|
|
864
855
|
|
|
@@ -868,19 +859,25 @@ export const RemoteControl = forwardRef<RemoteControlHandle, RemoteControlProps>
|
|
|
868
859
|
|
|
869
860
|
resizeObserver.observe(frame);
|
|
870
861
|
resizeObserver.observe(video);
|
|
871
|
-
|
|
862
|
+
|
|
872
863
|
// Also update when the frame image loads
|
|
873
864
|
frame.addEventListener('load', updateVideoPosition);
|
|
874
|
-
|
|
865
|
+
|
|
875
866
|
// Update when video metadata loads (to get correct intrinsic dimensions)
|
|
876
867
|
video.addEventListener('loadedmetadata', updateVideoPosition);
|
|
877
868
|
|
|
869
|
+
// IMPORTANT: When the WebRTC stream changes orientation, the intrinsic video size
|
|
870
|
+
// (videoWidth/videoHeight) can change without re-firing 'loadedmetadata'.
|
|
871
|
+
// The <video> element emits 'resize' in that case.
|
|
872
|
+
video.addEventListener('resize', updateVideoPosition);
|
|
873
|
+
|
|
878
874
|
// Initial calculation
|
|
879
875
|
updateVideoPosition();
|
|
880
876
|
|
|
881
877
|
return () => {
|
|
882
878
|
resizeObserver.disconnect();
|
|
883
879
|
video.removeEventListener('loadedmetadata', updateVideoPosition);
|
|
880
|
+
video.removeEventListener('resize', updateVideoPosition);
|
|
884
881
|
frame.removeEventListener('load', updateVideoPosition);
|
|
885
882
|
};
|
|
886
883
|
}, [config, showFrame]);
|
|
@@ -1026,6 +1023,7 @@ export const RemoteControl = forwardRef<RemoteControlHandle, RemoteControlProps>
|
|
|
1026
1023
|
className={clsx(
|
|
1027
1024
|
'rc-video',
|
|
1028
1025
|
!showFrame && 'rc-video-frameless',
|
|
1026
|
+
showFrame && platform === 'ios' && 'rc-video-ios-stretch',
|
|
1029
1027
|
!videoLoaded && 'rc-video-loading',
|
|
1030
1028
|
)}
|
|
1031
1029
|
style={{
|
package/src/demo.tsx
CHANGED
|
@@ -3,7 +3,7 @@ import { createRoot } from 'react-dom/client';
|
|
|
3
3
|
import { RemoteControl, RemoteControlHandle } from './components/remote-control';
|
|
4
4
|
|
|
5
5
|
function Demo() {
|
|
6
|
-
const [url, setUrl] = useState('
|
|
6
|
+
const [url, setUrl] = useState('ws://localhost:8833/signaling');
|
|
7
7
|
const [token, setToken] = useState('lim_44530d73085dc139b2ebe1a07658bd11af2ec422ddd2ceb7');
|
|
8
8
|
const [platform, setPlatform] = useState<'ios' | 'android'>('ios');
|
|
9
9
|
const [isConnected, setIsConnected] = useState(false);
|