generateHtml function

String generateHtml(
  1. List<String> images,
  2. Map<String, String> imageSizes
)

Generates an HTML page displaying image assets in a card-based layout.

This HTML is meant to be served via a local server for asset review and cleanup. It includes features like:

  • Select All
  • Delete Selected (bulk delete)
  • Individual delete buttons
  • Image size display

The HTML also includes a full-screen loader that shows up during deletion.

Parameters:

  • images: List of image file names (e.g., 'logo.png').
  • imageSizes: A map of image file names to their size strings (e.g., {'logo.png': '120 KB'}).

Returns: A complete HTML document as a string.

Implementation

String generateHtml(List<String> images, Map<String, String> imageSizes) {
  // Generate individual image cards with checkboxes, preview, and delete button
  final cards = images.map((name) {
    final imagePath = '/assets/$name';
    final sizeKb = (imageSizes[name] ?? "0");
    return '''
      <div class="card">
        <input type="checkbox" class="img-checkbox" value="$name" />
        <div class="image-wrapper">
          <span class="size-label">$sizeKb</span>
          <img src="$imagePath" alt="$name" />
        </div>
        <div class="actions">
          <span title="$name">$name</span>
          <button onclick="confirmDelete(['$name'])">🗑️</button>
        </div>
      </div>
    ''';
  }).join();

  // Show controls only if there are images
  final controls = images.isEmpty
      ? '<p style="font-size: 18px; color: #555;">📂 No images found in your project.</p>'
      : '''
      <div id="controls">
        <label><input type="checkbox" id="select-all" /> Select All</label>
        <button onclick="bulkDelete()">🗑️ Delete Selected</button>
      </div>
  ''';

  // Final HTML output including styles, JS logic, and loader
  return '''
<!DOCTYPE html>
<html>
<head>
  <title>Image Cleaner Asset Viewer</title>
  <link rel="icon" href="data:,">
  <style>
    /* Basic layout and card styles */
    body {
      font-family: sans-serif;
      padding: 20px;
      background: #f4f4f4;
    }
    h1 {
      margin-bottom: 10px;
    }
    .container {
      display: flex;
      flex-wrap: wrap;
      gap: 16px;
    }
    .card {
      background: white;
      border-radius: 12px;
      width: 200px;
      box-shadow: 0 4px 12px rgba(0,0,0,0.1);
      display: flex;
      flex-direction: column;
      overflow: hidden;
      position: relative;
    }
    .image-wrapper {
      width: 100%;
      height: 160px;
      overflow: hidden;
      border-radius: 12px 12px 0 0;
      position: relative;
    }
    .image-wrapper img {
      width: 100%;
      height: 100%;
      object-fit: contain;
      display: block;
      background: #fafafa;
    }
    .size-label {
      position: absolute;
      top: 6px;
      right: 8px;
      background: rgba(0,0,0,0.6);
      color: white;
      font-size: 12px;
      padding: 2px 6px;
      border-radius: 4px;
    }
    .card input[type="checkbox"] {
      position: absolute;
      margin: 10px;
      z-index: 2;
    }
    .actions {
      padding: 10px;
      display: flex;
      justify-content: space-between;
      align-items: center;
      gap: 6px;
    }
    .actions span {
      font-size: 14px;
      word-break: break-word;
      flex: 1;
    }
    button {
      background: black;
      color: white;
      border: none;
      padding: 4px 8px;
      border-radius: 5px;
      cursor: pointer;
      flex-shrink: 0;
      display: flex;
      align-items: center;
      gap: 4px;
    }
    button:hover {
      background: #333;
    }
    #controls {
      margin-bottom: 12px;
      display: flex;
      gap: 10px;
      align-items: center;
    }

    /* Fullscreen loader for delete action */
    #loader {
      position: fixed;
      top: 0;
      left: 0;
      height: 100vh;
      width: 100vw;
      background: rgba(255,255,255,0.7);
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 24px;
      z-index: 9999;
      display: none;
    }
  </style>
</head>
<body>
  <div id="loader">⏳ Deleting, please wait...</div>

  <h1>Image Asset Viewer</h1>
  $controls
  <div class="container">
    $cards
  </div>

  <script>
    // Toggle all checkboxes when "Select All" is clicked
    const selectAll = document.getElementById('select-all');
    if (selectAll) {
      selectAll.addEventListener('change', function() {
        document.querySelectorAll('.img-checkbox').forEach(cb => cb.checked = this.checked);
      });
    }

    // Show loader when deleting
    function showLoader() {
      document.getElementById('loader').style.display = 'flex';
    }

    // Delete a single image or multiple
    function confirmDelete(files) {
      if (confirm('Are you sure you want to delete ' + files.join(', ') + '?')) {
        showLoader();
        fetch('/delete', {
          method: 'POST',
          headers: {'Content-Type': 'application/json'},
          body: JSON.stringify({ files: files })
        }).then(() => location.reload());
      }
    }

    // Collect selected images and call confirmDelete
    function bulkDelete() {
      const files = [...document.querySelectorAll('.img-checkbox')]
                      .filter(cb => cb.checked)
                      .map(cb => cb.value);
      if (files.length === 0) {
        alert('No files selected.');
        return;
      }
      confirmDelete(files);
    }
  </script>
</body>
</html>
''';
}