Model: gemini-2.5-pro-exp-03-25
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Clipboard Format Viewer</title>
<style>
* {
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
line-height: 1.6;
margin: 0;
padding: 20px;
background-color: #f0f0f0;
}
h1 {
text-align: center;
color: #333;
}
#paste-area {
width: 100%;
height: 100px;
margin-bottom: 20px;
padding: 10px;
border: 2px dashed #aaa;
border-radius: 5px;
resize: vertical;
}
#output {
background-color: white;
border: 1px solid #ddd;
border-radius: 5px;
padding: 20px;
max-height: 70vh;
overflow-y: auto;
}
.format {
margin-bottom: 20px;
border-bottom: 1px solid #eee;
padding-bottom: 10px;
}
.format h2 {
color: #0066cc;
margin-bottom: 10px;
}
.format-content {
max-height: 200px;
overflow-y: auto;
background-color: #f5f5f5;
border: 1px solid #ddd;
border-radius: 3px;
padding: 10px;
}
pre {
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
}
</style>
</head>
<body>
<h1>Clipboard Format Viewer</h1>
<textarea id="paste-area" placeholder="Paste here or anywhere on the page"></textarea>
<div id="output"></div>
<script>
const pasteArea = document.getElementById('paste-area');
const output = document.getElementById('output');
function handlePaste(event) {
event.preventDefault();
const clipboardData = event.clipboardData || window.clipboardData;
output.innerHTML = '';
// Get available formats
const formats = clipboardData.types;
formats.forEach(format => {
const formatDiv = document.createElement('div');
formatDiv.className = 'format';
const formatTitle = document.createElement('h2');
formatTitle.textContent = format;
formatDiv.appendChild(formatTitle);
const formatContentWrapper = document.createElement('div');
formatContentWrapper.className = 'format-content';
const formatContent = document.createElement('pre');
let content = clipboardData.getData(format);
// Special handling for certain formats
if (format === 'text/html') {
const tempElement = document.createElement('div');
tempElement.innerHTML = content;
content = tempElement.innerHTML;
} else if (format === 'Files') {
const files = clipboardData.files;
content = Array.from(files).map(file => `${file.name} (${file.type}, ${file.size} bytes)`).join('\n');
}
formatContent.textContent = content;
formatContentWrapper.appendChild(formatContent);
formatDiv.appendChild(formatContentWrapper);
output.appendChild(formatDiv);
});
// Additional information about the clipboard event
const eventInfo = document.createElement('div');
eventInfo.className = 'format';
eventInfo.innerHTML = `
<h2>Clipboard Event Information</h2>
<div class="format-content">
<pre>
Event type: ${event.type}
Formats available: ${formats.join(', ')}
</pre>
</div>
`;
output.appendChild(eventInfo);
}
// Listen for paste events on the textarea
pasteArea.addEventListener('paste', handlePaste);
// Listen for paste events on the entire document
document.addEventListener('paste', handlePaste);
</script>
</body>
</html>Output a new version of this tool where any files that start with image/ are displayed on the page as max-width 100% images underneath the text that says image.png (image/png, 1522480 bytes)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Clipboard Format Viewer</title>
<style>
* {
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
line-height: 1.6;
margin: 0;
padding: 20px;
background-color: #f0f0f0;
}
h1 {
text-align: center;
color: #333;
}
#paste-area {
width: 100%;
height: 100px;
margin-bottom: 20px;
padding: 10px;
border: 2px dashed #aaa;
border-radius: 5px;
resize: vertical;
background-color: #fff;
}
#output {
background-color: white;
border: 1px solid #ddd;
border-radius: 5px;
padding: 20px;
max-height: 75vh; /* Increased max-height slightly */
overflow-y: auto;
}
.format {
margin-bottom: 20px;
border-bottom: 1px solid #eee;
padding-bottom: 10px;
}
.format:last-child {
border-bottom: none; /* Remove border for the last item */
margin-bottom: 0;
padding-bottom: 0;
}
.format h2 {
color: #0066cc;
margin-top: 0;
margin-bottom: 10px;
font-size: 1.1em;
}
.format-content {
max-height: 300px; /* Increased max-height for text content */
overflow-y: auto;
background-color: #f9f9f9; /* Slightly adjusted background */
border: 1px solid #e0e0e0; /* Slightly adjusted border */
border-radius: 3px;
padding: 10px;
font-size: 0.9em; /* Slightly smaller font for content */
}
/* Style for the container holding file info + image */
.file-entry {
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 1px dotted #ddd;
}
.file-entry:last-child {
margin-bottom: 0;
padding-bottom: 0;
border-bottom: none;
}
/* Style for displayed images */
.pasted-image {
max-width: 100%;
height: auto; /* Maintain aspect ratio */
display: block; /* Ensure it takes its own line */
margin-top: 10px; /* Space below the text info */
border: 1px solid #ccc; /* Add a light border around images */
}
pre {
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; /* Use monospace font */
}
.empty-format-note {
font-style: italic;
color: #888;
padding: 10px;
}
</style>
</head>
<body>
<h1>Clipboard Format Viewer</h1>
<textarea id="paste-area" placeholder="Paste anywhere on the page (Ctrl+V or Cmd+V)"></textarea>
<div id="output"><p>Paste content to see the available clipboard formats and data.</p></div>
<script>
const pasteArea = document.getElementById('paste-area');
const output = document.getElementById('output');
function handlePaste(event) {
// Prevent the default paste behavior (e.g., pasting into the textarea)
event.preventDefault();
const clipboardData = event.clipboardData || window.clipboardData;
output.innerHTML = ''; // Clear previous output
if (!clipboardData) {
output.innerHTML = '<p>Could not access clipboard data. Your browser might not support this feature or requires user permission.</p>';
return;
}
const formats = clipboardData.types;
const files = clipboardData.files; // Get files list once (for Files format)
const items = clipboardData.items; // Get items list once (for modern API access, especially images)
console.log("Clipboard Data:", clipboardData);
console.log("Available Formats:", formats);
console.log("Number of Files:", files.length);
console.log("Number of Items:", items ? items.length : 'N/A');
if (formats.length === 0 && files.length === 0 && (!items || items.length === 0)) {
output.innerHTML = '<p>Paste detected, but no standard formats, files, or items were found in the clipboard data.</p>';
return;
}
// --- Iterate through detected formats ---
formats.forEach(format => {
const formatDiv = document.createElement('div');
formatDiv.className = 'format';
const formatTitle = document.createElement('h2');
formatTitle.textContent = format;
formatDiv.appendChild(formatTitle);
// Wrapper for content (text or image)
const formatContentWrapper = document.createElement('div');
let contentDisplayed = false; // Flag to check if content was handled by special cases
// --- 1. Special Handling for "Files" format ---
// This is common when pasting files from the OS.
if (format === 'Files' && files.length > 0) {
const fileListContent = document.createElement('div');
fileListContent.className = 'format-content'; // Apply styling
Array.from(files).forEach(file => {
const fileContainer = document.createElement('div');
fileContainer.className = 'file-entry'; // Container for each file's info + image
// Display file information text
const fileInfo = document.createElement('div');
fileInfo.textContent = `${file.name} (${file.type || 'unknown type'}, ${file.size} bytes)`;
fileContainer.appendChild(fileInfo);
// Check if the file is an image and display it below the text
if (file.type.startsWith('image/')) {
try {
const img = document.createElement('img');
const objectURL = URL.createObjectURL(file);
img.src = objectURL;
img.alt = `${file.name} (Pasted Image)`;
img.className = 'pasted-image'; // Apply image styles
// IMPORTANT: Revoke the object URL once the image is loaded (or fails)
// to prevent memory leaks.
img.onload = () => {
console.log(`Image ${file.name} loaded, revoking ${objectURL}`);
URL.revokeObjectURL(objectURL);
};
img.onerror = () => {
console.error("Error loading image:", file.name);
URL.revokeObjectURL(objectURL); // Revoke even on error
img.alt += ' (Error loading)'; // Update alt text
// Optionally add an error message element
const errorMsg = document.createElement('div');
errorMsg.textContent = 'Could not load image preview.';
errorMsg.style.color = 'red';
fileContainer.appendChild(errorMsg);
};
fileContainer.appendChild(img); // Append the image below the file info
} catch (err) {
console.error(`Error creating object URL for ${file.name}:`, err);
const errorMsg = document.createElement('div');
errorMsg.textContent = 'Error creating image preview.';
errorMsg.style.color = 'red';
fileContainer.appendChild(errorMsg);
}
}
fileListContent.appendChild(fileContainer); // Append container for this file
});
formatContentWrapper.appendChild(fileListContent);
contentDisplayed = true;
}
// --- 2. Special Handling for "image/*" types (if not already handled by Files) ---
// This is common when pasting image data directly (e.g., screenshots, copy image)
// We use clipboardData.items for this, which is more reliable than getData for blobs.
else if (format.startsWith('image/') && files.length === 0 && items) {
// Check items only if Files format wasn't present or empty
let imageFromItemsDisplayed = false;
for (let i = 0; i < items.length; i++) {
const item = items[i];
// Find the first item matching the current format that is a file/blob
if (item.kind === 'file' && item.type === format) {
const file = item.getAsFile(); // This gets the image data as a Blob
if (file) {
try {
// Display image from item
const img = document.createElement('img');
const objectURL = URL.createObjectURL(file);
img.src = objectURL;
img.alt = `Pasted image (${format}, ${file.size} bytes)`;
img.className = 'pasted-image'; // Apply image styles
img.onload = () => {
console.log(`Image from item ${format} loaded, revoking ${objectURL}`);
URL.revokeObjectURL(objectURL);
};
img.onerror = () => {
console.error("Error loading image from item:", format);
URL.revokeObjectURL(objectURL);
img.alt += ' (Error loading)';
const errorMsg = document.createElement('div');
errorMsg.textContent = 'Could not load image preview.';
errorMsg.style.color = 'red';
formatContentWrapper.appendChild(errorMsg);
};
// Add a descriptive text above the image
const imgInfo = document.createElement('div');
// Use file.size if available, otherwise indicate unknown size
const sizeText = file.size ? `, ${file.size} bytes` : '';
imgInfo.textContent = `Image data (${format}${sizeText})`;
// No 'format-content' class needed here, just simple text
formatContentWrapper.appendChild(imgInfo);
formatContentWrapper.appendChild(img); // Append image
imageFromItemsDisplayed = true;
contentDisplayed = true;
break; // Handle only the first matching item for this format
} catch (err) {
console.error(`Error creating object URL for image item ${format}:`, err);
const errorMsg = document.createElement('div');
errorMsg.textContent = 'Error creating image preview.';
errorMsg.style.color = 'red';
formatContentWrapper.appendChild(errorMsg);
}
}
}
}
// If image wasn't found via items, fall through to default text handling below
}
// --- 3. Default Handling (Text, HTML, Fallbacks) ---
// Handles text/*, application/*, and serves as fallback if image handling failed
if (!contentDisplayed) {
const formatContent = document.createElement('pre');
let content = '';
let retrievalError = false;
try {
content = clipboardData.getData(format);
if (format === 'Files' && files.length === 0) {
// Explicitly handle Files format reported but with no actual files
content = '(No files found in clipboard)';
} else if (format.startsWith('image/') && files.length > 0) {
// If it's an image format but Files were present, assume handled there
content = '(Image data likely shown in the "Files" section above)';
} else if (content === '' && !format.startsWith('text/')) {
// If getData returns empty for non-text, it might be intentional or unavailable
content = '(Empty or non-retrievable data for this format)';
}
// Note: text/html is displayed as raw source by default in <pre>
formatContent.textContent = content;
} catch (e) {
retrievalError = true;
console.warn(`Could not getData for format: ${format}`, e);
// Provide more context based on format type
if (format.startsWith('image/')) {
content = '(Could not retrieve image data directly. It might be available via "Files" or Items API if present.)';
} else if (format === 'Files'){
content = '(Error accessing file list details.)';
} else {
content = `(Unable to retrieve data directly: ${e.message})`;
}
formatContent.textContent = content;
}
// Only add the <pre> element if there's content or an error message
if (content || retrievalError) {
formatContentWrapper.appendChild(formatContent);
formatContentWrapper.className = 'format-content'; // Add class for styling text content
} else {
// If formatContentWrapper is still empty, it means getData returned empty string
// and it wasn't an explicit Files/Image fallback case. Add a note.
const emptyNote = document.createElement('div');
emptyNote.textContent = '(No data retrieved or data is empty string)';
emptyNote.className = 'empty-format-note';
formatContentWrapper.appendChild(emptyNote);
}
}
// Append the content wrapper (containing text or image) to the format div
formatDiv.appendChild(formatContentWrapper);
output.appendChild(formatDiv); // Append the whole section for this format
});
// --- Additional Clipboard Event Information ---
const eventInfo = document.createElement('div');
eventInfo.className = 'format'; // Reuse format styling
eventInfo.innerHTML = `
<h2>Clipboard Event Information</h2>
<div class="format-content">
<pre>
Event type: ${event.type}
Formats available (${formats.length}): ${formats.join(', ') || 'None'}
Number of files reported: ${files.length}
Number of clipboard items: ${items ? items.length : 'N/A (Items API not supported/available)'}
</pre>
</div>
`;
// Append event info only if there was some content pasted initially
if (output.hasChildNodes()){
output.appendChild(eventInfo);
} else if (!output.innerHTML) { // Double check if output is truly empty
output.innerHTML = '<p>Paste detected, but no data could be read or displayed.</p>';
}
}
// Listen for paste events on the textarea (useful if focused)
pasteArea.addEventListener('paste', handlePaste);
// Listen for paste events on the entire document (allows pasting anywhere)
document.addEventListener('paste', handlePaste);
// Initial message update
if (navigator.clipboard && typeof navigator.clipboard.read !== 'function') {
const warning = document.createElement('p');
warning.style.color = 'orange';
warning.textContent = 'Warning: Your browser might not fully support the modern Clipboard API (read permission). Pasting might rely on the legacy event method.';
output.appendChild(warning);
}
</script>
</body>
</html>1,121 input, 3,656 output, {"promptTokensDetails": [{"modality": "TEXT", "tokenCount": 1121}]}