Made a Small WebApp to Organize My Posts Images [Code Inside]

avatar

Lately, I've been posting way more screenshots in my Gaming Community articles than before, and the time I spent copying and pasting the URLs is getting unbearable...

I'm uploading the images through INLEO. If I upload the images one-by-one the time in-between piles up, but if I upload in bulk, the URLs drop as soon as each image finishes uploading, so I can't control their order. I used to do it this way in the past few weeks...

The other day, I asked AI to code me something that gives me the images as small thumbnails. It allows me to order them via Drag-and-Drop. The app should give me the new order as the output I can copy. Also, it works offline!!

The version I currently use doesn't keep the aspect ratio of the images, and I also need to put the images in a table after I order them, so I might update the code to provide these features, but I decided to share the current version here.

I feel so proud!


This code was generated with GLM 4.7 via Venice.ai:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Image Grid Reorder Tool</title>
    (html comment removed:  SortableJS for Drag and Drop )
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.15.2/Sortable.min.js"></script>
    <style>
        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
            padding: 20px;
            background-color: #f4f4f9;
            color: #333;
            max-width: 1200px;
            margin: 0 auto;
        }

        .section {
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            margin-bottom: 20px;
        }

        h2 { margin-top: 0; font-size: 1.2rem; }

        textarea {
            width: 100%;
            height: 100px;
            padding: 10px;
            border: 1px solid #ccc;
            border-radius: 4px;
            box-sizing: border-box;
            font-family: monospace;
            resize: vertical;
        }

        .controls {
            display: flex;
            align-items: center;
            gap: 20px;
            margin-top: 10px;
        }

        input[type="number"] {
            width: 60px;
            padding: 5px;
        }

        button {
            padding: 10px 20px;
            background-color: #007bff;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-weight: bold;
        }

        button:hover {
            background-color: #0056b3;
        }

        #image-grid {
            display: grid;
            gap: 15px;
            margin-top: 20px;
            min-height: 100px;
            border: 2px dashed #eee;
            padding: 10px;
        }

        .grid-item {
            background: #eee;
            border-radius: 4px;
            overflow: hidden;
            cursor: grab;
            position: relative;
            aspect-ratio: 1 / 1;
            display: flex;
            align-items: center;
            justify-content: center;
            border: 1px solid #ddd;
        }

        .grid-item:active {
            cursor: grabbing;
        }

        .grid-item img {
            width: 100%;
            height: 100%;
            object-fit: cover;
            display: block;
        }

        .sortable-ghost {
            opacity: 0.3;
        }

        #output-text {
            height: 150px;
            background-color: #f8f9fa;
        }

        .copy-btn {
            margin-top: 10px;
            background-color: #28a745;
        }
    </style>
</head>
<body>

    <div class="section">
        <h2>[Input] Paste Text with Image URLs</h2>
        <textarea id="input-text" placeholder="Paste text here... The app will find all URLs ending in jpg, png, webp, etc."></textarea>
        
        <div class="controls">
            <div>
                <label for="grid-columns">Columns: </label>
                <input type="number" id="grid-columns" value="4" min="1" max="12">
            </div>
            <button id="generate-btn">[Generate Blocks Button]</button>
        </div>
    </div>

    <div class="section">
        <h2>[The Image Section] Drag to Reorder</h2>
        <div id="image-grid"></div>
    </div>

    <div class="section">
        <h2>[The Output section]</h2>
        <textarea id="output-text" readonly placeholder="Reordered URLs will appear here..."></textarea>
        <button class="copy-btn" onclick="copyOutput()">Copy to Clipboard</button>
    </div>

    <script>
        const inputText = document.getElementById('input-text');
        const outputText = document.getElementById('output-text');
        const imageGrid = document.getElementById('image-grid');
        const generateBtn = document.getElementById('generate-btn');
        const gridColumns = document.getElementById('grid-columns');

        let sortableInstance = null;

        // Initialize SortableJS
        function initSortable() {
            if (sortableInstance) sortableInstance.destroy();
            sortableInstance = new Sortable(imageGrid, {
                animation: 150,
                ghostClass: 'sortable-ghost',
                onEnd: updateOutput
            });
        }

        // Extract URLs from text
        function extractUrls(text) {
            const urlRegex = /https?:\/\/[^\s<]+?\.(jpg|jpeg|png|gif|webp|svg|bmp)/gi;
            return text.match(urlRegex) || [];
        }

        // Update the grid layout
        function updateGridLayout() {
            imageGrid.style.gridTemplateColumns = `repeat(${gridColumns.value}, 1fr)`;
        }

        // Generate the image blocks
        generateBtn.addEventListener('click', () => {
            const urls = extractUrls(inputText.value);
            imageGrid.innerHTML = '';
            
            if (urls.length === 0) {
                alert("No image URLs found.");
                return;
            }

            urls.forEach(url => {
                const div = document.createElement('div');
                div.className = 'grid-item';
                div.dataset.url = url;

                const img = document.createElement('img');
                img.src = url;
                img.loading = "lazy";
                
                // Handle broken images
                img.onerror = () => {
                    div.style.backgroundColor = '#ffcccc';
                    div.innerText = 'Broken Link';
                };

                div.appendChild(img);
                imageGrid.appendChild(div);
            });

            updateGridLayout();
            initSortable();
            updateOutput();
        });

        // Sync Output Textarea with Grid Order
        function updateOutput() {
            const items = imageGrid.querySelectorAll('.grid-item');
            const orderedUrls = Array.from(items).map(item => item.dataset.url);
            outputText.value = orderedUrls.join('\n');
        }

        // Listen for column changes in real-time
        gridColumns.addEventListener('input', updateGridLayout);

        // Copy Function
        function copyOutput() {
            outputText.select();
            document.execCommand('copy');
            alert('Copied to clipboard!');
        }
    </script>
</body>
</html>

Posted Using INLEO



0
0
0.000
1 comments
avatar

!ALIVE | !BBH | !CTP

0
0
0.000