Restore three-button capture interface: Share Entire Screen, Snip, Cancel

This commit is contained in:
2026-01-11 05:14:15 +00:00
parent ccb239c84b
commit 54d262b97d
6 changed files with 1125 additions and 15 deletions
+147
View File
@@ -0,0 +1,147 @@
# Screen Capture Tool
A standalone utility for capturing screenshots from user's screen using the native Browser Screen Capture API.
## Features
- 📸 Capture screenshots from user-selected screen/window
- 🎯 No external dependencies
- ⚡ Returns canvas for flexible post-processing
- 🛡️ Graceful error handling
- ♿ Respects user permissions
## Installation
Copy the `screen-capture` folder to your project:
```
your-project/
├── tools/
│ └── screen-capture/
│ ├── index.js
│ └── README.md
```
## Usage
### Basic Screenshot Capture
```javascript
import { captureScreenshot } from './tools/screen-capture/index.js';
const canvas = await captureScreenshot();
if (canvas) {
// Canvas is ready for use
// Convert to File, display, or manipulate further
}
```
### Convert to File
```javascript
import { captureScreenshot, canvasToFile } from './tools/screen-capture/index.js';
const canvas = await captureScreenshot();
if (canvas) {
const file = await canvasToFile(canvas, 'my-screenshot.png');
// Use file with FormData, attachments, etc.
}
```
### One-liner with Blob
```javascript
const canvas = await captureScreenshot();
canvas?.toBlob((blob) => {
const file = new File([blob], `screenshot-${Date.now()}.png`, { type: 'image/png' });
// Use file...
});
```
## API Reference
### `captureScreenshot()`
**Returns:** `Promise<Canvas | null>`
Opens native browser dialog for user to select screen/window to capture. Returns a canvas element containing the screenshot, or null if user cancels or error occurs.
**Example:**
```javascript
const canvas = await captureScreenshot();
```
### `canvasToFile(canvas, filename?)`
**Parameters:**
- `canvas` (Canvas): Canvas element to convert
- `filename` (string, optional): Custom filename. Defaults to `screenshot-{timestamp}.png`
**Returns:** `Promise<File>`
Converts canvas to PNG File object for easy handling in file uploads or attachments.
**Example:**
```javascript
const file = await canvasToFile(canvas, 'custom-name.png');
```
## Browser Support
Requires browsers supporting the Screen Capture API:
- Chrome 72+
- Edge 79+
- Firefox 66+
- Safari 13+
Note: User must grant permission for each capture request.
## Error Handling
The tool gracefully handles:
- User cancellation of the capture dialog
- Browser permission denial
- Browser incompatibility
- Missing canvas/video support
All errors are logged to console and return null.
## Examples
### React Component
```jsx
import { captureScreenshot, canvasToFile } from './tools/screen-capture/index.js';
export function ScreenshotButton() {
const handleCapture = async () => {
const canvas = await captureScreenshot();
if (canvas) {
const file = await canvasToFile(canvas);
console.log('Screenshot captured:', file.name);
}
};
return <button onClick={handleCapture}>Take Screenshot</button>;
}
```
### With Form Submission
```javascript
import { captureScreenshot, canvasToFile } from './tools/screen-capture/index.js';
const canvas = await captureScreenshot();
if (canvas) {
const file = await canvasToFile(canvas);
const formData = new FormData();
formData.append('screenshot', file);
await fetch('/api/upload', { method: 'POST', body: formData });
}
```
## License
MIT - Use freely in your projects
+245
View File
@@ -0,0 +1,245 @@
/**
* Screen Capture Tool
* Captures a screenshot from the user's screen using the Screen Capture API
*
* @module ScreenCapture
*/
(function() {
console.log('Starting Screen Capture Tool initialization...');
window.captureScreenshot = async function() {
console.log('captureScreenshot called');
try {
// Request screen capture from user
const mediaStream = await navigator.mediaDevices.getDisplayMedia({
video: { mediaSource: 'screen' }
});
// Create video element to capture frame
const video = document.createElement('video');
video.srcObject = mediaStream;
return new Promise((resolve) => {
video.onloadedmetadata = () => {
// Create canvas and draw video frame
const canvas = document.createElement('canvas');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(video, 0, 0);
// Stop all media tracks
mediaStream.getTracks().forEach(track => track.stop());
// Return canvas for further processing
resolve(canvas);
};
video.play();
});
} catch (error) {
console.error('Screenshot capture failed:', error);
return null;
}
};
window.captureSnip = async function() {
console.log('captureSnip called');
try {
// First, get the full screen capture
const mediaStream = await navigator.mediaDevices.getDisplayMedia({
video: { mediaSource: 'screen' }
});
// Create video element to capture frame
const video = document.createElement('video');
video.srcObject = mediaStream;
return new Promise((resolve) => {
video.onloadedmetadata = () => {
// Capture full screen to canvas
const fullCanvas = document.createElement('canvas');
fullCanvas.width = video.videoWidth;
fullCanvas.height = video.videoHeight;
const fullCtx = fullCanvas.getContext('2d');
fullCtx.drawImage(video, 0, 0);
// Stop video tracks
mediaStream.getTracks().forEach(track => track.stop());
// Show selection overlay
createSelectionOverlay(fullCanvas, resolve);
};
video.play();
});
} catch (error) {
console.error('Snip capture failed:', error);
return null;
}
};
function createSelectionOverlay(fullCanvas, resolve) {
console.log('createSelectionOverlay called');
// Create overlay container
const overlay = document.createElement('div');
overlay.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.3);
cursor: crosshair;
z-index: 99999;
user-select: none;
`;
// Create canvas for drawing selection box
const selectionCanvas = document.createElement('canvas');
selectionCanvas.style.cssText = `
position: fixed;
top: 0;
left: 0;
cursor: crosshair;
z-index: 100000;
`;
selectionCanvas.width = window.innerWidth;
selectionCanvas.height = window.innerHeight;
// Scale factor for mapping screen pixels to window pixels
const scaleX = fullCanvas.width / window.innerWidth;
const scaleY = fullCanvas.height / window.innerHeight;
let isDrawing = false;
let startX = 0;
let startY = 0;
let endX = 0;
let endY = 0;
const ctx = selectionCanvas.getContext('2d');
function drawSelection() {
ctx.clearRect(0, 0, selectionCanvas.width, selectionCanvas.height);
if (!isDrawing && (startX === endX && startY === endY)) {
return;
}
const x = Math.min(startX, endX);
const y = Math.min(startY, endY);
const width = Math.abs(endX - startX);
const height = Math.abs(endY - startY);
// Draw selection rectangle
ctx.strokeStyle = '#0EA5E9';
ctx.lineWidth = 2;
ctx.strokeRect(x, y, width, height);
// Fill with semi-transparent color
ctx.fillStyle = 'rgba(14, 165, 233, 0.1)';
ctx.fillRect(x, y, width, height);
// Draw corner handles
const handleSize = 8;
ctx.fillStyle = '#0EA5E9';
ctx.fillRect(x - handleSize / 2, y - handleSize / 2, handleSize, handleSize);
ctx.fillRect(x + width - handleSize / 2, y - handleSize / 2, handleSize, handleSize);
ctx.fillRect(x - handleSize / 2, y + height - handleSize / 2, handleSize, handleSize);
ctx.fillRect(x + width - handleSize / 2, y + height - handleSize / 2, handleSize, handleSize);
}
selectionCanvas.addEventListener('mousedown', (e) => {
isDrawing = true;
startX = e.clientX;
startY = e.clientY;
endX = startX;
endY = startY;
});
selectionCanvas.addEventListener('mousemove', (e) => {
if (isDrawing) {
endX = e.clientX;
endY = e.clientY;
drawSelection();
}
});
selectionCanvas.addEventListener('mouseup', () => {
if (isDrawing) {
isDrawing = false;
const x = Math.min(startX, endX);
const y = Math.min(startY, endY);
const width = Math.abs(endX - startX);
const height = Math.abs(endY - startY);
// Validate selection
if (width < 10 || height < 10) {
console.warn('Selection too small');
cleanup();
resolve(null);
return;
}
// Create cropped canvas from full screenshot
const croppedCanvas = document.createElement('canvas');
croppedCanvas.width = width * scaleX;
croppedCanvas.height = height * scaleY;
const croppedCtx = croppedCanvas.getContext('2d');
// Draw cropped section from full canvas
croppedCtx.drawImage(
fullCanvas,
x * scaleX,
y * scaleY,
width * scaleX,
height * scaleY,
0,
0,
width * scaleX,
height * scaleY
);
cleanup();
resolve(croppedCanvas);
}
});
// Handle escape key
const handleKeydown = (e) => {
if (e.key === 'Escape') {
cleanup();
resolve(null);
}
};
function cleanup() {
document.removeEventListener('keydown', handleKeydown);
overlay.remove();
selectionCanvas.remove();
}
document.addEventListener('keydown', handleKeydown);
document.body.appendChild(overlay);
document.body.appendChild(selectionCanvas);
console.log('Snipping tool active: Draw a rectangle to select area. Press ESC to cancel.');
}
window.canvasToFile = async function(canvas, filename = null) {
console.log('canvasToFile called');
return new Promise((resolve) => {
const name = filename || `screenshot-${Date.now()}.png`;
canvas.toBlob((blob) => {
const file = new File([blob], name, { type: 'image/png' });
resolve(file);
});
});
};
console.log('✓ Screen Capture Tool loaded successfully');
console.log('Functions available: window.captureScreenshot, window.captureSnip, window.canvasToFile');
})();