diff options
author | elioat <elioat@tilde.institute> | 2025-01-18 16:32:09 -0500 |
---|---|---|
committer | elioat <elioat@tilde.institute> | 2025-01-18 16:32:09 -0500 |
commit | 021b55d5c5971c2a07132adf72a57ea12db05e8c (patch) | |
tree | 1ce8c5becba6e0e344e9d2b420ac39f341b1b768 | |
parent | c07fb1c546b8161c0d67ef0e721c66a614b963fb (diff) | |
parent | e73510d9a67b6c80ea1857986787d37849c9c983 (diff) | |
download | tour-021b55d5c5971c2a07132adf72a57ea12db05e8c.tar.gz |
Merge branch 'master' of tilde.institute:~/public_repos/tour
-rw-r--r-- | html/read-write/index.html | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/html/read-write/index.html b/html/read-write/index.html new file mode 100644 index 0000000..0dd75b5 --- /dev/null +++ b/html/read-write/index.html @@ -0,0 +1,198 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>For Akkartik & Konrad</title> + <style> + body { + font-family: Arial, sans-serif; + max-width: 800px; + margin: 0 auto; + padding: 20px; + background-color: beige; + } + #imagePreview { + max-width: 100%; + margin: 20px 0; + } + .controls { + margin: 20px 0; + } + button { + padding: 10px 20px; + margin: 5px; + } + button:hover { + cursor: pointer; + } + </style> +</head> +<body> + <h1>For Akkartik & Konrad</h1> + <p>A demo of how to edit a file in place using the <a href="https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API">File System Access API</a>.</p> + <p>At a quick glance, it doesn't seem to be available in Firefox or WebKit, just Chromium browsers.</p> + <div class="controls"> + <button id="selectFile">Select a png</button> + <button id="saveChanges" disabled>Save changes</button> + <input type="file" id="fileInput" accept=".png" style="display: none;"> + </div> + + <img id="imagePreview" alt="Preview"> + + <div class="controls"> + <button id="invertColors" disabled>Invert colors</button> + <button id="grayscale" disabled>Convert to greyscale</button> + </div> + + <script> + // Store the file handle for later + let fileHandle = null; + let currentImageData = null; + + // Get DOM elements + const selectButton = document.getElementById('selectFile'); + const saveButton = document.getElementById('saveChanges'); + const invertButton = document.getElementById('invertColors'); + const grayscaleButton = document.getElementById('grayscale'); + const imagePreview = document.getElementById('imagePreview'); + + selectButton.addEventListener('click', async () => { + try { + if ('showOpenFilePicker' in window) { + // Modern File System Access API approach for browsers that support it + // This will open a file picker and return a file handle + fileHandle = await window.showOpenFilePicker({ + types: [{ + description: 'PNG Files', + accept: { + 'image/png': ['.png'] + } + }] + }); + + // Get the file object from the handle + const file = await fileHandle[0].getFile(); + handleSelectedFile(file); + } else { + // Fallback for browsers that don't support the API + document.getElementById('fileInput').click(); + } + } catch (err) { + console.error('Error selecting file:', err); + } + }); + + // Event listener for the fallback file input + document.getElementById('fileInput').addEventListener('change', async (e) => { + const file = e.target.files[0]; + if (file) { + handleSelectedFile(file); + // When in fallback mode, we download a new file, rather than saving in place + saveButton.textContent = 'Download edited image'; + } + }); + + async function handleSelectedFile(file) { + // Create a URL for the image preview + const imageUrl = URL.createObjectURL(file); + imagePreview.src = imageUrl; + + // Load the image data into a canvas for editing + await loadImageData(file); + + // Enable the editing buttons + invertButton.disabled = false; + grayscaleButton.disabled = false; + saveButton.disabled = false; + } + + // Load image data into canvas + async function loadImageData(file) { + return new Promise((resolve) => { + const img = new Image(); + img.onload = () => { + const canvas = document.createElement('canvas'); + canvas.width = img.width; + canvas.height = img.height; + const ctx = canvas.getContext('2d'); + ctx.drawImage(img, 0, 0); + currentImageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + resolve(); + }; + img.src = URL.createObjectURL(file); + }); + } + + // Invert colors + invertButton.addEventListener('click', () => { + const data = currentImageData.data; + for (let i = 0; i < data.length; i += 4) { + data[i] = 255 - data[i]; // Red + data[i + 1] = 255 - data[i + 1]; // Green + data[i + 2] = 255 - data[i + 2]; // Blue + } + updatePreview(); + }); + + // Convert to grayscale + grayscaleButton.addEventListener('click', () => { + const data = currentImageData.data; + for (let i = 0; i < data.length; i += 4) { + const avg = (data[i] + data[i + 1] + data[i + 2]) / 3; + data[i] = avg; + data[i + 1] = avg; + data[i + 2] = avg; + } + updatePreview(); + }); + + function updatePreview() { + const canvas = document.createElement('canvas'); + canvas.width = currentImageData.width; + canvas.height = currentImageData.height; + const ctx = canvas.getContext('2d'); + ctx.putImageData(currentImageData, 0, 0); + imagePreview.src = canvas.toDataURL('image/png'); + } + + saveButton.addEventListener('click', async () => { + try { + if (fileHandle) { + // Using the File System Access API + const writable = await fileHandle[0].createWritable(); + const blob = await getImageBlob(); + await writable.write(blob); + await writable.close(); + alert('Changes saved successfully!'); + } else { + // Download the edited image as a new file, if the API is not supported + const blob = await getImageBlob(); + const downloadUrl = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = downloadUrl; + a.download = 'edited-image.png'; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(downloadUrl); + } + } catch (err) { + console.error('Error saving the file:', err); + } + }); + + async function getImageBlob() { + const canvas = document.createElement('canvas'); + canvas.width = currentImageData.width; + canvas.height = currentImageData.height; + const ctx = canvas.getContext('2d'); + ctx.putImageData(currentImageData, 0, 0); + + return new Promise(resolve => { + canvas.toBlob(resolve, 'image/png'); + }); + } + </script> +</body> +</html> \ No newline at end of file |