mirror of
https://github.com/stackblitz/bolt.new
synced 2025-02-06 04:48:04 +00:00
More selection tool changes
This commit is contained in:
parent
7fdab0ad6a
commit
0e86bf7709
@ -1,4 +1,4 @@
|
||||
import { memo, useCallback, useState } from 'react';
|
||||
import { memo, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
interface ScreenshotSelectorProps {
|
||||
@ -12,40 +12,96 @@ export const ScreenshotSelector = memo(
|
||||
const [isCapturing, setIsCapturing] = useState(false);
|
||||
const [selectionStart, setSelectionStart] = useState<{ x: number; y: number } | null>(null);
|
||||
const [selectionEnd, setSelectionEnd] = useState<{ x: number; y: number } | null>(null);
|
||||
const mediaStreamRef = useRef<MediaStream | null>(null);
|
||||
const videoRef = useRef<HTMLVideoElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
// Cleanup function to stop all tracks when component unmounts
|
||||
return () => {
|
||||
if (videoRef.current) {
|
||||
videoRef.current.pause();
|
||||
videoRef.current.srcObject = null;
|
||||
videoRef.current.remove();
|
||||
videoRef.current = null;
|
||||
}
|
||||
if (mediaStreamRef.current) {
|
||||
mediaStreamRef.current.getTracks().forEach((track) => track.stop());
|
||||
mediaStreamRef.current = null;
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const initializeStream = async () => {
|
||||
if (!mediaStreamRef.current) {
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getDisplayMedia({
|
||||
audio: false,
|
||||
video: {
|
||||
displaySurface: 'window',
|
||||
preferCurrentTab: true,
|
||||
surfaceSwitching: 'include',
|
||||
systemAudio: 'exclude',
|
||||
},
|
||||
} as MediaStreamConstraints);
|
||||
|
||||
// Add handler for when sharing stops
|
||||
stream.addEventListener('inactive', () => {
|
||||
if (videoRef.current) {
|
||||
videoRef.current.pause();
|
||||
videoRef.current.srcObject = null;
|
||||
videoRef.current.remove();
|
||||
videoRef.current = null;
|
||||
}
|
||||
if (mediaStreamRef.current) {
|
||||
mediaStreamRef.current.getTracks().forEach((track) => track.stop());
|
||||
mediaStreamRef.current = null;
|
||||
}
|
||||
setIsSelectionMode(false);
|
||||
setSelectionStart(null);
|
||||
setSelectionEnd(null);
|
||||
setIsCapturing(false);
|
||||
});
|
||||
|
||||
mediaStreamRef.current = stream;
|
||||
|
||||
// Initialize video element if needed
|
||||
if (!videoRef.current) {
|
||||
const video = document.createElement('video');
|
||||
video.style.opacity = '0';
|
||||
video.style.position = 'fixed';
|
||||
video.style.pointerEvents = 'none';
|
||||
video.style.zIndex = '-1';
|
||||
document.body.appendChild(video);
|
||||
videoRef.current = video;
|
||||
}
|
||||
|
||||
// Set up video with the stream
|
||||
videoRef.current.srcObject = stream;
|
||||
await videoRef.current.play();
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize stream:', error);
|
||||
setIsSelectionMode(false);
|
||||
toast.error('Failed to initialize screen capture');
|
||||
}
|
||||
}
|
||||
return mediaStreamRef.current;
|
||||
};
|
||||
|
||||
const handleCopySelection = useCallback(async () => {
|
||||
if (!isSelectionMode || !selectionStart || !selectionEnd || !containerRef.current) return;
|
||||
|
||||
setIsCapturing(true);
|
||||
try {
|
||||
const width = Math.abs(selectionEnd.x - selectionStart.x);
|
||||
const height = Math.abs(selectionEnd.y - selectionStart.y);
|
||||
|
||||
// Create a video element to capture the screen
|
||||
const video = document.createElement('video');
|
||||
video.style.opacity = '0';
|
||||
document.body.appendChild(video);
|
||||
|
||||
try {
|
||||
// Capture the entire screen
|
||||
const stream = await navigator.mediaDevices.getDisplayMedia({
|
||||
audio: false,
|
||||
video: {
|
||||
displaySurface: 'window',
|
||||
},
|
||||
} as MediaStreamConstraints);
|
||||
|
||||
// Set up video with the stream
|
||||
video.srcObject = stream;
|
||||
await video.play();
|
||||
const stream = await initializeStream();
|
||||
if (!stream || !videoRef.current) return;
|
||||
|
||||
// Wait for video to be ready
|
||||
await new Promise((resolve) => setTimeout(resolve, 300));
|
||||
|
||||
// Create temporary canvas for full screenshot
|
||||
const tempCanvas = document.createElement('canvas');
|
||||
tempCanvas.width = video.videoWidth;
|
||||
tempCanvas.height = video.videoHeight;
|
||||
tempCanvas.width = videoRef.current.videoWidth;
|
||||
tempCanvas.height = videoRef.current.videoHeight;
|
||||
const tempCtx = tempCanvas.getContext('2d');
|
||||
|
||||
if (!tempCtx) {
|
||||
@ -53,25 +109,37 @@ export const ScreenshotSelector = memo(
|
||||
}
|
||||
|
||||
// Draw the full video frame
|
||||
tempCtx.drawImage(video, 0, 0);
|
||||
tempCtx.drawImage(videoRef.current, 0, 0);
|
||||
|
||||
// Calculate scale factor between video and screen
|
||||
const scaleX = videoRef.current.videoWidth / window.innerWidth;
|
||||
const scaleY = videoRef.current.videoHeight / window.innerHeight;
|
||||
|
||||
// Get window scroll position
|
||||
const scrollX = window.scrollX;
|
||||
const scrollY = window.scrollY;
|
||||
|
||||
// Get the container's position in the page
|
||||
const containerRect = containerRef.current.getBoundingClientRect();
|
||||
|
||||
// Calculate scale factor between video and screen
|
||||
const scaleX = video.videoWidth / window.innerWidth;
|
||||
const scaleY = video.videoHeight / window.innerHeight;
|
||||
// Offset adjustments for more accurate clipping
|
||||
const leftOffset = -9; // Adjust left position
|
||||
const bottomOffset = -14; // Adjust bottom position
|
||||
|
||||
// Calculate the scaled coordinates
|
||||
const scaledX = (containerRect.left + Math.min(selectionStart.x, selectionEnd.x)) * scaleX;
|
||||
const scaledY = (containerRect.top + Math.min(selectionStart.y, selectionEnd.y)) * scaleY;
|
||||
const scaledWidth = width * scaleX;
|
||||
const scaledHeight = height * scaleY;
|
||||
// Calculate the scaled coordinates with scroll offset and adjustments
|
||||
const scaledX = Math.round(
|
||||
(containerRect.left + Math.min(selectionStart.x, selectionEnd.x) + scrollX + leftOffset) * scaleX,
|
||||
);
|
||||
const scaledY = Math.round(
|
||||
(containerRect.top + Math.min(selectionStart.y, selectionEnd.y) + scrollY + bottomOffset) * scaleY,
|
||||
);
|
||||
const scaledWidth = Math.round(Math.abs(selectionEnd.x - selectionStart.x) * scaleX);
|
||||
const scaledHeight = Math.round(Math.abs(selectionEnd.y - selectionStart.y) * scaleY);
|
||||
|
||||
// Create final canvas for the cropped area
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
canvas.width = Math.round(Math.abs(selectionEnd.x - selectionStart.x));
|
||||
canvas.height = Math.round(Math.abs(selectionEnd.y - selectionStart.y));
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
if (!ctx) {
|
||||
@ -79,7 +147,7 @@ export const ScreenshotSelector = memo(
|
||||
}
|
||||
|
||||
// Draw the cropped area
|
||||
ctx.drawImage(tempCanvas, scaledX, scaledY, scaledWidth, scaledHeight, 0, 0, width, height);
|
||||
ctx.drawImage(tempCanvas, scaledX, scaledY, scaledWidth, scaledHeight, 0, 0, canvas.width, canvas.height);
|
||||
|
||||
// Convert to blob
|
||||
const blob = await new Promise<Blob>((resolve, reject) => {
|
||||
@ -115,21 +183,18 @@ export const ScreenshotSelector = memo(
|
||||
}
|
||||
};
|
||||
reader.readAsDataURL(blob);
|
||||
|
||||
// Stop all tracks
|
||||
stream.getTracks().forEach((track) => track.stop());
|
||||
} finally {
|
||||
// Clean up video element
|
||||
document.body.removeChild(video);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to capture screenshot:', error);
|
||||
toast.error('Failed to capture screenshot');
|
||||
if (mediaStreamRef.current) {
|
||||
mediaStreamRef.current.getTracks().forEach((track) => track.stop());
|
||||
mediaStreamRef.current = null;
|
||||
}
|
||||
} finally {
|
||||
setIsCapturing(false);
|
||||
setSelectionStart(null);
|
||||
setSelectionEnd(null);
|
||||
setIsSelectionMode(false);
|
||||
setIsSelectionMode(false); // Turn off selection mode after capture
|
||||
}
|
||||
}, [isSelectionMode, selectionStart, selectionEnd, containerRef, setIsSelectionMode]);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user