Changes to imagemeta.scroll.pub

root
root
25 days ago
Initial commit
body.html
Changed around line 1
+
+

Image Meta & XMP Editor

+

Extract metadata & apply Lightroom effects

+
+
+
+
+
+ Upload icon
+

Drop image here or click to upload

+

Supports JPG, PNG, TIFF formats

+
+
+
+
+
+

Image Metadata

+
+
+
+
+

Lightroom Effects

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

Built with care by Image Meta & XMP Editor

+
index.scroll
Changed around line 1
+ buildHtml
+ baseUrl https://imagemeta.scroll.pub
+ metaTags
+ editButton /edit.html
+ title Image Metadata & XMP Effects Editor
+ style.css
+ body.html
+ script.js
prompt.json
Changed around line 1
+ {
+ "id": "msg_016558nBTHvQPVfrNwZ5eXCq",
+ "type": "message",
+ "role": "assistant",
+ "model": "claude-3-5-sonnet-20241022",
+ "content": [
+ {
+ "type": "text",
+ "text": "Here's my solution for an image metadata and Lightroom XMP effects web app:\n\n---domain---\nimagemeta.scroll.pub\n\n---index.scroll---\nbuildHtml\nbaseUrl https://imagemeta.scroll.pub\nmetaTags\neditButton /edit.html\ntitle Image Metadata & XMP Effects Editor\nstyle.css\nbody.html\nscript.js\n\n---body.html---\n
\n

Image Meta & XMP Editor

\n

Extract metadata & apply Lightroom effects

\n
\n\n
\n
\n
\n \"Upload\n

Drop image here or click to upload

\n

Supports JPG, PNG, TIFF formats

\n
\n \n
\n\n \n\n
\n

Lightroom Effects

\n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n \n
\n
\n\n
\n

Built with care by Image Meta & XMP Editor

\n
\n\n---style.css---\n:root {\n --primary-color: #2d5ff3;\n --secondary-color: #17153a;\n --accent-color: #8b3dff;\n --background-color: #f8f9fd;\n --text-color: #2c3e50;\n --border-radius: 12px;\n --transition: all 0.3s ease;\n}\n\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n line-height: 1.6;\n color: var(--text-color);\n background: var(--background-color);\n min-height: 100vh;\n padding: 2rem;\n}\n\nheader {\n text-align: center;\n margin-bottom: 3rem;\n}\n\nh1 {\n font-size: 2.5rem;\n color: var(--secondary-color);\n margin-bottom: 0.5rem;\n background: linear-gradient(135deg, var(--primary-color), var(--accent-color));\n -webkit-background-clip: text;\n -webkit-text-fill-color: transparent;\n}\n\n.tagline {\n color: var(--text-color);\n opacity: 0.8;\n}\n\n.drop-zone {\n background: white;\n border: 3px dashed var(--primary-color);\n border-radius: var(--border-radius);\n padding: 3rem;\n text-align: center;\n cursor: pointer;\n transition: var(--transition);\n margin-bottom: 2rem;\n}\n\n.drop-zone:hover {\n background: rgba(45, 95, 243, 0.05);\n transform: translateY(-2px);\n}\n\n.drop-content img {\n margin-bottom: 1rem;\n opacity: 0.6;\n}\n\n.file-input {\n display: none;\n}\n\n.metadata-panel, .effects-panel {\n background: white;\n border-radius: var(--border-radius);\n padding: 2rem;\n margin-bottom: 2rem;\n box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);\n}\n\n.effects-controls {\n display: grid;\n gap: 1.5rem;\n margin: 2rem 0;\n}\n\n.effect-group {\n display: grid;\n gap: 0.5rem;\n}\n\ninput[type=\"range\"] {\n width: 100%;\n height: 6px;\n background: var(--background-color);\n border-radius: 3px;\n outline: none;\n -webkit-appearance: none;\n}\n\ninput[type=\"range\"]::-webkit-slider-thumb {\n -webkit-appearance: none;\n width: 18px;\n height: 18px;\n background: var(--primary-color);\n border-radius: 50%;\n cursor: pointer;\n transition: var(--transition);\n}\n\ninput[type=\"range\"]::-webkit-slider-thumb:hover {\n transform: scale(1.1);\n}\n\n.export-button {\n background: var(--primary-color);\n color: white;\n border: none;\n padding: 1rem 2rem;\n border-radius: var(--border-radius);\n cursor: pointer;\n font-weight: 600;\n transition: var(--transition);\n}\n\n.export-button:hover {\n background: var(--accent-color);\n transform: translateY(-2px);\n}\n\n.hidden {\n display: none;\n}\n\nfooter {\n text-align: center;\n margin-top: 3rem;\n color: var(--text-color);\n opacity: 0.8;\n}\n\n@media (max-width: 768px) {\n body {\n padding: 1rem;\n }\n\n h1 {\n font-size: 2rem;\n }\n\n .drop-zone {\n padding: 2rem;\n }\n}\n\n---script.js---\ndocument.addEventListener('DOMContentLoaded', () => {\n const dropZone = document.getElementById('dropZone');\n const fileInput = document.getElementById('fileInput');\n const metadataPanel = document.getElementById('metadataPanel');\n const effectsPanel = document.getElementById('effectsPanel');\n const metadataContent = document.getElementById('metadataContent');\n const exportXmp = document.getElementById('exportXmp');\n\n // Handle drag and drop\n dropZone.addEventListener('dragover', (e) => {\n e.preventDefault();\n dropZone.style.borderColor = 'var(--accent-color)';\n });\n\n dropZone.addEventListener('dragleave', () => {\n dropZone.style.borderColor = 'var(--primary-color)';\n });\n\n dropZone.addEventListener('drop', (e) => {\n e.preventDefault();\n dropZone.style.borderColor = 'var(--primary-color)';\n handleFile(e.dataTransfer.files[0]);\n });\n\n // Handle click upload\n dropZone.addEventListener('click', () => {\n fileInput.click();\n });\n\n fileInput.addEventListener('change', (e) => {\n handleFile(e.target.files[0]);\n });\n\n function handleFile(file) {\n if (!file || !file.type.startsWith('image/')) {\n alert('Please upload an image file');\n return;\n }\n\n // Read image metadata\n const reader = new FileReader();\n reader.onload = async (e) => {\n try {\n // Extract basic image metadata\n const img = new Image();\n img.src = e.target.result;\n await img.decode();\n\n const metadata = {\n 'File Name': file.name,\n 'File Size': formatFileSize(file.size),\n 'Image Width': img.naturalWidth + 'px',\n 'Image Height': img.naturalHeight + 'px',\n 'File Type': file.type\n };\n\n displayMetadata(metadata);\n metadataPanel.classList.remove('hidden');\n effectsPanel.classList.remove('hidden');\n } catch (error) {\n console.error('Error processing image:', error);\n alert('Error processing image');\n }\n };\n reader.readAsDataURL(file);\n }\n\n function formatFileSize(bytes) {\n if (bytes === 0) return '0 Bytes';\n const k = 1024;\n const sizes = ['Bytes', 'KB', 'MB', 'GB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];\n }\n\n function displayMetadata(metadata) {\n metadataContent.innerHTML = Object.entries(metadata)\n .map(([key, value]) => `

${key}: ${value}

`)\n .join('');\n }\n\n // Handle XMP export\n exportXmp.addEventListener('click', () => {\n const xmpData = generateXmpData();\n downloadXmp(xmpData);\n });\n\n function generateXmpData() {\n const exposure = document.getElementById('exposure').value;\n const contrast = document.getElementById('contrast').value;\n const highlights = document.getElementById('highlights').value;\n const shadows = document.getElementById('shadows').value;\n\n return `\n\n \n \n ${exposure}\n ${contrast}\n ${highlights}\n ${shadows}\n \n \n\n`;\n }\n\n function downloadXmp(xmpContent) {\n const blob = new Blob([xmpContent], { type: 'application/xml' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = 'lightroom-effects.xmp';\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n URL.revokeObjectURL(url);\n }\n});\n\n---end---"
+ }
+ ],
+ "stop_reason": "end_turn",
+ "stop_sequence": null,
+ "usage": {
+ "input_tokens": 289,
+ "cache_creation_input_tokens": 0,
+ "cache_read_input_tokens": 0,
+ "output_tokens": 3034
+ }
+ }
readme.scroll
Changed around line 1
+ # imagemeta.scroll.pub
+ Website generated by Claude from prompt: Web app for extracting metadata from images and applying the lightroom effects into an xmp file
script.js
Changed around line 1
+ document.addEventListener('DOMContentLoaded', () => {
+ const dropZone = document.getElementById('dropZone');
+ const fileInput = document.getElementById('fileInput');
+ const metadataPanel = document.getElementById('metadataPanel');
+ const effectsPanel = document.getElementById('effectsPanel');
+ const metadataContent = document.getElementById('metadataContent');
+ const exportXmp = document.getElementById('exportXmp');
+
+ // Handle drag and drop
+ dropZone.addEventListener('dragover', (e) => {
+ e.preventDefault();
+ dropZone.style.borderColor = 'var(--accent-color)';
+ });
+
+ dropZone.addEventListener('dragleave', () => {
+ dropZone.style.borderColor = 'var(--primary-color)';
+ });
+
+ dropZone.addEventListener('drop', (e) => {
+ e.preventDefault();
+ dropZone.style.borderColor = 'var(--primary-color)';
+ handleFile(e.dataTransfer.files[0]);
+ });
+
+ // Handle click upload
+ dropZone.addEventListener('click', () => {
+ fileInput.click();
+ });
+
+ fileInput.addEventListener('change', (e) => {
+ handleFile(e.target.files[0]);
+ });
+
+ function handleFile(file) {
+ if (!file || !file.type.startsWith('image/')) {
+ alert('Please upload an image file');
+ return;
+ }
+
+ // Read image metadata
+ const reader = new FileReader();
+ reader.onload = async (e) => {
+ try {
+ // Extract basic image metadata
+ const img = new Image();
+ img.src = e.target.result;
+ await img.decode();
+
+ const metadata = {
+ 'File Name': file.name,
+ 'File Size': formatFileSize(file.size),
+ 'Image Width': img.naturalWidth + 'px',
+ 'Image Height': img.naturalHeight + 'px',
+ 'File Type': file.type
+ };
+
+ displayMetadata(metadata);
+ metadataPanel.classList.remove('hidden');
+ effectsPanel.classList.remove('hidden');
+ } catch (error) {
+ console.error('Error processing image:', error);
+ alert('Error processing image');
+ }
+ };
+ reader.readAsDataURL(file);
+ }
+
+ function formatFileSize(bytes) {
+ if (bytes === 0) return '0 Bytes';
+ const k = 1024;
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
+ }
+
+ function displayMetadata(metadata) {
+ metadataContent.innerHTML = Object.entries(metadata)
+ .map(([key, value]) => `

${key}: ${value}

`)
+ .join('');
+ }
+
+ // Handle XMP export
+ exportXmp.addEventListener('click', () => {
+ const xmpData = generateXmpData();
+ downloadXmp(xmpData);
+ });
+
+ function generateXmpData() {
+ const exposure = document.getElementById('exposure').value;
+ const contrast = document.getElementById('contrast').value;
+ const highlights = document.getElementById('highlights').value;
+ const shadows = document.getElementById('shadows').value;
+
+ return `
+
+
+
+ xmlns:crs="http://ns.adobe.com/camera-raw-settings/1.0/">
+ ${exposure}
+ ${contrast}
+ ${highlights}
+ ${shadows}
+
+
+
+ `;
+ }
+
+ function downloadXmp(xmpContent) {
+ const blob = new Blob([xmpContent], { type: 'application/xml' });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = 'lightroom-effects.xmp';
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+ }
+ });
style.css
Changed around line 1
+ :root {
+ --primary-color: #2d5ff3;
+ --secondary-color: #17153a;
+ --accent-color: #8b3dff;
+ --background-color: #f8f9fd;
+ --text-color: #2c3e50;
+ --border-radius: 12px;
+ --transition: all 0.3s ease;
+ }
+
+ * {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ }
+
+ body {
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+ line-height: 1.6;
+ color: var(--text-color);
+ background: var(--background-color);
+ min-height: 100vh;
+ padding: 2rem;
+ }
+
+ header {
+ text-align: center;
+ margin-bottom: 3rem;
+ }
+
+ h1 {
+ font-size: 2.5rem;
+ color: var(--secondary-color);
+ margin-bottom: 0.5rem;
+ background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ }
+
+ .tagline {
+ color: var(--text-color);
+ opacity: 0.8;
+ }
+
+ .drop-zone {
+ background: white;
+ border: 3px dashed var(--primary-color);
+ border-radius: var(--border-radius);
+ padding: 3rem;
+ text-align: center;
+ cursor: pointer;
+ transition: var(--transition);
+ margin-bottom: 2rem;
+ }
+
+ .drop-zone:hover {
+ background: rgba(45, 95, 243, 0.05);
+ transform: translateY(-2px);
+ }
+
+ .drop-content img {
+ margin-bottom: 1rem;
+ opacity: 0.6;
+ }
+
+ .file-input {
+ display: none;
+ }
+
+ .metadata-panel, .effects-panel {
+ background: white;
+ border-radius: var(--border-radius);
+ padding: 2rem;
+ margin-bottom: 2rem;
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
+ }
+
+ .effects-controls {
+ display: grid;
+ gap: 1.5rem;
+ margin: 2rem 0;
+ }
+
+ .effect-group {
+ display: grid;
+ gap: 0.5rem;
+ }
+
+ input[type="range"] {
+ width: 100%;
+ height: 6px;
+ background: var(--background-color);
+ border-radius: 3px;
+ outline: none;
+ -webkit-appearance: none;
+ }
+
+ input[type="range"]::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ width: 18px;
+ height: 18px;
+ background: var(--primary-color);
+ border-radius: 50%;
+ cursor: pointer;
+ transition: var(--transition);
+ }
+
+ input[type="range"]::-webkit-slider-thumb:hover {
+ transform: scale(1.1);
+ }
+
+ .export-button {
+ background: var(--primary-color);
+ color: white;
+ border: none;
+ padding: 1rem 2rem;
+ border-radius: var(--border-radius);
+ cursor: pointer;
+ font-weight: 600;
+ transition: var(--transition);
+ }
+
+ .export-button:hover {
+ background: var(--accent-color);
+ transform: translateY(-2px);
+ }
+
+ .hidden {
+ display: none;
+ }
+
+ footer {
+ text-align: center;
+ margin-top: 3rem;
+ color: var(--text-color);
+ opacity: 0.8;
+ }
+
+ @media (max-width: 768px) {
+ body {
+ padding: 1rem;
+ }
+
+ h1 {
+ font-size: 2rem;
+ }
+
+ .drop-zone {
+ padding: 2rem;
+ }
+ }