(() => {
  
  var jquery_default = window.jQuery;

  
  var AjaxClient = class {
    constructor(config) {
      this.ajaxUrl = config.ajaxUrl;
      this.nonce = config.nonce;
    }
    






    async post(action, data = {}) {
      const params = new URLSearchParams({
        action,
        nonce: this.nonce,
        ...data
      });
      const response = await fetch(this.ajaxUrl, {
        method: "POST",
        headers: { "Content-Type": "application/x-www-form-urlencoded" },
        body: params
      });
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }
      const json = await response.json();
      if (!json.success) {
        throw new Error(json.data || "Unknown error");
      }
      return json.data;
    }
  };

  
  var Helpers = class _Helpers {
    static setText(id, value) {
      const el = document.getElementById(id);
      if (el) el.textContent = value;
    }
    static escHtml(str) {
      const div = document.createElement("div");
      div.textContent = str;
      return div.innerHTML;
    }
    static formatSize(bytes) {
      if (bytes === 0) return "0 B";
      const units = ["B", "KB", "MB", "GB"];
      const i = Math.floor(Math.log(bytes) / Math.log(1024));
      return (bytes / Math.pow(1024, i)).toFixed(1) + " " + units[i];
    }
    static delay(ms) {
      return new Promise((resolve) => setTimeout(resolve, ms));
    }
    


    static clearNavActiveState() {
      ["#cwpm-nav-settings", "#cwpm-nav-scan", "#cwpm-logs-nav", "#cwpm-trash-nav"].forEach((sel) => {
        var _a;
        (_a = document.querySelector(sel)) == null ? void 0 : _a.classList.remove("active");
      });
    }
    






    static createActionsTooltip(trigger, getSelected) {
      const tooltip = document.createElement("div");
      tooltip.className = "cwpm-actions-tooltip";
      tooltip.hidden = true;
      trigger.appendChild(tooltip);
      trigger.addEventListener("mouseenter", () => {
        const items = getSelected();
        if (items.length === 0) return;
        const heading = '<div class="cwpm-actions-tooltip__heading">Click to set actions</div>';
        const html = items.map(
          (item) => `<div class="cwpm-actions-tooltip__item"><span class="cwpm-actions-tooltip__dot" style="background:${item.color}"></span>${_Helpers.escHtml(item.label)}</div>`
        ).join("");
        tooltip.innerHTML = heading + html;
        tooltip.hidden = false;
      });
      trigger.addEventListener("mouseleave", () => {
        tooltip.hidden = true;
      });
      return tooltip;
    }
  };

  
  var StepWizard = class {
    constructor() {
      this.nav = document.getElementById("cwpm-wizard-nav");
      this.stepsContainer = document.getElementById("cwpm-steps");
      this.navItems = this.nav ? Array.from(this.nav.querySelectorAll(".cwpm-wizard-nav__item")) : [];
      this.steps = this.stepsContainer ? Array.from(this.stepsContainer.querySelectorAll(".cwpm-step")) : [];
      this.currentStep = 1;
      this.completedSteps =  new Set();
      this._transitionTimer = null;
    }
    init() {
      if (!this.nav || !this.steps.length) {
        return;
      }
      this.bindStepButtons();
      this.navItems.forEach((item) => {
        item.style.cursor = "pointer";
        item.addEventListener("click", () => {
          const stepNum = parseInt(item.dataset.step, 10);
          if (stepNum && stepNum !== this.currentStep) {
            this.goTo(stepNum);
            document.dispatchEvent(new CustomEvent("cwpm:nav-step", { detail: { step: stepNum } }));
          }
        });
      });
      this.goTo(1);
    }
    bindStepButtons() {
      document.querySelectorAll(".cwpm-next-step").forEach((btn) => {
        btn.addEventListener("click", () => {
          const target = parseInt(btn.dataset.next, 10);
          this.markCompleted(this.currentStep);
          this.goTo(target);
        });
      });
      document.querySelectorAll(".cwpm-prev-step").forEach((btn) => {
        btn.addEventListener("click", () => {
          const target = parseInt(btn.dataset.prev, 10);
          this.goTo(target);
        });
      });
    }
    goTo(stepNumber) {
      if (stepNumber < 1 || stepNumber > this.steps.length) {
        return;
      }
      if (this._transitionTimer !== null) {
        clearTimeout(this._transitionTimer);
        this._transitionTimer = null;
      }
      this.steps.forEach((s) => s.classList.remove("cwpm-step--exiting"));
      const prevStep = this.steps.find((s) => parseInt(s.dataset.step, 10) === this.currentStep);
      const nextStep = this.steps.find((s) => parseInt(s.dataset.step, 10) === stepNumber);
      this.currentStep = stepNumber;
      const stepViewMap = { 1: "scan", 2: null, 3: "results" };
      const viewParam = stepViewMap[stepNumber] || null;
      const url = new URL(window.location.href);
      if (viewParam) {
        url.searchParams.set("view", viewParam);
      } else {
        url.searchParams.delete("view");
      }
      history.replaceState(null, "", url.toString());
      this.navItems.forEach((item) => {
        const num = parseInt(item.dataset.step, 10);
        item.classList.toggle("cwpm-wizard-nav__item--active", num === stepNumber);
        item.toggleAttribute("aria-current", num === stepNumber);
      });
      const showNext = () => {
        if (nextStep) {
          nextStep.classList.add("cwpm-step--active", "cwpm-step--entering");
          nextStep.addEventListener("animationend", () => {
            nextStep.classList.remove("cwpm-step--entering");
          }, { once: true });
        }
        window.scrollTo({ top: 0, behavior: "smooth" });
      };
      if (prevStep && prevStep !== nextStep && prevStep.classList.contains("cwpm-step--active")) {
        prevStep.classList.remove("cwpm-step--active");
        prevStep.classList.add("cwpm-step--exiting");
        const proceed = () => {
          this._transitionTimer = null;
          prevStep.classList.remove("cwpm-step--exiting");
          showNext();
        };
        prevStep.addEventListener("animationend", proceed, { once: true });
        this._transitionTimer = setTimeout(() => {
          if (prevStep.classList.contains("cwpm-step--exiting")) {
            prevStep.classList.remove("cwpm-step--exiting");
            showNext();
          }
          this._transitionTimer = null;
        }, 300);
      } else {
        showNext();
      }
    }
    next() {
      this.markCompleted(this.currentStep);
      this.goTo(this.currentStep + 1);
    }
    markCompleted(stepNumber) {
      this.completedSteps.add(stepNumber);
      const item = this.navItems.find((i) => parseInt(i.dataset.step, 10) === stepNumber);
      if (item) {
        item.classList.add("cwpm-wizard-nav__item--completed");
      }
    }
  };

  
  var ThumbnailManager = class {
    constructor(ajaxClient) {
      this.ajaxClient = ajaxClient;
    }
    init() {
      this.wireToggles();
      this.wireBatchSize();
    }
    wireToggles() {
      const toggles = document.querySelectorAll(".cwpm-size-toggle");
      toggles.forEach((toggle) => {
        toggle.addEventListener("change", (e) => this.onToggle(e));
      });
    }
    wireBatchSize() {
      const input = document.getElementById("cwpm-batch-size");
      if (!input) {
        return;
      }
      input.addEventListener("change", () => this.saveBatchSize(input));
    }
    async saveBatchSize(input) {
      const value = Math.max(10, Math.min(200, parseInt(input.value, 10) || 50));
      input.value = value;
      try {
        await this.ajaxClient.post("cwpm_save_batch_size", { batch_size: value });
      } catch (err) {
        console.error("[CWPM] Failed to save batch size:", err);
      }
    }
    async onToggle(e) {
      const toggle = e.target;
      const size = toggle.dataset.size;
      const enabled = toggle.checked;
      const row = toggle.closest("tr");
      if (row) {
        row.classList.add("cwpm-saving");
      }
      try {
        await this.ajaxClient.post("cwpm_toggle_thumbnail_size", {
          size,
          enabled: enabled ? "1" : "0"
        });
      } catch (err) {
        console.error("[CWPM] Failed to save thumbnail size:", err);
        toggle.checked = !enabled;
      } finally {
        if (row) {
          row.classList.remove("cwpm-saving");
        }
      }
    }
  };

  
  var MediaScanner = class {
    constructor(wizard, ajaxClient) {
      var _a;
      this.wizard = wizard;
      this.ajaxClient = ajaxClient;
      this.progressWrap = document.getElementById("cwpm-scan-progress");
      this.progressBar = document.getElementById("cwpm-progress-bar");
      this.statusText = document.getElementById("cwpm-scan-status");
      this.controlsWrap = document.getElementById("cwpm-scan-controls");
      this.cardNewScan = document.getElementById("cwpm-card-new-scan");
      this.cardLastScan = document.getElementById("cwpm-card-last-scan");
      this.cardResumeScan = document.getElementById("cwpm-card-resume-scan");
      this.scanCards = document.getElementById("cwpm-scan-cards");
      this.folderList = document.getElementById("cwpm-folder-list");
      this.statsGrid = document.getElementById("cwpm-stats-grid");
      this.actionBar = document.getElementById("cwpm-action-bar");
      this.cleanBtn = document.getElementById("cwpm-start-clean");
      this.disclaimerProceedBtn = document.getElementById("cwpm-disclaimer-proceed");
      this.disclaimerCheckbox = document.getElementById("cwpm-disclaimer-checkbox");
      this.cleanProgress = document.getElementById("cwpm-clean-progress");
      this.cleanProgressBar = document.getElementById("cwpm-clean-progress-bar");
      this.cleanStatus = document.getElementById("cwpm-clean-progress-status");
      this.disclaimerShown = false;
      this.scanAborted = false;
      this.scanResults = [];
      const savedPrefs = ((_a = window.CWPMConfig) == null ? void 0 : _a.userPrefs) || {};
      this._groupByMonth = savedPrefs.group_by_month !== void 0 ? savedPrefs.group_by_month : true;
      this._currentPage = 1;
      this._perPage = savedPrefs.per_page !== void 0 ? savedPrefs.per_page : 200;
      this._searchQuery = "";
      this._statusFilter = "";
      this._totalResults = 0;
      this._totalPages = 1;
      this._selectAllActive = false;
      this._allResultsForClean = [];
      this._excludedRelPaths = /* @__PURE__ */ new Set();
    }
    init() {
      var _a;
      const scanMediaBtn = document.querySelector('.cwpm-next-step[data-next="2"]');
      if (scanMediaBtn) {
        const clone = scanMediaBtn.cloneNode(true);
        scanMediaBtn.parentNode.replaceChild(clone, scanMediaBtn);
        clone.addEventListener("click", (e) => {
          e.preventDefault();
          this.wizard.markCompleted(1);
          this.wizard.goTo(2);
          if (this.controlsWrap) this.controlsWrap.style.display = "";
          if (this.progressWrap) this.progressWrap.style.display = "none";
        });
      }
      if (this.cardNewScan) {
        this.cardNewScan.addEventListener("click", () => {
          var _a2;
          const meta = (_a2 = window.CWPMConfig) == null ? void 0 : _a2.scanMeta;
          if (meta) {
            if (typeof window.Modal !== "undefined") {
              window.Modal.open("cwpm-confirm-new-scan-modal");
            } else {
              this.runScan(true);
            }
          } else {
            this.runScan(true);
          }
        });
      }
      const confirmNewScanBtn = document.getElementById("cwpm-confirm-new-scan-proceed");
      if (confirmNewScanBtn) {
        confirmNewScanBtn.addEventListener("click", () => {
          var _a2;
          (_a2 = window.Modal) == null ? void 0 : _a2.close("cwpm-confirm-new-scan-modal");
          this.runScan(true);
        });
      }
      if (this.cardLastScan) {
        this.cardLastScan.addEventListener("click", async () => {
          if (this.wizard) {
            this.wizard.markCompleted(1);
            this.wizard.markCompleted(2);
            this.wizard.goTo(3);
          }
          if (this.statsGrid) this.statsGrid.style.display = "";
          if (this.actionBar) this.actionBar.style.display = "flex";
          await this.loadStats();
          await this.loadResults({ page: 1 });
          this.updateScanDate();
        });
      }
      if (this.cardResumeScan) {
        this.cardResumeScan.addEventListener("click", () => {
          this.runScan();
        });
      }
      this.updateScanCards();
      if (this.cleanBtn) {
        this.cleanBtn.addEventListener("click", () => this.onStartCleanClick());
      }
      if (this.disclaimerCheckbox && this.disclaimerProceedBtn) {
        this.disclaimerCheckbox.addEventListener("change", () => {
          this.disclaimerProceedBtn.disabled = !this.disclaimerCheckbox.checked;
        });
      }
      if (this.disclaimerProceedBtn) {
        this.disclaimerProceedBtn.addEventListener("click", () => {
          if (typeof window.Modal !== "undefined") {
            window.Modal.close("cwpm-disclaimer-modal");
          }
          this.runClean();
        });
      }
      const disclaimerTrashLink = document.getElementById("cwpm-disclaimer-trash-link");
      if (disclaimerTrashLink) {
        disclaimerTrashLink.addEventListener("click", (e) => {
          var _a2;
          e.preventDefault();
          if (typeof window.Modal !== "undefined") {
            window.Modal.close("cwpm-disclaimer-modal");
          }
          (_a2 = document.querySelector("#cwpm-trash-nav")) == null ? void 0 : _a2.click();
        });
      }
      const groupToggle = document.getElementById("cwpm-group-by-month");
      if (groupToggle) {
        const savedPrefsForGroup = ((_a = window.CWPMConfig) == null ? void 0 : _a.userPrefs) || {};
        if (savedPrefsForGroup.group_by_month !== void 0) {
          groupToggle.checked = savedPrefsForGroup.group_by_month;
        } else {
          const localSaved = localStorage.getItem("cwpm_group_by_month");
          if (localSaved !== null) {
            groupToggle.checked = localSaved === "true";
          }
        }
        this._groupByMonth = groupToggle.checked;
        groupToggle.addEventListener("change", () => {
          this._groupByMonth = groupToggle.checked;
          localStorage.setItem("cwpm_group_by_month", String(groupToggle.checked));
          this._savePrefs({ group_by_month: groupToggle.checked });
          this.loadResults();
        });
      }
      this.wireFilters();
      this.wirePagination();
      this.wireActionButtons();
      document.addEventListener("cwpm:nav-step", async (e) => {
        var _a2;
        const step = (_a2 = e.detail) == null ? void 0 : _a2.step;
        if (step === 2) {
          if (this.controlsWrap) this.controlsWrap.style.display = "";
          if (this.progressWrap) this.progressWrap.style.display = "none";
        } else if (step === 3) {
          await this.handleResultsNavClick();
        }
      });
      const goToScanLink = document.getElementById("cwpm-go-to-scan");
      if (goToScanLink) {
        goToScanLink.addEventListener("click", (e) => {
          e.preventDefault();
          if (this.wizard) this.wizard.goTo(2);
        });
      }
    }
    /**
     * Main scan flow: init → batch loop → render results.
     */
    async runScan(forceNew = false) {
      var _a;
      if (this.wizard) {
        this.wizard.markCompleted(1);
        this.forceStep(2);
      }
      this.scanAborted = false;
      const stopBtn = document.getElementById("cwpm-stop-scan");
      if (stopBtn) {
        stopBtn.style.display = "";
        stopBtn.onclick = async () => {
          this.scanAborted = true;
          try {
            await this.ajaxClient.post("cwpm_scan_abort");
          } catch (e) {
            console.warn("Failed to notify server of scan abort:", e);
          }
        };
      }
      const existingBack = document.querySelector(".cwpm-prev-step[data-cwpm-back-button]");
      if (existingBack) existingBack.remove();
      if (this.controlsWrap) this.controlsWrap.style.display = "none";
      if (this.progressWrap) this.progressWrap.style.display = "block";
      this.statusText.textContent = "Preparing scan...";
      this.progressBar.style.width = "0%";
      this.scanResults = [];
      if (this.folderList) this.folderList.innerHTML = "";
      if (this.statsGrid) this.statsGrid.style.display = "none";
      if (this.actionBar) this.actionBar.style.display = "none";
      try {
        const initParams = forceNew ? { force_new: "1" } : {};
        const initData = await this.ajaxClient.post("cwpm_scan_init", initParams);
        const { folders, total, batch_size: batchSize, scan_id: scanId, resume, last_file_index: lastFileIndex } = initData;
        if (total === 0) {
          this.statusText.textContent = "No media files found.";
          if (stopBtn) {
            stopBtn.style.display = "none";
            stopBtn.onclick = null;
          }
          this.showBackButton();
          return;
        }
        const allFiles = [];
        for (const folder of folders) {
          for (const file of folder.files) {
            allFiles.push({
              year: folder.year,
              month: folder.month,
              file
            });
          }
        }
        let processed = 0;
        let pending = allFiles.slice();
        if (resume && lastFileIndex > 0) {
          pending = allFiles.slice(lastFileIndex);
          processed = lastFileIndex;
          this.statusText.textContent = `Resuming scan from file ${processed}/${total}...`;
          this.progressBar.style.width = Math.round(processed / total * 80) + "%";
        }
        while (pending.length > 0 && !this.scanAborted) {
          const batch = pending.splice(0, batchSize);
          const pct = Math.round((processed + batch.length) / total * 80);
          this.statusText.textContent = `Scanning files... ${processed + batch.length}/${total} (${pct}%)`;
          const response = await this.postBatch(batch);
          if (response.timeout && response.remaining) {
            pending.unshift(...response.remaining);
            processed += response.files.length;
          } else {
            processed += batch.length;
          }
          this.progressBar.style.width = Math.round(processed / total * 80) + "%";
        }
        if (stopBtn) stopBtn.onclick = null;
        if (this.scanAborted) {
          this.statusText.textContent = `Scan stopped \u2014 ${processed} files scanned.`;
        } else {
          this.statusText.textContent = `Checking for broken media entries...`;
          try {
            await this.ajaxClient.post("cwpm_scan_broken");
          } catch (brokenErr) {
            console.warn("Broken media scan failed:", brokenErr);
          }
          this.statusText.textContent = `Scan complete! ${processed} files scanned.`;
          this.progressBar.style.width = "80%";
          try {
            await this.ajaxClient.post("cwpm_scan_complete");
          } catch (e) {
            console.warn("Failed to mark scan as complete:", e);
          }
          window.CWPMConfig.scanMeta = {
            status: "complete",
            scan_id: scanId,
            completed_at: (/* @__PURE__ */ new Date()).toISOString(),
            started_at: ((_a = window.CWPMConfig.scanMeta) == null ? void 0 : _a.started_at) || (/* @__PURE__ */ new Date()).toISOString(),
            scanned_files: processed,
            total_files: total
          };
          this.updateScanCards();
          this.updateScanDate();
        }
        await this.loadStats();
        await this.loadResults({ page: 1 });
        if (this.statsGrid) this.statsGrid.style.display = "";
        if (this.actionBar) this.actionBar.style.display = "flex";
        if (this.wizard) {
          this.wizard.markCompleted(2);
          this.wizard.goTo(3);
        }
      } catch (err) {
        if (stopBtn) stopBtn.onclick = null;
        this.statusText.textContent = `Scan failed: ${err.message}`;
        if (this.controlsWrap) this.controlsWrap.style.display = "";
      }
    }
    /**
     * Send a batch of files to the scan_batch endpoint.
     *
     * Uses FormData to support nested array structure.
     *
     * @param {Array} files Array of { year, month, file }.
     * @returns {Promise<Array>} Array of file result objects.
     */
    async postBatch(files) {
      const formData = new FormData();
      formData.append("action", "cwpm_scan_batch");
      formData.append("nonce", this.ajaxClient.nonce);
      files.forEach((f, i) => {
        formData.append(`files[${i}][year]`, f.year);
        formData.append(`files[${i}][month]`, f.month);
        formData.append(`files[${i}][file]`, f.file);
      });
      const response = await fetch(this.ajaxClient.ajaxUrl, {
        method: "POST",
        body: formData
      });
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }
      const json = await response.json();
      if (!json.success) {
        throw new Error(json.data || "Batch scan failed");
      }
      return json.data;
    }
    /**
     * Group flat results array into year → months → files structure.
     */
    groupByYearMonth(results) {
      const map = {};
      for (const file of results) {
        const key = file.year;
        if (!map[key]) {
          map[key] = { year: file.year, months: {}, totalSize: 0 };
        }
        if (!map[key].months[file.month]) {
          map[key].months[file.month] = { month: file.month, files: [] };
        }
        map[key].months[file.month].files.push(file);
        map[key].totalSize += file.size || 0;
      }
      return Object.keys(map).sort((a, b) => b.localeCompare(a)).map((year) => {
        const entry = map[year];
        return {
          year: entry.year,
          totalSize: Helpers.formatSize(entry.totalSize),
          months: Object.keys(entry.months).sort().map((m) => entry.months[m])
        };
      });
    }
    populateStats(results) {
      if (!this.statsGrid) return;
      const totalFiles = results.length;
      const inMedia = results.filter((f) => f.in_media && !f.broken);
      const usedThumbs = inMedia.filter((f) => f.used).length;
      const unusedThumbs = inMedia.filter((f) => !f.used).length;
      const orphaned = results.filter((f) => !f.in_media).length;
      const broken = results.filter((f) => f.broken).length;
      const totalSize = results.reduce((s, f) => {
        let size = f.size || 0;
        (f.sizes || []).forEach((sz) => {
          if (sz.file_size && (sz.status === "ok" || sz.status === "pending")) {
            size += sz.file_size;
          }
        });
        (f.orphan_thumbs || []).forEach((t) => {
          size += t.size || 0;
        });
        return s + size;
      }, 0);
      const savings = results.reduce((s, f) => s + (f.savings_raw || 0), 0);
      Helpers.setText("cwpm-stat-total-files", totalFiles);
      Helpers.setText("cwpm-stat-used-thumbs", usedThumbs);
      Helpers.setText("cwpm-stat-unused-thumbs", unusedThumbs);
      Helpers.setText("cwpm-stat-orphaned", orphaned);
      Helpers.setText("cwpm-stat-broken", broken);
      Helpers.setText("cwpm-stat-total-size", Helpers.formatSize(totalSize));
      Helpers.setText("cwpm-stat-savings", Helpers.formatSize(savings));
      this.statsGrid.style.display = "";
    }
    renderYearResults(yearsData) {
      if (!this.folderList) return;
      this.folderList.innerHTML = "";
      yearsData.forEach((yearData) => {
        const totalFiles = yearData.months.reduce((s, m) => s + m.files.length, 0);
        const tbodyRows = yearData.months.map((monthData) => {
          const monthRowId = `month-${yearData.year}-${monthData.month}`;
          const monthRow = `
						<tr class="cwpm-month-row" data-month-id="${monthRowId}">
							<td colspan="6">
								<span class="cwpm-month-row__cell-inner">
									<span class="icon icon--xs cwpm-month-row__icon" aria-hidden="true">
										<i class="fa-solid fa-folder-open"></i>
									</span>
									${monthData.month}
								</span>
							</td>
							<td><label class="form__check"><input type="checkbox" class="month-select" data-month-id="${monthRowId}" /><span></span></label></td>
						</tr>
					`;
          const fileRows = monthData.files.map((f) => this.renderFileRow(f, monthRowId)).join("");
          return monthRow + fileRows;
        }).join("");
        const li = document.createElement("li");
        li.className = "accordion__item";
        li.innerHTML = `
				<details class="accordion__details">
					<summary class="accordion__summary">
						<span class="accordion__icon" aria-hidden="true">+</span>
						<span class="accordion__label">
							<span class="icon icon--xs" aria-hidden="true"><i class="fa-solid fa-folder"></i></span>
							${yearData.year}
							<span class="cwpm-year-meta">
								<span class="badge badge-sm badge-neutral">${totalFiles} files</span>
								<span class="badge badge-sm badge-neutral"><i class="fa-solid fa-hard-drive" aria-hidden="true"></i> ${yearData.totalSize}</span>
							</span>
						</span>
						<label class="form__check cwpm-year-select">
							<input type="checkbox" class="year-select" />
							<span></span>
						</label>
					</summary>
					<div class="accordion__panel accordion__panel--no-padding">
						<div class="cwpm-table-scroll">
						<table class="table table--heading-secondary cwpm-file-table">
							<thead>
								<tr>
									<th class="th-index"></th>
									<th class="th-file">File</th>
									<th>Status</th>
									<th>Sizes</th>
									<th class="th-size">Size</th>
									<th class="th-potential-saving">Potential Saving</th>
									<th class="th-select">Select</th>
								</tr>
							</thead>
							<tbody>
								${tbodyRows}
							</tbody>
						</table>
						</div>
					</div>
				</details>
			`;
        this.folderList.appendChild(li);
      });
      this.wireSelectAll();
      this.wireUsagePopups();
      this.wireSizesPopups();
    }
    /**
     * Render a single file row as an HTML string.
     *
     * Extracted from renderYearResults so both grouped and flat views share
     * identical row markup.
     *
     * @param {Object} f           File result object.
     * @param {string} monthRowId  Month identifier for grouped view; empty string for flat view.
     * @returns {string} HTML string for one <tr>.
     */
    renderFileRow(f, monthRowId = "") {
      const destructiveActions = ["delete-unused", "delete-missing", "delete-broken", "trash-file", "deleted"];
      if (f.action_applied && destructiveActions.includes(f.action_applied)) {
        return this.renderProcessedFileRow(f, monthRowId);
      }
      const statusBadges = [];
      if (f.broken) {
        statusBadges.push('<span class="badge badge-sm badge-error">Broken</span>');
      } else if (f.action_applied === "add-to-media") {
        statusBadges.push('<span class="badge badge-sm badge-success"><i class="fa-solid fa-circle-plus" style="margin-right:0.25em" aria-hidden="true"></i>Added to Media</span>');
      } else if (f.in_media) {
        statusBadges.push('<span class="badge badge-sm badge-success">In Media</span>');
      } else {
        statusBadges.push('<span class="badge badge-sm badge-warning">Not in Media</span>');
      }
      if (f.action_applied === "restored") {
        statusBadges.push('<span class="badge badge-sm badge-success"><i class="fa-solid fa-rotate-left" style="margin-right:0.25em" aria-hidden="true"></i>Restored</span>');
      }
      if (f.broken) {
      } else if (!f.in_media) {
      } else if (f.used && f.used_in && f.used_in.length) {
        const listId = `cwpm-usage-${f.rel_path.replace(/[^a-z0-9]/gi, "-")}`;
        const groupedTypes = ["acf", "elementor", "woocommerce"];
        const grouped = {};
        const individual = [];
        f.used_in.forEach((u) => {
          if (groupedTypes.includes(u.type)) {
            const key = u.title;
            if (!grouped[key]) grouped[key] = { ...u, contexts: [] };
            const ctxItems = Array.isArray(u.context) ? u.context : u.context ? [u.context] : [];
            ctxItems.forEach((c) => {
              if (c && !grouped[key].contexts.includes(c)) grouped[key].contexts.push(c);
            });
          } else {
            individual.push(u);
          }
        });
        const groupedItems = Object.values(grouped).map((u) => {
          const badges = u.contexts.map((c) => `<span class="badge badge-xs badge-neutral">${Helpers.escHtml(c)}</span>`).join(" ");
          return `<li><a href="${u.edit_url}" target="_blank" rel="noopener">${Helpers.escHtml(u.title)}</a>${badges ? " " + badges : ""}</li>`;
        });
        const individualItems = individual.map((u) => {
          const label = Helpers.escHtml(u.title);
          const typeBadge = u.type !== "post" ? ` <span class="badge badge-xs badge-neutral">${Helpers.escHtml(u.type)}</span>` : "";
          const ctxLabels = { featured: "featured", content: "", meta: "meta", widget: "widget", theme: "theme", block: "block" };
          const ctxArr = Array.isArray(u.context) ? u.context : u.context ? [u.context] : [];
          const ctx = ctxArr.map((c) => {
            const l = c in ctxLabels ? ctxLabels[c] : c;
            return l ? ` <span class="badge badge-xs badge-neutral">${Helpers.escHtml(l)}</span>` : "";
          }).join("");
          return `<li><a href="${u.edit_url}" target="_blank" rel="noopener">${label}</a>${typeBadge}${ctx}</li>`;
        });
        const listItems = [...groupedItems, ...individualItems].join("");
        const displayCount = Object.keys(grouped).length + individual.length;
        statusBadges.push(`<span class="cwpm-usage-wrap">
				<button type="button" class="badge badge-sm badge-success cwpm-usage-toggle" aria-expanded="false" aria-controls="${listId}">Used <i class="fa-solid fa-circle-info"></i></button>
				<div id="${listId}" class="cwpm-usage-popup" hidden>
					<div class="cwpm-usage-popup__header">Used in ${displayCount} place${displayCount > 1 ? "s" : ""}</div>
					<ul class="cwpm-usage-popup__list">${listItems}</ul>
				</div>
			</span>`);
      } else if (f.used) {
        statusBadges.push('<span class="badge badge-sm badge-success">Used</span>');
      } else {
        statusBadges.push('<span class="badge badge-sm badge-warning">Unused</span>');
      }
      let displaySizes = f.sizes || [];
      let displayOrphanThumbs = f.orphan_thumbs || [];
      if (f.action_applied === "regenerate-thumbnails") {
        displaySizes = displaySizes.map(
          (s) => s.status === "missing" ? { ...s, status: "regenerated" } : s
        );
      } else if (f.action_applied === "delete-orphan-thumbnails") {
        displaySizes = displaySizes.map(
          (s) => s.status === "pending" ? { ...s, status: "trashed_orphan" } : s
        );
        displayOrphanThumbs = [];
      }
      const sizes = displaySizes;
      const orphanThumbs = displayOrphanThumbs;
      const okCount = sizes.filter((s) => s.status === "ok").length;
      const missingCount = sizes.filter((s) => s.status === "missing").length;
      const pendingCount = sizes.filter((s) => s.status === "pending").length;
      const regeneratedCount = sizes.filter((s) => s.status === "regenerated").length;
      const trashedCount = sizes.filter((s) => s.status === "trashed_orphan").length;
      const sizesSummaryId = `cwpm-sizes-${f.rel_path.replace(/[^a-z0-9]/gi, "-")}`;
      const summaryParts = [];
      if (okCount > 0) {
        summaryParts.push(`<span class="cwpm-size-pill--ok" title="${okCount} size${okCount !== 1 ? "s" : ""} present and enabled">${okCount} <i class="fa-solid fa-check-circle" aria-hidden="true"></i></span>`);
      }
      if (missingCount > 0) {
        summaryParts.push(`<span class="cwpm-size-pill--missing" title="${missingCount} size${missingCount !== 1 ? "s" : ""} missing from disk \u2014 regenerate thumbnails to restore">${missingCount} <i class="fa-solid fa-xmark" aria-hidden="true"></i></span>`);
      }
      if (pendingCount > 0) {
        summaryParts.push(`<span class="cwpm-size-pill--pending" title="${pendingCount} orphan thumbnail${pendingCount !== 1 ? "s" : ""} \u2014 size is disabled but file still exists on disk">${pendingCount} <i class="fa-solid fa-triangle-exclamation" aria-hidden="true"></i></span>`);
      }
      if (regeneratedCount > 0) {
        summaryParts.push(`<span class="cwpm-size-pill--regenerated" title="${regeneratedCount} size${regeneratedCount !== 1 ? "s" : ""} regenerated">${regeneratedCount} <i class="fa-solid fa-wand-magic-sparkles" aria-hidden="true"></i></span>`);
      }
      if (trashedCount > 0) {
        summaryParts.push(`<span class="cwpm-size-pill--trashed" title="${trashedCount} orphan thumbnail${trashedCount !== 1 ? "s" : ""} moved to trash">${trashedCount} <i class="fa-solid fa-trash" aria-hidden="true"></i></span>`);
      }
      if (orphanThumbs.length > 0 && f.in_media) {
        summaryParts.push(`<span class="cwpm-size-pill--pending" title="${orphanThumbs.length} untracked thumbnail derivative${orphanThumbs.length !== 1 ? "s" : ""} on disk">${orphanThumbs.length} <i class="fa-solid fa-triangle-exclamation" aria-hidden="true"></i></span>`);
      }
      const sizeSummaryText = summaryParts.join(" ");
      const sizePillsHtml = sizes.map((size) => {
        let statusClass = "cwpm-size-pill--ok";
        let icon = '<i class="fa-solid fa-check-circle" aria-hidden="true"></i>';
        let title = `${size.label} \u2014 present and enabled`;
        if (size.status === "missing") {
          statusClass = "cwpm-size-pill--missing";
          icon = '<i class="fa-solid fa-xmark" aria-hidden="true"></i>';
          title = `${size.label} \u2014 missing from disk, regenerate thumbnails to restore`;
        } else if (size.status === "pending") {
          statusClass = "cwpm-size-pill--pending";
          icon = '<i class="fa-solid fa-triangle-exclamation" aria-hidden="true"></i>';
          title = `${size.label} \u2014 size is disabled but file still exists on disk (orphan thumbnail)`;
        } else if (size.status === "regenerated") {
          statusClass = "cwpm-size-pill--regenerated";
          icon = '<i class="fa-solid fa-wand-magic-sparkles" aria-hidden="true"></i>';
          title = `${size.label} \u2014 regenerated`;
        } else if (size.status === "trashed_orphan") {
          statusClass = "cwpm-size-pill--trashed";
          icon = '<i class="fa-solid fa-trash" aria-hidden="true"></i>';
          title = `${size.label} \u2014 moved to trash`;
        }
        return `<span class="cwpm-size-pill ${statusClass}" data-size-name="${size.name}" title="${title}">${icon} <span class="cwpm-size-pill__label">${size.label}</span></span>`;
      }).join("");
      const orphanThumbPillsHtml = orphanThumbs.map((t) => {
        const label = Helpers.escHtml(t.dims || t.file);
        return `<span class="cwpm-size-pill cwpm-size-pill--pending" data-size-name="${Helpers.escHtml(t.file)}" title="${Helpers.escHtml(t.file)} \u2014 orphan thumbnail derivative (${Helpers.escHtml(t.size_formatted)})"><i class="fa-solid fa-triangle-exclamation" aria-hidden="true"></i> <span class="cwpm-size-pill__label">${label}</span></span>`;
      }).join("");
      let sizesHtml;
      if (!f.in_media && orphanThumbs.length > 0) {
        const orphanSummaryPart = `<span class="cwpm-size-pill--pending" title="${orphanThumbs.length} orphan thumbnail derivative${orphanThumbs.length !== 1 ? "s" : ""}">${orphanThumbs.length} <i class="fa-solid fa-triangle-exclamation" aria-hidden="true"></i></span>`;
        sizesHtml = `<span class="cwpm-sizes-summary">
				<button type="button" class="cwpm-sizes-toggle" aria-expanded="false" aria-controls="${sizesSummaryId}">
					${orphanSummaryPart}
					<i class="fa-solid fa-chevron-down cwpm-sizes-toggle__icon" aria-hidden="true"></i>
				</button>
				<div id="${sizesSummaryId}" class="cwpm-sizes-popup" hidden>
					<div class="cwpm-sizes-popup__header">${orphanThumbs.length} orphan thumbnail derivative${orphanThumbs.length !== 1 ? "s" : ""}</div>
					<div class="cwpm-size-list" style="padding: 0.35rem 0.75rem;">${orphanThumbPillsHtml}</div>
				</div>
			</span>
			<div class="cwpm-size-list cwpm-size-list--inline">${orphanThumbPillsHtml}</div>`;
      } else {
        const allPillsHtml = sizePillsHtml + orphanThumbPillsHtml;
        const totalCount = sizes.length + orphanThumbs.length;
        sizesHtml = totalCount > 0 ? `<span class="cwpm-sizes-summary">
					<button type="button" class="cwpm-sizes-toggle" aria-expanded="false" aria-controls="${sizesSummaryId}">
						${sizeSummaryText}
						<i class="fa-solid fa-chevron-down cwpm-sizes-toggle__icon" aria-hidden="true"></i>
					</button>
					<div id="${sizesSummaryId}" class="cwpm-sizes-popup" hidden>
						<div class="cwpm-sizes-popup__header">${totalCount} thumbnail size${totalCount !== 1 ? "s" : ""}</div>
						<div class="cwpm-size-list" style="padding: 0.35rem 0.75rem;">${allPillsHtml}</div>
					</div>
				</span>
				<div class="cwpm-size-list cwpm-size-list--inline">${allPillsHtml}</div>` : "\u2014";
      }
      const thumbUrl = f.thumb_url || "";
      const thumbHtml = thumbUrl ? `<img src="${thumbUrl}" alt="${Helpers.escHtml(f.name)}" class="cwpm-file-thumb__img" loading="lazy" />` : `<div class="cwpm-file-thumb__placeholder" aria-hidden="true"></div>`;
      const fileStatus = f.broken ? "broken" : !f.in_media ? "missing" : !f.used ? "unused" : "used";
      const hasMissingThumbs = missingCount > 0 ? "true" : "false";
      const hasOrphanThumbs = pendingCount > 0 || orphanThumbs.length > 0 ? "true" : "false";
      const monthIdAttr = monthRowId ? ` data-month-id="${monthRowId}"` : "";
      const checkedAttr = this._selectAllActive && !this._excludedRelPaths.has(f.rel_path) ? " checked" : "";
      return `
			<tr${monthIdAttr} data-file-row="true" data-file-name="${Helpers.escHtml(f.name.toLowerCase())}" data-file-status="${fileStatus}" data-missing-thumbs="${hasMissingThumbs}" data-orphan-thumbs="${hasOrphanThumbs}" data-rel-path="${Helpers.escHtml(f.rel_path)}" data-attachment-id="${f.attachment_id || 0}">
				<td><span class="cwpm-row-status"></span></td>
				<td>
					<div class="cwpm-file-cell">
						<div class="cwpm-file-thumb">
							${thumbHtml}
						</div>
						${f.edit_url ? `<a href="${f.edit_url}" target="_blank" rel="noopener" class="cwpm-media-link" title="Edit in Media Library"><strong>${Helpers.escHtml(f.name)}</strong> <i class="fa-solid fa-arrow-up-right-from-square"></i></a>` : `<strong>${Helpers.escHtml(f.name)}</strong>`}
					</div>
				</td>
				<td><div class="cwpm-status-cell">${statusBadges.join("")}</div></td>
				<td>${sizesHtml}</td>
				<td class="text-muted">${f.size_formatted || "\u2014"}</td>
				<td class="text-muted">${f.potential_saving || "\u2014"}</td>
				<td><label class="form__check"><input type="checkbox" class="row-select"${monthIdAttr}${checkedAttr} /><span></span></label></td>
			</tr>
		`;
    }
    /**
     * Render a file row that has already been processed by a clean action.
     *
     * Shows a checkmark status, the file name, and an action badge instead
     * of the normal interactive row. No checkbox is rendered.
     *
     * @param {Object} f           File result object with action_applied set.
     * @param {string} monthRowId  Month identifier for grouped view.
     * @returns {string} HTML string for one <tr>.
     */
    renderProcessedFileRow(f, monthRowId = "") {
      const actionBadges = {
        "delete-unused": '<span class="badge badge-sm badge-neutral"><i class="fa-solid fa-trash" style="margin-right:0.25em" aria-hidden="true"></i>Trashed</span>',
        "trash-file": '<span class="badge badge-sm badge-neutral"><i class="fa-solid fa-trash" style="margin-right:0.25em" aria-hidden="true"></i>Trashed</span>',
        "delete-missing": '<span class="badge badge-sm badge-neutral"><i class="fa-solid fa-trash" style="margin-right:0.25em" aria-hidden="true"></i>Trashed</span>',
        "delete-broken": '<span class="badge badge-sm badge-neutral"><i class="fa-solid fa-trash" style="margin-right:0.25em" aria-hidden="true"></i>Trashed</span>',
        "restored": '<span class="badge badge-sm badge-success"><i class="fa-solid fa-rotate-left" style="margin-right:0.25em" aria-hidden="true"></i>Restored</span>',
        "deleted": '<span class="badge badge-sm badge-error"><i class="fa-solid fa-circle-xmark" style="margin-right:0.25em" aria-hidden="true"></i>Deleted</span>',
        "add-to-media": '<span class="badge badge-sm badge-success"><i class="fa-solid fa-circle-plus" style="margin-right:0.25em" aria-hidden="true"></i>Added to Media</span>',
        "regenerate-thumbnails": '<span class="badge badge-sm badge-info"><i class="fa-solid fa-wand-magic-sparkles" style="margin-right:0.25em" aria-hidden="true"></i>Regenerated</span>',
        "delete-orphan-thumbnails": '<span class="badge badge-sm badge-warning"><i class="fa-solid fa-trash" style="margin-right:0.25em" aria-hidden="true"></i>Cleaned</span>'
      };
      const badge = actionBadges[f.action_applied] || `<span class="badge badge-sm badge-neutral">${Helpers.escHtml(f.action_applied)}</span>`;
      const isDestructive = ["delete-unused", "trash-file", "delete-missing", "delete-broken"].includes(f.action_applied);
      const thumbUrl = !isDestructive && f.thumb_url ? f.thumb_url : "";
      const thumbHtml = thumbUrl ? `<img src="${thumbUrl}" alt="${Helpers.escHtml(f.name)}" class="cwpm-file-thumb__img" loading="lazy" />` : `<div class="cwpm-file-thumb__placeholder" aria-hidden="true"></div>`;
      const monthIdAttr = monthRowId ? ` data-month-id="${monthRowId}"` : "";
      return `
			<tr${monthIdAttr} data-file-row="true" data-file-name="${Helpers.escHtml(f.name.toLowerCase())}" data-rel-path="${Helpers.escHtml(f.rel_path)}" data-attachment-id="${f.attachment_id || 0}">
				<td><span class="cwpm-row-status"><i class="fa-solid fa-circle-check"></i></span></td>
				<td>
					<div class="cwpm-file-cell">
						<div class="cwpm-file-thumb">
							${thumbHtml}
						</div>
						<strong>${Helpers.escHtml(f.name)}</strong>
					</div>
				</td>
				<td><div class="cwpm-status-cell">${badge}</div></td>
				<td class="text-muted">\u2014</td>
				<td class="text-muted">${f.size_formatted || "\u2014"}</td>
				<td class="text-muted">\u2014</td>
				<td></td>
			</tr>
		`;
    }
    /**
     * Render results as a single flat table (no year accordions, no month sub-headers).
     *
     * @param {Object[]} results  Flat array of file result objects.
     */
    renderFlatResults(results) {
      if (!this.folderList) return;
      this.folderList.innerHTML = "";
      if (results.length === 0) {
        this.folderList.innerHTML = '<li class="cwpm-no-results"><p class="text-muted">No results found.</p></li>';
        return;
      }
      const tbodyRows = results.map((f) => this.renderFileRow(f)).join("");
      const li = document.createElement("li");
      li.innerHTML = `
			<div class="cwpm-table-scroll">
				<table class="table table--heading-secondary cwpm-file-table">
					<thead>
						<tr>
							<th class="th-index"></th>
							<th class="th-file">File</th>
							<th>Status</th>
							<th>Sizes</th>
							<th class="th-size">Size</th>
							<th class="th-potential-saving">Potential Saving</th>
							<th class="th-select">Select</th>
						</tr>
					</thead>
					<tbody>
						${tbodyRows}
					</tbody>
				</table>
			</div>
		`;
      this.folderList.appendChild(li);
      this.wireSelectAll();
      this.wireUsagePopups();
      this.wireSizesPopups();
    }
    /**
     * Load a page of scan results from the DB and render them.
     *
     * @param {Object} params  Optional overrides for page, per_page, status_filter, search.
     */
    async loadResults(params = {}) {
      const defaults = {
        page: this._currentPage || 1,
        per_page: this._perPage || 200,
        status_filter: this._statusFilter || "",
        search: this._searchQuery || ""
      };
      const query = { ...defaults, ...params };
      try {
        const data = await this.ajaxClient.post("cwpm_scan_results_page", query);
        this.scanResults = data.results;
        this._currentPage = data.page;
        this._perPage = data.per_page;
        this._totalResults = data.total;
        this._totalPages = data.total_pages;
        if (this._groupByMonth) {
          const grouped = this.groupByYearMonth(data.results);
          this.renderYearResults(grouped);
        } else {
          this.renderFlatResults(data.results);
        }
        this.updatePaginationUI();
      } catch (err) {
        console.error("Failed to load results:", err);
      }
    }
    /**
     * Load stats for the full scan (no filters, no pagination).
     */
    async loadStats() {
      try {
        const data = await this.ajaxClient.post("cwpm_scan_results_page", {
          per_page: 0,
          status_filter: "",
          search: ""
        });
        this.populateStats(data.results);
      } catch (err) {
        console.error("Failed to load stats:", err);
      }
    }
    /**
     * Update the pagination controls based on current state.
     */
    updatePaginationUI() {
      const infoEl = document.getElementById("cwpm-pagination-info");
      const prevBtn = document.getElementById("cwpm-page-prev");
      const nextBtn = document.getElementById("cwpm-page-next");
      if (infoEl) {
        if (this._perPage === 0) {
          infoEl.textContent = `1\u2013${this._totalResults} of ${this._totalResults}`;
        } else {
          const start = this._totalResults > 0 ? (this._currentPage - 1) * this._perPage + 1 : 0;
          const end = Math.min(this._currentPage * this._perPage, this._totalResults);
          infoEl.textContent = `${start}\u2013${end} of ${this._totalResults}`;
        }
      }
      if (prevBtn) prevBtn.disabled = this._currentPage <= 1;
      if (nextBtn) nextBtn.disabled = this._currentPage >= this._totalPages;
    }
    // ── Clean (still dummy for now) ─────────────────────────
    onStartCleanClick() {
      var _a;
      const anySelected = [...document.querySelectorAll("#cwpm-folder-list .row-select")].some((cb) => cb.checked);
      if (!anySelected) {
        Helpers.setText("cwpm-nothing-to-clean-message", "Select at least one file row before cleaning.");
        if (typeof window.Modal !== "undefined") {
          window.Modal.open("cwpm-nothing-to-clean-modal");
        }
        return;
      }
      const selectedActionsStr = ((_a = document.getElementById("cwpm-selected-actions")) == null ? void 0 : _a.value) || "";
      const selectedActions = selectedActionsStr ? selectedActionsStr.split(",").filter(Boolean) : [];
      const targets = this.buildCleanTargets(selectedActions);
      const totalItems = Object.values(targets).reduce((sum, arr) => sum + arr.length, 0);
      if (totalItems === 0) {
        Helpers.setText("cwpm-nothing-to-clean-message", "None of the selected files match the chosen actions. Try selecting different files or changing the active actions.");
        if (typeof window.Modal !== "undefined") {
          window.Modal.open("cwpm-nothing-to-clean-modal");
        }
        return;
      }
      if (!this.disclaimerShown) {
        this.disclaimerShown = true;
        if (typeof window.Modal !== "undefined") {
          if (this.disclaimerCheckbox) {
            this.disclaimerCheckbox.checked = false;
          }
          if (this.disclaimerProceedBtn) {
            this.disclaimerProceedBtn.disabled = true;
          }
          window.Modal.open("cwpm-disclaimer-modal");
        } else {
          this.runClean();
        }
      } else {
        this.runClean();
      }
    }
    async runClean() {
      var _a, _b;
      if (typeof window.Modal !== "undefined") {
        window.Modal.close("cwpm-disclaimer-modal");
      }
      this.cleanBtn.disabled = true;
      const row1 = document.getElementById("cwpm-action-bar-row1");
      const row2 = document.getElementById("cwpm-action-bar-row2");
      const barCleanProgress = document.getElementById("cwpm-bar-clean-progress");
      const barCleanProgressBar = document.getElementById("cwpm-bar-clean-progress-bar");
      const barCleanStatus = document.getElementById("cwpm-bar-clean-status");
      const barCleanFile = document.getElementById("cwpm-bar-clean-file");
      if (row1) row1.style.display = "none";
      if (row2) row2.style.display = "none";
      if (barCleanProgress) barCleanProgress.style.display = "";
      await Helpers.delay(50);
      const selectedActionsStr = ((_a = document.getElementById("cwpm-selected-actions")) == null ? void 0 : _a.value) || "";
      const selectedActions = selectedActionsStr ? selectedActionsStr.split(",").filter(Boolean) : [];
      const targets = this.buildCleanTargets(selectedActions);
      const totalItems = Object.values(targets).reduce((sum, arr) => sum + arr.length, 0);
      Object.values(targets).flat().forEach((f) => {
        const row = document.querySelector(`#cwpm-folder-list tr[data-rel-path="${CSS.escape(f.rel_path)}"]`);
        if (!row) return;
        const details = row.closest(".accordion__details");
        if (details && !details.open) details.open = true;
        if (row.style.display === "none") row.style.display = "";
      });
      let processed = 0;
      const cleanBatchSize = 10;
      const actionStats = {};
      const actionLabels = {
        "regenerate-thumbnails": "Generating thumbnails",
        "delete-orphan-thumbnails": "Cleaning orphan thumbnails",
        "add-to-media": "Adding to Media Library",
        "delete-broken": "Deleting broken records"
      };
      for (const action of selectedActions) {
        const actionTargets = targets[action] || [];
        if (actionTargets.length === 0) continue;
        if (!actionStats[action]) {
          actionStats[action] = { count: 0, bytes: 0 };
        }
        const label = actionLabels[action] || "Cleaning";
        for (let i = 0; i < actionTargets.length; i += cleanBatchSize) {
          const batch = actionTargets.slice(i, i + cleanBatchSize);
          const firstName = ((_b = batch[0]) == null ? void 0 : _b.name) || "";
          if (barCleanFile) barCleanFile.textContent = firstName;
          batch.forEach((f) => this.setRowStatus(f.rel_path, "loading"));
          const batchResult = await this.processActionBatch(action, batch);
          batch.forEach((f) => {
            var _a2;
            const s = (_a2 = batchResult.succeeded) != null ? _a2 : [];
            const succeeded = s.includes(f.rel_path) || f.in_media && (s.includes(f.attachment_id) || s.includes(String(f.attachment_id)));
            this.setRowStatus(f.rel_path, succeeded ? "done" : "error");
            if (succeeded) {
              this.updateRowSizePills(f.rel_path, action);
            }
          });
          actionStats[action].count += batchResult.actionCount || 0;
          actionStats[action].bytes += batchResult.actionBytes || 0;
          processed += batch.length;
          const pct = totalItems > 0 ? Math.round(processed / totalItems * 100) : 100;
          if (barCleanProgressBar) barCleanProgressBar.style.width = pct + "%";
          if (barCleanStatus) barCleanStatus.textContent = `${label}... ${pct}%`;
        }
      }
      if (barCleanProgress) barCleanProgress.style.display = "none";
      if (row1) row1.style.display = "";
      if (row2) row2.style.display = "";
      this.populateCleanDoneModal(actionStats);
      if (typeof window.Modal !== "undefined") {
        window.Modal.open("cwpm-clean-done-modal");
      }
      if (this.wizard) {
        this.wizard.markCompleted(3);
      }
    }
    /**
     * Build target file lists from scanResults for each selected action.
     *
     * @param {string[]} actions
     * @returns {Object} Map of action => array of scan result objects.
     */
    buildCleanTargets(actions) {
      const targets = {};
      let sourceResults;
      if (this._selectAllActive && this._allResultsForClean.length > 0) {
        sourceResults = this._allResultsForClean.filter(
          (f) => !this._excludedRelPaths.has(f.rel_path)
        );
      } else {
        sourceResults = this.scanResults.filter((f) => {
          var _a, _b;
          const row = document.querySelector(
            `#cwpm-folder-list tr[data-rel-path="${CSS.escape(f.rel_path)}"]`
          );
          return (_b = (_a = row == null ? void 0 : row.querySelector(".row-select")) == null ? void 0 : _a.checked) != null ? _b : false;
        });
      }
      for (const action of actions) {
        switch (action) {
          case "regenerate-thumbnails":
            targets[action] = sourceResults.filter(
              (f) => f.in_media && f.attachment_id && (f.sizes || []).some((s) => s.status === "missing")
            );
            break;
          case "delete-unused":
            targets[action] = sourceResults.filter(
              (f) => !f.broken && (f.in_media && f.attachment_id && !f.used || // in-media unused
              !f.in_media)
            );
            break;
          case "trash-file":
            targets[action] = sourceResults.slice();
            break;
          case "delete-missing":
            targets[action] = sourceResults.filter(
              (f) => !f.in_media && !f.broken
            );
            break;
          case "add-to-media":
            targets[action] = sourceResults.filter(
              (f) => !f.in_media && !f.broken
            );
            break;
          case "delete-broken":
            targets[action] = sourceResults.filter(
              (f) => f.broken && f.attachment_id
            );
            break;
          case "delete-orphan-thumbnails":
            targets[action] = sourceResults.filter(
              (f) => (
                // Registered attachment with disabled (pending) thumbnail sizes.
                f.in_media && f.attachment_id && (f.sizes || []).some((s) => s.status === "pending" && s.name) || // File with untracked thumbnail derivatives on disk.
                (f.orphan_thumbs || []).length > 0
              )
            );
            break;
          default:
            targets[action] = [];
        }
      }
      const trashedPaths = /* @__PURE__ */ new Set([
        ...(targets["delete-unused"] || []).map((f) => f.rel_path),
        ...(targets["trash-file"] || []).map((f) => f.rel_path)
      ]);
      if (trashedPaths.size > 0) {
        for (const thumbAction of ["regenerate-thumbnails", "delete-orphan-thumbnails"]) {
          if (targets[thumbAction]) {
            targets[thumbAction] = targets[thumbAction].filter(
              (f) => !trashedPaths.has(f.rel_path)
            );
          }
        }
      }
      return targets;
    }
    /**
     * Send one batch of items to the backend for a given action.
     *
     * Returns an object with a `succeeded` array of attachment IDs that were
     * processed successfully (used to update row status).
     *
     * @param {string} action
     * @param {Object[]} items  Scan result objects.
     * @returns {Promise<{succeeded: number[]}>}
     */
    async processActionBatch(action, items) {
      switch (action) {
        case "trash-file":
        case "delete-unused": {
          const succeeded = [];
          let actionCount = 0;
          let actionBytes = 0;
          for (const f of items) {
            try {
              if (f.attachment_id) {
                await this.ajaxClient.post("cwpm_delete_unused", { attachment_id: f.attachment_id });
                succeeded.push(f.attachment_id);
                const thumbCount = (f.sizes || []).filter((s) => s.status === "ok").length;
                actionCount += 1 + thumbCount;
              } else {
                await this.ajaxClient.post("cwpm_delete_missing", { rel_path: f.rel_path });
                succeeded.push(f.rel_path);
                actionCount += 1;
              }
              actionBytes += f.size || 0;
            } catch (err) {
              console.error("Delete unused failed for", f.attachment_id || f.rel_path, err);
            }
          }
          return { succeeded, actionCount, actionBytes };
        }
        case "delete-missing": {
          const succeeded = [];
          let actionCount = 0;
          let actionBytes = 0;
          for (const f of items) {
            if (!f.rel_path) continue;
            try {
              await this.ajaxClient.post("cwpm_delete_missing", { rel_path: f.rel_path });
              succeeded.push(f.rel_path);
              actionCount += 1;
              actionBytes += f.size || 0;
            } catch (err) {
              console.error("Delete missing failed for", f.rel_path, err);
            }
          }
          return { succeeded, actionCount, actionBytes };
        }
        case "add-to-media": {
          const succeeded = [];
          let actionCount = 0;
          let actionBytes = 0;
          for (const f of items) {
            if (!f.rel_path) continue;
            try {
              await this.ajaxClient.post("cwpm_add_to_media", { rel_path: f.rel_path });
              succeeded.push(f.rel_path);
              actionCount += 1;
              actionBytes += f.size || 0;
            } catch (err) {
              console.error("Add to media failed for", f.rel_path, err);
            }
          }
          return { succeeded, actionCount, actionBytes };
        }
        case "delete-broken": {
          const succeeded = [];
          let actionCount = 0;
          let actionBytes = 0;
          for (const f of items) {
            if (!f.attachment_id) continue;
            try {
              await this.ajaxClient.post("cwpm_delete_broken", { attachment_id: f.attachment_id });
              succeeded.push(f.attachment_id);
              actionCount += 1;
              actionBytes += f.size || 0;
            } catch (err) {
              console.error("Delete broken failed for attachment", f.attachment_id, err);
            }
          }
          return { succeeded, actionCount, actionBytes };
        }
        case "regenerate-thumbnails": {
          const ids = items.map((f) => f.attachment_id).filter(Boolean);
          if (ids.length === 0) return { succeeded: [], actionCount: 0, actionBytes: 0 };
          try {
            const response = await this.ajaxClient.post("cwpm_regenerate_thumbnails", {
              attachment_ids: ids.join(",")
            });
            const succeededResults = (response.results || []).filter((r) => r.success);
            const succeeded = succeededResults.map((r) => r.id);
            const actionCount = items.filter((f) => succeeded.includes(f.attachment_id)).reduce((sum, f) => sum + (f.sizes || []).filter((s) => s.status === "missing").length, 0);
            const actionBytes = succeededResults.reduce((sum, r) => sum + (r.size_bytes || 0), 0);
            return { succeeded, actionCount, actionBytes };
          } catch (err) {
            console.error("Regenerate thumbnails batch failed:", err);
            return { succeeded: [], actionCount: 0, actionBytes: 0 };
          }
        }
        case "delete-orphan-thumbnails": {
          const inMediaItems = items.filter((f) => f.in_media && f.attachment_id);
          const orphanFileItems = items.filter((f) => (f.orphan_thumbs || []).length > 0);
          let succeeded = [];
          let actionCount = 0;
          let actionBytes = 0;
          const payload = inMediaItems.map((f) => ({
            id: f.attachment_id,
            sizes: (f.sizes || []).filter((s) => s.status === "pending" && s.name).map((s) => s.name)
          })).filter((i) => i.sizes.length > 0);
          if (payload.length > 0) {
            try {
              const response = await this.ajaxClient.post("cwpm_delete_orphan_thumbnails", {
                items: JSON.stringify(payload)
              });
              const ok = [...new Set(
                (response.results || []).filter((r) => r.success).map((r) => r.id)
              )];
              succeeded = succeeded.concat(ok.map(String));
              actionCount += (response.results || []).filter((r) => r.success).length;
              actionBytes += inMediaItems.filter((f) => ok.includes(f.attachment_id)).reduce((sum, f) => sum + (f.sizes || []).filter((s) => s.status === "pending").reduce((s2, sz) => s2 + (sz.file_size || 0), 0), 0);
            } catch (err) {
              console.error("Delete orphan thumbnails (attachment) batch failed:", err);
            }
          }
          if (orphanFileItems.length > 0) {
            const relPaths = orphanFileItems.flatMap(
              (f) => (f.orphan_thumbs || []).map((t) => t.rel_path)
            );
            try {
              const response = await this.ajaxClient.post("cwpm_delete_orphan_file_thumbs", {
                rel_paths: JSON.stringify(relPaths)
              });
              const okPaths = new Set(
                (response.results || []).filter((r) => r.success).map((r) => r.rel_path)
              );
              const okParents = orphanFileItems.filter((f) => (f.orphan_thumbs || []).some((t) => okPaths.has(t.rel_path))).map((f) => f.rel_path);
              succeeded = succeeded.concat(okParents);
              actionCount += okPaths.size;
              actionBytes += orphanFileItems.filter((f) => okParents.includes(f.rel_path)).reduce((sum, f) => sum + (f.orphan_thumbs || []).filter((t) => okPaths.has(t.rel_path)).reduce((s2, t) => s2 + (t.size || 0), 0), 0);
            } catch (err) {
              console.error("Delete orphan file thumbs batch failed:", err);
            }
          }
          return { succeeded, actionCount, actionBytes };
        }
        default:
          return { succeeded: items.map((f) => f.attachment_id).filter(Boolean), actionCount: 0, actionBytes: 0 };
      }
    }
    /**
     * Update the status icon in a file row.
     *
     * @param {string} relPath  The file's rel_path (used as the row selector key).
     * @param {'loading'|'done'|'error'} state
     */
    setRowStatus(relPath, state) {
      var _a;
      const row = document.querySelector(`#cwpm-folder-list tr[data-rel-path="${CSS.escape(relPath)}"]`);
      if (!row) return;
      const cell = row.querySelector(".cwpm-row-status");
      if (!cell) return;
      if (state === "loading") {
        const loaderUrl = (((_a = window.CWPMConfig) == null ? void 0 : _a.pluginUrl) || "") + "assets/vendor/color-punch/img/loader.svg";
        cell.innerHTML = `<img src="${loaderUrl}" class="loader" width="20" height="20" alt="" />`;
      } else if (state === "done") {
        cell.innerHTML = '<i class="fa-solid fa-circle-check"></i>';
      } else {
        cell.innerHTML = '<i class="fa-solid fa-circle-xmark"></i>';
      }
    }
    







    updateRowSizePills(relPath, action) {
      var _a, _b;
      const row = document.querySelector(`#cwpm-folder-list tr[data-rel-path="${CSS.escape(relPath)}"]`);
      if (!row) return;
      if (action === "delete-unused" || action === "trash-file") {
        const statusCell = row.querySelector(".cwpm-status-cell");
        const cells = row.querySelectorAll("td");
        if (statusCell) statusCell.innerHTML = '<span class="badge badge-sm badge-neutral"><i class="fa-solid fa-trash" style="margin-right:0.25em" aria-hidden="true"></i>Trashed</span>';
        if (cells[3]) cells[3].innerHTML = "\u2014";
        const link = (_a = cells[1]) == null ? void 0 : _a.querySelector(".cwpm-media-link");
        if (link) {
          const strong = link.querySelector("strong");
          link.replaceWith(strong || document.createTextNode(link.textContent));
        }
      } else if (action === "delete-broken") {
        const statusCell = row.querySelector(".cwpm-status-cell");
        const cells = row.querySelectorAll("td");
        if (statusCell) statusCell.innerHTML = '<span class="badge badge-sm badge-neutral"><i class="fa-solid fa-trash" style="margin-right:0.25em" aria-hidden="true"></i>Trashed</span>';
        if (cells[3]) cells[3].innerHTML = "\u2014";
        const link = (_b = cells[1]) == null ? void 0 : _b.querySelector(".cwpm-media-link");
        if (link) {
          const strong = link.querySelector("strong");
          link.replaceWith(strong || document.createTextNode(link.textContent));
        }
      } else if (action === "delete-missing") {
        const statusCell = row.querySelector(".cwpm-status-cell");
        const cells = row.querySelectorAll("td");
        if (statusCell) statusCell.innerHTML = '<span class="badge badge-sm badge-neutral"><i class="fa-solid fa-trash" style="margin-right:0.25em" aria-hidden="true"></i>Trashed</span>';
        if (cells[3]) cells[3].innerHTML = "\u2014";
      } else if (action === "delete-duplicate") {
        const statusCell = row.querySelector(".cwpm-status-cell");
        const cells = row.querySelectorAll("td");
        if (statusCell) statusCell.innerHTML = '<span class="badge badge-sm badge-neutral"><i class="fa-solid fa-trash" style="margin-right:0.25em" aria-hidden="true"></i>Trashed</span>';
        if (cells[3]) cells[3].innerHTML = "\u2014";
      } else if (action === "add-to-media") {
        const statusCell = row.querySelector(".cwpm-status-cell");
        const cells = row.querySelectorAll("td");
        if (statusCell) statusCell.innerHTML = '<span class="badge badge-sm badge-success"><i class="fa-solid fa-circle-plus" style="margin-right:0.25em" aria-hidden="true"></i>Added to Media</span>';
        if (cells[3]) cells[3].innerHTML = "\u2014";
      } else if (action === "regenerate-thumbnails") {
        row.querySelectorAll(".cwpm-size-pill--missing").forEach((pill) => {
          var _a2;
          pill.classList.replace("cwpm-size-pill--missing", "cwpm-size-pill--regenerated");
          const label = ((_a2 = pill.querySelector(".cwpm-size-pill__label")) == null ? void 0 : _a2.textContent) || "Thumbnail";
          pill.title = `${label} \u2014 regenerated`;
          const icon = pill.querySelector("i");
          if (icon) icon.className = "fa-solid fa-wand-magic-sparkles";
        });
      } else if (action === "delete-orphan-thumbnails") {
        row.querySelectorAll(".cwpm-size-pill--pending").forEach((pill) => {
          var _a2;
          pill.classList.replace("cwpm-size-pill--pending", "cwpm-size-pill--trashed");
          const label = ((_a2 = pill.querySelector(".cwpm-size-pill__label")) == null ? void 0 : _a2.textContent) || "Thumbnail";
          pill.title = `${label} \u2014 moved to trash`;
          const icon = pill.querySelector("i");
          if (icon) icon.className = "fa-solid fa-trash";
        });
      }
    }
    




    populateCleanDoneModal(actionStats) {
      const container = document.getElementById("cwpm-clean-action-results");
      if (!container) return;
      const meta = {
        "delete-unused": {
          icon: "fa-solid fa-trash",
          iconColor: "text-error",
          label: "Files trashed",
          bytesLabel: "Space freed",
          bytesSign: "-"
        },
        "trash-file": {
          icon: "fa-solid fa-trash",
          iconColor: "text-error",
          label: "Files trashed",
          bytesLabel: "Space freed",
          bytesSign: "-"
        },
        "delete-broken": {
          icon: "fa-solid fa-circle-minus",
          iconColor: "text-error",
          label: "Records deleted",
          bytesLabel: "Space freed",
          bytesSign: "-"
        },
        "regenerate-thumbnails": {
          icon: "fa-solid fa-wand-magic-sparkles",
          iconColor: "text-info",
          label: "Thumbnails generated",
          bytesLabel: "Data gained",
          bytesSign: "+"
        },
        "delete-orphan-thumbnails": {
          icon: "fa-solid fa-trash",
          iconColor: "text-warning",
          label: "Thumbnails trashed",
          bytesLabel: "Space freed",
          bytesSign: "-"
        },
        "add-to-media": {
          icon: "fa-solid fa-circle-plus",
          iconColor: "text-success",
          label: "Files added to Media Library",
          bytesLabel: "Data imported",
          bytesSign: "+"
        }
      };
      const sections = [];
      let totalBytesGained = 0;
      let totalBytesFreed = 0;
      for (const [action, stats] of Object.entries(actionStats)) {
        const m = meta[action] || { icon: "fa-solid fa-circle-check", iconColor: "", label: "Items processed", bytesLabel: "Space", bytesSign: "" };
        if (m.bytesSign === "+") totalBytesGained += stats.bytes;
        else totalBytesFreed += stats.bytes;
        let mathLine = "";
        if (stats.bytes > 0 && stats.count > 0) {
          const avg = stats.bytes / stats.count;
          mathLine = `<span class="cwpm-stat-card__math">${stats.count} \xD7 avg ${Helpers.formatSize(avg)} = ${Helpers.formatSize(stats.bytes)}</span>`;
        }
        sections.push(`
				<div class="cwpm-stat-card">
					<span class="cwpm-stat-card__icon icon ${m.iconColor}" aria-hidden="true"><i class="${m.icon}"></i></span>
					<div class="cwpm-stat-card__body">
						<span class="cwpm-stat-card__value">${stats.count}</span>
						<span class="cwpm-stat-card__label">${m.label}</span>
						${mathLine}
					</div>
				</div>`);
      }
      if (sections.length === 0) {
        container.innerHTML = '<p class="text-muted">No items were processed.</p>';
        return;
      }
      const tallyParts = [];
      if (totalBytesGained > 0) tallyParts.push(`<span class="cwpm-tally__gained">+${Helpers.formatSize(totalBytesGained)} gained</span>`);
      if (totalBytesFreed > 0) tallyParts.push(`<span class="cwpm-tally__freed">\u2212${Helpers.formatSize(totalBytesFreed)} freed</span>`);
      const tallyHtml = tallyParts.length > 0 ? `<div class="cwpm-tally">${tallyParts.join('<span class="cwpm-tally__sep"> \xB7 </span>')}</div>` : "";
      container.innerHTML = `<div class="cwpm-stats-grid">${sections.join("")}</div>${tallyHtml}`;
    }
    // ── Shared UI helpers (same as DummyScan) ───────────────
    wireActionButtons() {
      const trigger = document.getElementById("cwpm-actions-trigger");
      const selectedInput = document.getElementById("cwpm-selected-actions");
      const countBadge = document.getElementById("cwpm-actions-count");
      const cards = document.querySelectorAll("#cwpm-actions-modal .cwpm-action-card");
      const descEl = document.getElementById("cwpm-action-cards-desc");
      const defaultDesc = (descEl == null ? void 0 : descEl.textContent) || "";
      const actionMeta = {
        "regenerate-thumbnails": { label: "Regenerate Thumbnails", color: "var(--blue)" },
        "delete-orphan-thumbnails": { label: "Delete Orphan Thumbnails", color: "var(--amber)" },
        "delete-unused": { label: "Delete Unused Files", color: "var(--green)" },
        "trash-file": { label: "Trash File", color: "var(--red)" },
        "delete-broken": { label: "Delete Broken Entries", color: "var(--purple)" },
        "delete-missing": { label: "Delete Missing in Media Library", color: "var(--red)" },
        "add-to-media": { label: "Add to Media Library", color: "var(--teal)" }
      };
      const mutualExclusive = { "add-to-media": "delete-missing", "delete-missing": "add-to-media" };
      const selected =  new Set();
      let tooltip = null;
      if (trigger) {
        tooltip = Helpers.createActionsTooltip(
          trigger,
          () => [...selected].map((action) => {
            const meta = actionMeta[action] || { label: action, color: "var(--gray)" };
            return { action, label: meta.label, color: meta.color };
          })
        );
      }
      const modalTitle = document.getElementById("cwpm-actions-modal-title");
      const syncActions = () => {
        if (selectedInput) selectedInput.value = [...selected].join(",");
        if (countBadge) {
          countBadge.textContent = selected.size;
          countBadge.hidden = selected.size === 0;
        }
        if (modalTitle) {
          modalTitle.textContent = selected.size > 0 ? `Selected Actions (${selected.size})` : "Select Actions";
        }
      };
      if (trigger) {
        trigger.addEventListener("click", () => {
          if (tooltip) tooltip.hidden = true;
          if (typeof window.Modal !== "undefined") {
            window.Modal.open("cwpm-actions-modal");
          }
        });
      }
      cards.forEach((card) => {
        const action = card.dataset.action;
        const desc = card.dataset.desc || "";
        card.addEventListener("click", () => {
          if (selected.has(action)) {
            selected.delete(action);
            card.classList.remove("cwpm-action-card--selected");
          } else {
            if (action === "trash-file") {
              selected.forEach((a) => {
                const c = document.querySelector(`.cwpm-action-card[data-action="${a}"]`);
                if (c) c.classList.remove("cwpm-action-card--selected");
              });
              selected.clear();
            } else {
              if (selected.has("trash-file")) {
                selected.delete("trash-file");
                const trashCard = document.querySelector('.cwpm-action-card[data-action="trash-file"]');
                if (trashCard) trashCard.classList.remove("cwpm-action-card--selected");
              }
              if (mutualExclusive[action]) {
                const opposite = mutualExclusive[action];
                selected.delete(opposite);
                const oppositeCard = document.querySelector(`.cwpm-action-card[data-action="${opposite}"]`);
                if (oppositeCard) oppositeCard.classList.remove("cwpm-action-card--selected");
              }
            }
            selected.add(action);
            card.classList.add("cwpm-action-card--selected");
          }
          syncActions();
        });
        card.addEventListener("mouseenter", () => {
          if (descEl) descEl.textContent = desc;
        });
        card.addEventListener("mouseleave", () => {
          if (descEl) descEl.textContent = defaultDesc;
        });
      });
      ["regenerate-thumbnails", "delete-orphan-thumbnails"].forEach((action) => {
        selected.add(action);
        const card = document.querySelector(`.cwpm-action-card[data-action="${action}"]`);
        if (card) card.classList.add("cwpm-action-card--selected");
      });
      syncActions();
      const barClean = document.getElementById("cwpm-bar-clean");
      if (barClean) {
        barClean.addEventListener("click", () => this.onStartCleanClick());
      }
    }
    


    wireFilters() {
      var _a;
      const searchInput = document.getElementById("cwpm-search-filter");
      const statusInput = document.getElementById("cwpm-status-filter");
      let searchTimeout;
      if (searchInput) {
        searchInput.addEventListener("input", () => {
          clearTimeout(searchTimeout);
          searchTimeout = setTimeout(() => {
            this._searchQuery = searchInput.value.trim();
            this._currentPage = 1;
            this.loadResults();
          }, 300);
        });
      }
      if (statusInput) {
        const dropdownRoot = statusInput.closest(".dropdown");
        if (dropdownRoot) {
          dropdownRoot.querySelectorAll(".dropdown__option").forEach((opt) => {
            opt.addEventListener("click", () => {
              this._statusFilter = opt.getAttribute("data-value") || "";
              this._currentPage = 1;
              this.loadResults();
            });
          });
        }
      }
      const compactToggle = document.getElementById("cwpm-compact-sizes");
      const resultsContainer = document.getElementById("cwpm-results-content");
      if (compactToggle && resultsContainer) {
        const savedPrefsForCompact = ((_a = window.CWPMConfig) == null ? void 0 : _a.userPrefs) || {};
        if (savedPrefsForCompact.compact_sizes !== void 0) {
          compactToggle.checked = savedPrefsForCompact.compact_sizes;
          resultsContainer.classList.toggle("cwpm-scan-results--sizes-expanded", !compactToggle.checked);
        }
        compactToggle.addEventListener("change", () => {
          resultsContainer.classList.toggle("cwpm-scan-results--sizes-expanded", !compactToggle.checked);
          this._savePrefs({ compact_sizes: compactToggle.checked });
        });
      }
    }
    



    wirePagination() {
      var _a;
      const perPageSelect = document.getElementById("cwpm-per-page");
      const prevBtn = document.getElementById("cwpm-page-prev");
      const nextBtn = document.getElementById("cwpm-page-next");
      if (!perPageSelect || !prevBtn || !nextBtn) return;
      const savedPrefsForPager = ((_a = window.CWPMConfig) == null ? void 0 : _a.userPrefs) || {};
      if (savedPrefsForPager.per_page !== void 0) {
        const savedPerPage = savedPrefsForPager.per_page;
        const perPageDropdown2 = perPageSelect.closest(".dropdown");
        if (perPageDropdown2) {
          const matchingOpt = perPageDropdown2.querySelector(`.dropdown__option[data-value="${savedPerPage}"]`);
          if (matchingOpt) {
            perPageSelect.value = String(savedPerPage);
            const trigger = perPageDropdown2.querySelector(".dropdown__trigger");
            if (trigger) trigger.textContent = matchingOpt.textContent;
            perPageDropdown2.querySelectorAll(".dropdown__option").forEach((o) => o.removeAttribute("aria-selected"));
            matchingOpt.setAttribute("aria-selected", "true");
          }
        }
      }
      this._currentPage = 1;
      this._perPage = parseInt(perPageSelect.value, 10) || 200;
      this._searchQuery = "";
      this._statusFilter = "";
      this._totalResults = 0;
      this._totalPages = 1;
      const perPageDropdown = perPageSelect.closest(".dropdown");
      if (perPageDropdown) {
        perPageDropdown.querySelectorAll(".dropdown__option").forEach((opt) => {
          opt.addEventListener("click", () => setTimeout(() => {
            this._perPage = parseInt(perPageSelect.value, 10) || 0;
            this._currentPage = 1;
            this._savePrefs({ per_page: this._perPage });
            this.loadResults();
          }, 50));
        });
      }
      prevBtn.addEventListener("click", () => {
        if (this._currentPage > 1) {
          this._currentPage--;
          this.loadResults();
        }
      });
      nextBtn.addEventListener("click", () => {
        if (this._currentPage < this._totalPages) {
          this._currentPage++;
          this.loadResults();
        }
      });
    }
    




    _savePrefs(prefs) {
      clearTimeout(this._prefsSaveTimer);
      this._prefsSaveTimer = setTimeout(() => {
        this.ajaxClient.post("cwpm_save_user_prefs", prefs).catch(() => {
        });
      }, 500);
    }
    


    wireSizesPopups() {
      document.querySelectorAll(".cwpm-sizes-toggle").forEach((btn) => {
        btn.addEventListener("click", (e) => {
          e.stopPropagation();
          const popup = btn.nextElementSibling;
          if (!popup) return;
          const isOpen = !popup.hidden;
          document.querySelectorAll(".cwpm-sizes-popup:not([hidden])").forEach((p) => {
            var _a;
            p.hidden = true;
            (_a = p.previousElementSibling) == null ? void 0 : _a.setAttribute("aria-expanded", "false");
          });
          if (!isOpen) {
            popup.hidden = false;
            btn.setAttribute("aria-expanded", "true");
          }
        });
      });
    }
    wireSelectAll() {
      const selectVisibleCb = document.getElementById("cwpm-select-visible");
      const selectAllCb = document.getElementById("cwpm-select-all");
      const rowCheckboxes = document.querySelectorAll("#cwpm-folder-list .row-select");
      const monthCheckboxes = document.querySelectorAll("#cwpm-folder-list .month-select");
      const yearCheckboxes = document.querySelectorAll("#cwpm-folder-list .year-select");
      if (!rowCheckboxes.length) return;
      document.querySelectorAll("#cwpm-folder-list .cwpm-year-select").forEach((label) => {
        label.addEventListener("click", (e) => e.stopPropagation());
      });
      const getVisibleRows = () => [...rowCheckboxes].filter((cb) => {
        const row = cb.closest("tr");
        return row && row.style.display !== "none";
      });
      const updateCheckboxStates = () => {
        const visible = getVisibleRows();
        const visibleN = visible.length;
        const visibleC = visible.filter((cb) => cb.checked).length;
        if (selectVisibleCb) {
          selectVisibleCb.checked = visibleN > 0 && visibleC === visibleN;
          selectVisibleCb.indeterminate = visibleC > 0 && visibleC < visibleN;
        }
        const allN = rowCheckboxes.length;
        const allC = [...rowCheckboxes].filter((cb) => cb.checked).length;
        if (selectAllCb) {
          selectAllCb.checked = allN > 0 && allC === allN;
          selectAllCb.indeterminate = allC > 0 && allC < allN;
        }
      };
      if (selectVisibleCb) {
        selectVisibleCb.addEventListener("change", () => {
          const on = selectVisibleCb.checked;
          getVisibleRows().forEach((cb) => {
            cb.checked = on;
          });
          selectVisibleCb.indeterminate = false;
          this._selectAllActive = false;
          this._allResultsForClean = [];
          this._excludedRelPaths.clear();
          updateMonthStates();
        });
      }
      if (selectAllCb) {
        selectAllCb.addEventListener("change", async () => {
          const on = selectAllCb.checked;
          rowCheckboxes.forEach((cb) => {
            cb.checked = on;
          });
          monthCheckboxes.forEach((cb) => {
            cb.checked = on;
          });
          yearCheckboxes.forEach((cb) => {
            cb.checked = on;
          });
          selectAllCb.indeterminate = false;
          this._selectAllActive = on;
          this._excludedRelPaths.clear();
          if (on) {
            try {
              const data = await this.ajaxClient.post("cwpm_scan_results_ids", {
                status_filter: this._statusFilter || "",
                search: this._searchQuery || ""
              });
              this._allResultsForClean = data.results || [];
            } catch (err) {
              console.error("Failed to fetch all results for clean:", err);
              this._allResultsForClean = [];
            }
          } else {
            this._allResultsForClean = [];
          }
          updateMonthStates();
        });
      }
      const syncExclusionForRow = (rowCb) => {
        if (!this._selectAllActive) return;
        const tr = rowCb.closest("tr");
        const rel = tr == null ? void 0 : tr.dataset.relPath;
        if (!rel) return;
        if (rowCb.checked) {
          this._excludedRelPaths.delete(rel);
        } else {
          this._excludedRelPaths.add(rel);
        }
      };
      yearCheckboxes.forEach((yearCb) => {
        yearCb.addEventListener("change", () => {
          const item = yearCb.closest(".accordion__item");
          if (!item) return;
          item.querySelectorAll(".row-select, .month-select").forEach((cb) => {
            cb.checked = yearCb.checked;
          });
          item.querySelectorAll(".row-select").forEach(syncExclusionForRow);
          updateMonthStates();
        });
      });
      monthCheckboxes.forEach((monthCb) => {
        monthCb.addEventListener("change", () => {
          const monthId = monthCb.dataset.monthId;
          const monthRows = document.querySelectorAll(`#cwpm-folder-list .row-select[data-month-id="${monthId}"]`);
          monthRows.forEach((cb) => {
            cb.checked = monthCb.checked;
          });
          monthRows.forEach(syncExclusionForRow);
          updateMonthStates();
        });
      });
      const updateMonthStates = () => {
        monthCheckboxes.forEach((monthCb) => {
          const monthId = monthCb.dataset.monthId;
          const monthRows = [...document.querySelectorAll(`#cwpm-folder-list .row-select[data-month-id="${monthId}"]`)].filter((cb) => {
            const r = cb.closest("tr");
            return r && r.style.display !== "none";
          });
          if (monthRows.length === 0) return;
          const monthChecked = monthRows.filter((cb) => cb.checked).length;
          monthCb.checked = monthChecked === monthRows.length;
          monthCb.indeterminate = monthChecked > 0 && monthChecked < monthRows.length;
        });
        updateYearStates();
        updateCheckboxStates();
      };
      const updateYearStates = () => {
        yearCheckboxes.forEach((yearCb) => {
          const item = yearCb.closest(".accordion__item");
          if (!item) return;
          const rows = [...item.querySelectorAll(".row-select")].filter((cb) => {
            const r = cb.closest("tr");
            return r && r.style.display !== "none";
          });
          if (rows.length === 0) return;
          const checked = rows.filter((cb) => cb.checked).length;
          yearCb.checked = checked === rows.length;
          yearCb.indeterminate = checked > 0 && checked < rows.length;
        });
      };
      rowCheckboxes.forEach((cb) => {
        cb.addEventListener("change", () => {
          if (this._selectAllActive) {
            const row = cb.closest("tr");
            const rel = row == null ? void 0 : row.dataset.relPath;
            if (rel) {
              if (cb.checked) {
                this._excludedRelPaths.delete(rel);
              } else {
                this._excludedRelPaths.add(rel);
              }
            }
          }
          updateMonthStates();
        });
      });
      updateCheckboxStates();
    }
    


    wireUsagePopups() {
      document.querySelectorAll(".cwpm-usage-toggle").forEach((btn) => {
        btn.addEventListener("click", (e) => {
          e.stopPropagation();
          const popup = btn.nextElementSibling;
          if (!popup) return;
          const isOpen = !popup.hidden;
          document.querySelectorAll(".cwpm-usage-popup:not([hidden])").forEach((p) => {
            var _a;
            p.hidden = true;
            (_a = p.previousElementSibling) == null ? void 0 : _a.setAttribute("aria-expanded", "false");
          });
          if (!isOpen) {
            popup.hidden = false;
            btn.setAttribute("aria-expanded", "true");
          }
        });
      });
      document.addEventListener("click", () => {
        document.querySelectorAll(".cwpm-usage-popup:not([hidden])").forEach((p) => {
          var _a;
          p.hidden = true;
          (_a = p.previousElementSibling) == null ? void 0 : _a.setAttribute("aria-expanded", "false");
        });
        document.querySelectorAll(".cwpm-sizes-popup:not([hidden])").forEach((p) => {
          var _a;
          p.hidden = true;
          (_a = p.previousElementSibling) == null ? void 0 : _a.setAttribute("aria-expanded", "false");
        });
      });
    }
    





    async handleResultsNavClick() {
      var _a;
      const meta = (_a = window.CWPMConfig) == null ? void 0 : _a.scanMeta;
      const noResultsMsg = document.getElementById("cwpm-no-results-message");
      const desc = document.getElementById("cwpm-results-desc");
      const footer = document.getElementById("cwpm-results-footer");
      const resultsContent = document.getElementById("cwpm-results-content");
      if (!meta || meta.status !== "complete" && meta.status !== "in_progress" && meta.status !== "interrupted") {
        if (noResultsMsg) noResultsMsg.style.display = "";
        if (desc) desc.style.display = "none";
        if (footer) footer.style.display = "none";
        if (resultsContent) resultsContent.style.display = "none";
        if (this.statsGrid) this.statsGrid.style.display = "none";
        if (this.actionBar) this.actionBar.style.display = "none";
        if (this.folderList) this.folderList.innerHTML = "";
        return;
      }
      if (noResultsMsg) noResultsMsg.style.display = "none";
      if (desc) desc.style.display = "";
      if (footer) footer.style.display = "";
      if (resultsContent) resultsContent.style.display = "";
      if (this.statsGrid) this.statsGrid.style.display = "";
      if (this.actionBar) this.actionBar.style.display = "flex";
      await this.loadStats();
      await this.loadResults({ page: 1 });
      this.updateScanDate();
    }
    


    updateScanDate() {
      var _a;
      const dateEl = document.getElementById("cwpm-scan-date");
      const meta = (_a = window.CWPMConfig) == null ? void 0 : _a.scanMeta;
      if (dateEl && (meta == null ? void 0 : meta.completed_at)) {
        const d = new Date(meta.completed_at);
        dateEl.textContent = "\u2014 " + d.toLocaleDateString(void 0, {
          year: "numeric",
          month: "short",
          day: "numeric"
        }) + ", " + d.toLocaleTimeString(void 0, {
          hour: "numeric",
          minute: "2-digit"
        });
      }
    }
    



    showBackButton() {
      if (!this.statusText || !this.wizard) return;
      const existing = document.querySelector(".cwpm-prev-step[data-cwpm-back-button]");
      if (existing) existing.remove();
      const btn = document.createElement("button");
      btn.className = "btn btn--outline cwpm-prev-step";
      btn.dataset.cwpmBackButton = "1";
      btn.textContent = "Back to Settings";
      btn.style.marginTop = "var(--space-4)";
      btn.addEventListener("click", () => this.wizard.goTo(1));
      this.statusText.insertAdjacentElement("afterend", btn);
    }
    


    updateScanCards() {
      var _a;
      const meta = (_a = window.CWPMConfig) == null ? void 0 : _a.scanMeta;
      if (!meta) {
        if (this.cardLastScan) this.cardLastScan.style.display = "none";
        if (this.cardResumeScan) this.cardResumeScan.style.display = "none";
        return;
      }
      if (meta.status === "complete") {
        if (this.cardLastScan) {
          this.cardLastScan.style.display = "";
          const dateEl = document.getElementById("cwpm-last-scan-date");
          if (dateEl && meta.completed_at) {
            const d = new Date(meta.completed_at);
            dateEl.textContent = d.toLocaleDateString(void 0, {
              year: "numeric",
              month: "short",
              day: "numeric"
            }) + ", " + d.toLocaleTimeString(void 0, {
              hour: "numeric",
              minute: "2-digit"
            });
          }
        }
        if (this.cardResumeScan) this.cardResumeScan.style.display = "none";
      } else if (meta.status === "in_progress" || meta.status === "interrupted") {
        if (this.cardResumeScan) {
          this.cardResumeScan.style.display = "";
          const infoEl = document.getElementById("cwpm-resume-scan-info");
          if (infoEl) {
            infoEl.textContent = `${meta.scanned_files} / ${meta.total_files} files`;
          }
        }
        if (this.cardLastScan) this.cardLastScan.style.display = "none";
      }
    }
    



    forceStep(stepNumber) {
      if (!this.wizard) return;
      const steps = this.wizard.steps;
      steps.forEach((s) => {
        s.classList.remove("cwpm-step--active", "cwpm-step--entering", "cwpm-step--exiting");
      });
      const target = steps.find((s) => parseInt(s.dataset.step, 10) === stepNumber);
      if (target) target.classList.add("cwpm-step--active");
      this.wizard.currentStep = stepNumber;
      this.wizard.navItems.forEach((item) => {
        const num = parseInt(item.dataset.step, 10);
        item.classList.toggle("cwpm-wizard-nav__item--active", num === stepNumber);
        item.toggleAttribute("aria-current", num === stepNumber);
      });
    }
  };

  
  var ViewRouter = class _ViewRouter {
    constructor() {
      this.stepsEl = document.querySelector("#cwpm-steps");
      this.wizardNav = document.querySelector("#cwpm-wizard-nav");
      this.views =  new Map();
      this.activeView = null;
      this._isAnimating = false;
    }
    





    register(name, el) {
      this.views.set(name, el);
    }
    




    show(name) {
      const target = this.views.get(name);
      if (!target || this._isAnimating) return;
      this._isAnimating = true;
      const wizardVisible = this.stepsEl && this.stepsEl.style.display !== "none";
      if (wizardVisible) {
        this._exitWizard(() => this._showTarget(target, name));
      } else {
        for (const [viewName, el] of this.views) {
          if (viewName !== name) {
            el.style.setProperty("display", "none");
          }
        }
        this._showTargetInstant(target, name);
      }
    }
    _showTarget(target, name) {
      var _a, _b;
      (_a = this.stepsEl) == null ? void 0 : _a.style.setProperty("display", "none");
      (_b = this.wizardNav) == null ? void 0 : _b.style.setProperty("display", "none");
      for (const [, el] of this.views) {
        el.style.setProperty("display", "none");
      }
      target.style.removeProperty("display");
      target.classList.add("cwpm-secondary-view--entering");
      target.addEventListener("animationend", () => {
        target.classList.remove("cwpm-secondary-view--entering");
      }, { once: true });
      this.activeView = name;
      _ViewRouter.setUrlView(name);
      this._isAnimating = false;
    }
    _showTargetInstant(target, name) {
      target.style.removeProperty("display");
      target.classList.add("cwpm-secondary-view--entering");
      target.addEventListener("animationend", () => {
        target.classList.remove("cwpm-secondary-view--entering");
      }, { once: true });
      this.activeView = name;
      _ViewRouter.setUrlView(name);
      this._isAnimating = false;
    }
    _exitWizard(callback) {
      var _a;
      const currentStep = (_a = this.stepsEl) == null ? void 0 : _a.querySelector(".cwpm-step--active");
      if (currentStep) {
        currentStep.classList.add("cwpm-step--exiting");
        const proceed = () => {
          currentStep.classList.remove("cwpm-step--active", "cwpm-step--exiting");
          callback();
        };
        currentStep.addEventListener("animationend", proceed, { once: true });
        setTimeout(() => {
          if (currentStep.classList.contains("cwpm-step--exiting")) {
            currentStep.classList.remove("cwpm-step--active", "cwpm-step--exiting");
            callback();
          }
        }, 300);
      } else {
        callback();
      }
    }
    _enterWizard(callback) {
      var _a, _b;
      (_a = this.stepsEl) == null ? void 0 : _a.style.removeProperty("display");
      (_b = this.wizardNav) == null ? void 0 : _b.style.removeProperty("display");
      callback();
    }
    static setUrlView(view) {
      const url = new URL(window.location.href);
      if (view) {
        url.searchParams.set("view", view);
      } else {
        url.searchParams.delete("view");
      }
      history.replaceState(null, "", url.toString());
    }
    /**
     * Return to the wizard, hiding all views.
     *
     * @param {Function} [onEnter] Optional callback fired after the wizard is visible.
     */
    back(onEnter) {
      if (this._isAnimating || !this.activeView) return;
      this._isAnimating = true;
      const currentView = this.views.get(this.activeView);
      if (!currentView) {
        this._isAnimating = false;
        return;
      }
      currentView.classList.add("cwpm-secondary-view--exiting");
      const proceed = () => {
        currentView.classList.remove("cwpm-secondary-view--exiting");
        currentView.style.setProperty("display", "none");
        this._enterWizard(() => {
          this.activeView = null;
          _ViewRouter.setUrlView(null);
          this._isAnimating = false;
          if (onEnter) onEnter();
        });
      };
      currentView.addEventListener("animationend", proceed, { once: true });
      setTimeout(() => {
        if (currentView.classList.contains("cwpm-secondary-view--exiting")) {
          proceed();
        }
      }, 300);
    }
  };

  
  var TrashManager = class {
    constructor(ajaxClient, router) {
      this.ajaxClient = ajaxClient;
      this.router = router;
      this._currentPage = 1;
      this._perPage = 50;
      this._searchQuery = "";
      this._totalResults = 0;
      this._totalPages = 1;
      this._selectAllActive = false;
      this._allTrashForAction = [];
      this._excludedRelPaths = /* @__PURE__ */ new Set();
      this._selectedAction = "";
      this.actionBar = document.querySelector("#cwpm-trash-action-bar");
    }
    init() {
      const navBtn = document.querySelector("#cwpm-trash-nav");
      const trashView = document.querySelector("#cwpm-trash-view");
      const autoDeleteCb = document.querySelector("#cwpm-trash-auto-delete");
      if (!navBtn || !trashView) return;
      navBtn.addEventListener("click", () => {
        Helpers.clearNavActiveState();
        navBtn.classList.add("active");
        this.router.show("trash");
        this.loadTrash();
      });
      autoDeleteCb == null ? void 0 : autoDeleteCb.addEventListener("change", () => {
        this.saveAutoDelete(autoDeleteCb.checked);
      });
      this.wireSearch();
      this.wirePagination();
      this.wireSelectAll();
      this.wireEmptyTrash();
      this.wireActionsModal();
      this.wireRunActions();
    }
    async saveAutoDelete(enabled) {
      try {
        await this.ajaxClient.post("cwpm_save_trash_settings", {
          trash_auto_delete: enabled ? "1" : "0"
        });
      } catch (e) {
        console.error("Failed to save trash auto-delete setting:", e);
      }
    }
    
    async loadTrash(opts = {}) {
      const page = opts.page || this._currentPage;
      const tbody = document.querySelector("#cwpm-trash-tbody");
      if (!tbody) return;
      tbody.innerHTML = '<tr><td colspan="5" class="cwpm-trash-view__loading"><span class="cwpm-row-spinner" aria-hidden="true"></span> Loading\u2026</td></tr>';
      try {
        const data = await this.ajaxClient.post("cwpm_trash_list", {
          page,
          per_page: this._perPage,
          search: this._searchQuery
        });
        this._currentPage = data.page;
        this._totalResults = data.total;
        this._totalSize = data.total_size || 0;
        this._totalPages = data.total_pages;
        if (this.actionBar) {
          const hasResults = data.total > 0;
          const hasSearch = this._searchQuery.length > 0;
          this.actionBar.style.display = hasResults || hasSearch ? "flex" : "none";
        }
        this.renderTable(data.results || []);
        this.updatePagination();
        this.updateStats();
      } catch (e) {
        tbody.innerHTML = '<tr><td colspan="5" class="text-error">Failed to load trash.</td></tr>';
      }
    }
    
    renderTable(files) {
      const tbody = document.querySelector("#cwpm-trash-tbody");
      const emptyState = document.querySelector("#cwpm-trash-empty-state");
      const table = document.querySelector("#cwpm-trash-table");
      const emptyBtn = document.querySelector("#cwpm-trash-empty");
      if (!tbody) return;
      if (files.length === 0) {
        tbody.innerHTML = "";
        table == null ? void 0 : table.style.setProperty("display", "none");
        emptyState == null ? void 0 : emptyState.style.removeProperty("display");
        if (emptyBtn) emptyBtn.disabled = true;
        return;
      }
      table == null ? void 0 : table.style.removeProperty("display");
      emptyState == null ? void 0 : emptyState.style.setProperty("display", "none");
      if (emptyBtn) emptyBtn.disabled = false;
      tbody.innerHTML = files.map((f) => {
        const dir = f.original_path ? f.original_path.split("/").slice(0, -1).join("/") : "";
        const thumbImg = f.thumb_url ? `<img src="${Helpers.escHtml(f.thumb_url)}" alt="" class="cwpm-trash__thumb-img" loading="lazy" />` : `<div class="cwpm-trash__thumb-placeholder" aria-hidden="true"><i class="fa-solid fa-file-image"></i></div>`;
        const checked = this._selectAllActive && !this._excludedRelPaths.has(f.rel_path) ? " checked" : "";
        return `<tr data-rel-path="${Helpers.escHtml(f.rel_path)}">
				<td class="td-select">
					<label class="form__check">
						<input type="checkbox" class="row-select"${checked} />
						<span></span>
					</label>
				</td>
				<td>
					<div class="cwpm-trash__name-cell">
						${thumbImg}
						<span class="cwpm-trash__filename">${Helpers.escHtml(f.file_name)}</span>
						${dir ? `<span class="cwpm-trash__dir">${Helpers.escHtml(dir)}</span>` : ""}
					</div>
				</td>
				<td class="text-muted">${Helpers.escHtml(f.size_formatted || "")}</td>
				<td class="cwpm-trash__date text-muted">${this._formatDate(f.trashed_at)}</td>
				<td class="cwpm-trash__actions">
					<button type="button" class="cwpm-trash__action-btn cwpm-trash__action-btn--restore"
						data-action="restore" title="Restore">
						<i class="fa-solid fa-rotate-left"></i>
					</button>
					<button type="button" class="cwpm-trash__action-btn cwpm-trash__action-btn--delete"
						data-action="delete" title="Delete permanently">
						<i class="fa-solid fa-trash"></i>
					</button>
				</td>
			</tr>`;
      }).join("");
      tbody.querySelectorAll(".cwpm-trash__action-btn--restore").forEach((btn) => {
        btn.addEventListener("click", () => {
          const row = btn.closest("tr");
          this.restoreFile(row.dataset.relPath, row);
        });
      });
      tbody.querySelectorAll(".cwpm-trash__action-btn--delete").forEach((btn) => {
        btn.addEventListener("click", () => {
          const row = btn.closest("tr");
          this.deleteFile(row.dataset.relPath, row);
        });
      });
      tbody.querySelectorAll('input[type="checkbox"]').forEach((cb) => {
        cb.addEventListener("change", () => {
          if (this._selectAllActive) {
            const row = cb.closest("tr");
            const rel = row == null ? void 0 : row.dataset.relPath;
            if (rel) {
              if (cb.checked) {
                this._excludedRelPaths.delete(rel);
              } else {
                this._excludedRelPaths.add(rel);
              }
            }
          }
          this._updateCheckboxStates();
        });
      });
    }
    /**
     * Format a MySQL datetime string as "Apr 5, 2026 5:30 PM".
     */
    _formatDate(datetimeStr) {
      if (!datetimeStr) return "";
      const d = new Date(datetimeStr);
      if (isNaN(d.getTime())) return datetimeStr;
      return d.toLocaleDateString(void 0, { month: "short", day: "numeric", year: "numeric" }) + " " + d.toLocaleTimeString(void 0, { hour: "numeric", minute: "2-digit" });
    }
    /* ─── updateStats ─────────────────────────────────────────────── */
    updateStats() {
      const countEl = document.querySelector("#cwpm-trash-count");
      const sizeEl = document.querySelector("#cwpm-trash-size");
      if (countEl) countEl.textContent = this._totalResults;
      if (sizeEl) sizeEl.textContent = Helpers.formatSize(this._totalSize);
    }
    /* ─── updatePagination ────────────────────────────────────────── */
    updatePagination() {
      const info = document.querySelector("#cwpm-trash-pagination-info");
      const prev = document.querySelector("#cwpm-trash-page-prev");
      const next = document.querySelector("#cwpm-trash-page-next");
      if (info) {
        if (this._totalResults === 0) {
          info.textContent = "0\u20130 of 0";
        } else {
          const start = (this._currentPage - 1) * this._perPage + 1;
          const end = Math.min(start + this._perPage - 1, this._totalResults);
          info.textContent = `${start}\u2013${end} of ${this._totalResults}`;
        }
      }
      if (prev) prev.disabled = this._currentPage <= 1;
      if (next) next.disabled = this._currentPage >= this._totalPages;
    }
    /* ─── wireSearch ─────────────────────────────────────────────── */
    wireSearch() {
      const input = document.querySelector("#cwpm-trash-search-filter");
      if (!input) return;
      let timeout;
      input.addEventListener("input", () => {
        clearTimeout(timeout);
        timeout = setTimeout(() => {
          this._searchQuery = input.value.trim();
          this._currentPage = 1;
          this._selectAllActive = false;
          this._allTrashForAction = [];
          this.loadTrash();
        }, 300);
      });
    }
    /* ─── wirePagination ─────────────────────────────────────────── */
    wirePagination() {
      const perPageSelect = document.querySelector("#cwpm-trash-per-page");
      const prevBtn = document.querySelector("#cwpm-trash-page-prev");
      const nextBtn = document.querySelector("#cwpm-trash-page-next");
      if (perPageSelect) {
        const dropdown = perPageSelect.closest(".dropdown");
        if (dropdown) {
          dropdown.querySelectorAll(".dropdown__option").forEach((opt) => {
            opt.addEventListener("click", () => {
              setTimeout(() => {
                this._perPage = parseInt(perPageSelect.value, 10) || 50;
                this._currentPage = 1;
                this._selectAllActive = false;
                this._allTrashForAction = [];
                this.loadTrash();
              }, 50);
            });
          });
        }
      }
      if (prevBtn) {
        prevBtn.addEventListener("click", () => {
          if (this._currentPage > 1) {
            this._currentPage--;
            this.loadTrash();
          }
        });
      }
      if (nextBtn) {
        nextBtn.addEventListener("click", () => {
          if (this._currentPage < this._totalPages) {
            this._currentPage++;
            this.loadTrash();
          }
        });
      }
    }
    /* ─── wireSelectAll ──────────────────────────────────────────── */
    wireSelectAll() {
      const selectVisibleCb = document.querySelector("#cwpm-trash-select-visible");
      const selectAllCb = document.querySelector("#cwpm-trash-select-all");
      const getRowCheckboxes = () => [...document.querySelectorAll('#cwpm-trash-tbody tr[data-rel-path] input[type="checkbox"]')];
      this._updateCheckboxStates = () => {
        const cbs = getRowCheckboxes();
        const checked = cbs.filter((cb) => cb.checked).length;
        if (selectVisibleCb) {
          selectVisibleCb.checked = cbs.length > 0 && checked === cbs.length;
          selectVisibleCb.indeterminate = checked > 0 && checked < cbs.length;
        }
        if (selectAllCb) {
          selectAllCb.checked = this._selectAllActive && this._allTrashForAction.length > 0;
          selectAllCb.indeterminate = !this._selectAllActive && checked > 0;
        }
      };
      if (selectVisibleCb) {
        selectVisibleCb.addEventListener("change", () => {
          const on = selectVisibleCb.checked;
          getRowCheckboxes().forEach((cb) => {
            cb.checked = on;
          });
          this._selectAllActive = false;
          this._allTrashForAction = [];
          this._excludedRelPaths.clear();
          this._updateCheckboxStates();
        });
      }
      if (selectAllCb) {
        selectAllCb.addEventListener("change", async () => {
          const on = selectAllCb.checked;
          if (on) {
            try {
              const data = await this.ajaxClient.post("cwpm_trash_list_ids", {
                search: this._searchQuery
              });
              this._allTrashForAction = data.results || [];
            } catch (err) {
              this._allTrashForAction = [];
            }
            this._selectAllActive = true;
            this._excludedRelPaths.clear();
            getRowCheckboxes().forEach((cb) => {
              cb.checked = true;
            });
          } else {
            this._selectAllActive = false;
            this._allTrashForAction = [];
            this._excludedRelPaths.clear();
            getRowCheckboxes().forEach((cb) => {
              cb.checked = false;
            });
          }
          this._updateCheckboxStates();
        });
      }
    }
    /* ─── wireEmptyTrash ─────────────────────────────────────────── */
    wireEmptyTrash() {
      const emptyBtn = document.querySelector("#cwpm-trash-empty");
      if (emptyBtn) {
        emptyBtn.addEventListener("click", () => this.emptyTrash());
      }
    }
    /* ─── runBulkAction ─────────────────────────────────────────── */
    async runBulkAction() {
      const relPaths = this.getSelectedRelPaths();
      if (relPaths.length === 0) {
        Helpers.setText("cwpm-nothing-to-clean-message", "Select at least one file before continuing.");
        if (typeof window.Modal !== "undefined") {
          window.Modal.open("cwpm-nothing-to-clean-modal");
        }
        return;
      }
      if (!this._selectedAction) {
        Helpers.setText("cwpm-nothing-to-clean-message", "Select an action from the Actions menu first.");
        if (typeof window.Modal !== "undefined") {
          window.Modal.open("cwpm-nothing-to-clean-modal");
        }
        return;
      }
      if (this._selectedAction === "restore") {
        await this.batchRestore(relPaths);
        this._resetActionSelection();
      } else if (this._selectedAction === "permanent-delete") {
        this.showDisclaimerModal(async () => {
          await this.batchDelete(relPaths);
          this._resetActionSelection();
        });
      }
    }
    /* ─── _resetActionSelection ─────────────────────────────────── */
    _resetActionSelection() {
      this._selectedAction = "";
      document.querySelectorAll("#cwpm-trash-actions-modal .cwpm-action-card").forEach((c) => c.classList.remove("cwpm-action-card--selected"));
      this._syncActionCount();
    }
    /* ─── getSelectedRelPaths ────────────────────────────────────── */
    getSelectedRelPaths() {
      if (this._selectAllActive && this._allTrashForAction.length) {
        return this._allTrashForAction.map((f) => f.rel_path).filter((rel) => !this._excludedRelPaths.has(rel));
      }
      const cbs = [...document.querySelectorAll('#cwpm-trash-tbody tr[data-rel-path] input[type="checkbox"]')];
      return cbs.filter((cb) => cb.checked).map((cb) => {
        var _a;
        return (_a = cb.closest("tr")) == null ? void 0 : _a.dataset.relPath;
      }).filter(Boolean);
    }
    /* ─── restoreFile ────────────────────────────────────────────── */
    // Routes through batchRestore so missing-file (orphan) records reach the
    // orphan-confirmation modal + force-delete recovery path. The single
    // cwpm_trash_restore endpoint has no force flow, so its file_exists() check
    // surfaces a generic "Restore failed" with no recovery for the user.
    async restoreFile(relPath, row) {
      this.setRowBusy(row, true);
      try {
        await this.batchRestore([relPath]);
      } catch (e) {
        this.setRowBusy(row, false);
      }
    }
    /* ─── deleteFile ─────────────────────────────────────────────── */
    // Same rationale as restoreFile — funnel single-row deletes through
    // batchDelete so orphan DB records can be cleaned via the force flow.
    async deleteFile(relPath, row) {
      this.showDisclaimerModal(async () => {
        this.setRowBusy(row, true);
        try {
          await this.batchDelete([relPath]);
        } catch (e) {
          this.setRowBusy(row, false);
        }
      });
    }
    /* ─── progress UI helpers ─────────────────────────────────── */
    // Hide the action-bar rows and show the progress bar inside the same bar.
    // Mirrors the scan-results clean flow so users get the same feedback shape.
    _showProgressUI(label) {
      const row1 = document.getElementById("cwpm-trash-action-bar-row1");
      const row2 = document.getElementById("cwpm-trash-action-bar-row2");
      const wrap = document.getElementById("cwpm-trash-bar-clean-progress");
      const statusEl = document.getElementById("cwpm-trash-bar-clean-status");
      const fileEl = document.getElementById("cwpm-trash-bar-clean-file");
      const barEl = document.getElementById("cwpm-trash-bar-clean-progress-bar");
      if (row1) row1.style.display = "none";
      if (row2) row2.style.display = "none";
      if (wrap) wrap.style.display = "";
      if (statusEl) statusEl.textContent = label;
      if (fileEl) fileEl.textContent = "";
      if (barEl) barEl.style.width = "0%";
    }
    _setProgressUI(pct, label, currentFile = "") {
      const statusEl = document.getElementById("cwpm-trash-bar-clean-status");
      const fileEl = document.getElementById("cwpm-trash-bar-clean-file");
      const barEl = document.getElementById("cwpm-trash-bar-clean-progress-bar");
      if (barEl) barEl.style.width = `${pct}%`;
      if (statusEl && label) statusEl.textContent = `${label} ${pct}%`;
      if (fileEl) fileEl.textContent = currentFile;
    }
    _hideProgressUI() {
      const row1 = document.getElementById("cwpm-trash-action-bar-row1");
      const row2 = document.getElementById("cwpm-trash-action-bar-row2");
      const wrap = document.getElementById("cwpm-trash-bar-clean-progress");
      if (row1) row1.style.display = "";
      if (row2) row2.style.display = "";
      if (wrap) wrap.style.display = "none";
    }
    // Post a list of rel_paths to a batch endpoint in chunks, updating the
    // progress bar between requests. Returns aggregated counts + failed paths.
    async _postChunked(action, relPaths, label, force = false) {
      var _a, _b;
      const CHUNK = 50;
      let succeeded = 0;
      let failed = 0;
      const failedPaths = [];
      for (let i = 0; i < relPaths.length; i += CHUNK) {
        const chunk = relPaths.slice(i, i + CHUNK);
        const payload = { rel_paths: JSON.stringify(chunk) };
        if (force) payload.force = "1";
        const data = await this.ajaxClient.post(action, payload);
        succeeded += (_a = data == null ? void 0 : data.succeeded) != null ? _a : 0;
        failed += (_b = data == null ? void 0 : data.failed) != null ? _b : 0;
        if (Array.isArray(data == null ? void 0 : data.results)) {
          for (const r of data.results) {
            if (!r.success) failedPaths.push(r.rel_path);
          }
        }
        const done = Math.min(i + chunk.length, relPaths.length);
        const pct = Math.round(done / relPaths.length * 100);
        this._setProgressUI(pct, label, chunk[chunk.length - 1] || "");
      }
      return { succeeded, failed, failedPaths };
    }
    /* ─── batchRestore ───────────────────────────────────────────── */
    async batchRestore(relPaths) {
      this._showProgressUI("Restoring...");
      let totalSucceeded = 0;
      try {
        const first = await this._postChunked("cwpm_trash_batch_restore", relPaths, "Restoring...");
        totalSucceeded = first.succeeded;
        if (first.failedPaths.length > 0) {
          this._setProgressUI(0, "Cleaning orphan records...");
          const forced = await this._postChunked("cwpm_trash_batch_restore", first.failedPaths, "Cleaning orphan records...", true);
          totalSucceeded += forced.succeeded;
        }
      } finally {
        this._hideProgressUI();
      }
      this._selectAllActive = false;
      this._allTrashForAction = [];
      this._excludedRelPaths.clear();
      this.loadTrash();
      this._showTrashDoneModal("restore", totalSucceeded);
    }
    /* ─── batchDelete ───────────────────────────────────────────── */
    async batchDelete(relPaths) {
      this._pendingBatchDeletePaths = relPaths;
      this._showProgressUI("Deleting...");
      let result;
      try {
        result = await this._postChunked("cwpm_trash_batch_delete", relPaths, "Deleting...");
      } finally {
        this._hideProgressUI();
      }
      if (result.failedPaths.length > 0) {
        this._showOrphanModal(result.failedPaths, "delete");
        return;
      }
      this._selectAllActive = false;
      this._allTrashForAction = [];
      this._excludedRelPaths.clear();
      this.loadTrash();
      this._showTrashDoneModal("delete", result.succeeded);
    }
    /* ─── _showOrphanModal ─────────────────────────────────────── */
    _showOrphanModal(failedPaths, action) {
      var _a;
      const modal = "cwpm-orphan-delete-modal";
      const list = document.querySelector("#cwpm-orphan-delete-list");
      const titleEl = document.querySelector("#cwpm-orphan-delete-modal-title");
      const messageEl = document.querySelector("#cwpm-orphan-delete-message");
      const descEl = document.querySelector("#cwpm-orphan-delete-desc");
      const oldProceed = document.querySelector("#cwpm-orphan-delete-proceed");
      const oldCancel = document.querySelector("#cwpm-orphan-delete-cancel");
      const newProceed = oldProceed ? oldProceed.cloneNode(true) : null;
      const newCancel = oldCancel ? oldCancel.cloneNode(true) : null;
      if (oldProceed && newProceed) oldProceed.parentNode.replaceChild(newProceed, oldProceed);
      if (oldCancel && newCancel) oldCancel.parentNode.replaceChild(newCancel, oldCancel);
      if (list) {
        list.innerHTML = failedPaths.map((p) => `<li>${Helpers.escHtml(p)}</li>`).join("");
      }
      if (titleEl) titleEl.textContent = "Orphan Trash Records";
      if (messageEl) messageEl.textContent = "These trash records have no matching file or attachment. Remove the database records anyway?";
      if (descEl) descEl.textContent = "Do you want to remove the database records for these files anyway?";
      if (newCancel) {
        newCancel.addEventListener("click", () => {
          var _a2;
          (_a2 = window.Modal) == null ? void 0 : _a2.close(modal);
          this._selectAllActive = false;
          this._allTrashForAction = [];
          this.loadTrash();
        });
      }
      if (newProceed) {
        newProceed.addEventListener("click", async () => {
          var _a2;
          (_a2 = window.Modal) == null ? void 0 : _a2.close(modal);
          if (action === "restore") {
            await this._forceBatchRestore(this._pendingBatchRestorePaths);
          } else {
            await this._forceBatchDelete(this._pendingBatchDeletePaths);
          }
        });
      }
      (_a = window.Modal) == null ? void 0 : _a.open(modal);
    }
    /* ─── _forceBatchDelete ────────────────────────────────────── */
    async _forceBatchDelete(relPaths) {
      this._showProgressUI("Cleaning orphan records...");
      let result;
      try {
        result = await this._postChunked("cwpm_trash_batch_delete", relPaths, "Cleaning orphan records...", true);
      } finally {
        this._hideProgressUI();
      }
      this._selectAllActive = false;
      this._allTrashForAction = [];
      this._excludedRelPaths.clear();
      this.loadTrash();
      this._showTrashDoneModal("delete", result.succeeded || relPaths.length);
    }
    /* ─── _forceBatchRestore ───────────────────────────────────── */
    async _forceBatchRestore(relPaths) {
      this._showProgressUI("Cleaning orphan records...");
      let result;
      try {
        result = await this._postChunked("cwpm_trash_batch_restore", relPaths, "Cleaning orphan records...", true);
      } finally {
        this._hideProgressUI();
      }
      this._selectAllActive = false;
      this._allTrashForAction = [];
      this._excludedRelPaths.clear();
      this.loadTrash();
      this._showTrashDoneModal("restore", result.succeeded || relPaths.length);
    }
    /* ─── emptyTrash ─────────────────────────────────────────────── */
    async emptyTrash() {
      this.showDisclaimerModal(async () => {
        var _a;
        const emptyBtn = document.querySelector("#cwpm-trash-empty");
        if (emptyBtn) emptyBtn.disabled = true;
        this._showProgressUI("Emptying trash...");
        this._setProgressUI(50, "Emptying trash...");
        try {
          const data = await this.ajaxClient.post("cwpm_trash_empty", {});
          this._setProgressUI(100, "Emptying trash...");
          this.loadTrash();
          this._showTrashDoneModal("empty", (_a = data.deleted) != null ? _a : 0);
        } catch (e) {
          if (emptyBtn) emptyBtn.disabled = false;
        } finally {
          this._hideProgressUI();
        }
      });
    }
    /* ─── _showTrashDoneModal ────────────────────────────────────── */
    _showTrashDoneModal(action, count) {
      var _a;
      const container = document.getElementById("cwpm-clean-action-results");
      const msgEl = document.querySelector("#cwpm-clean-done-modal .cwpm-clean-done-message");
      if (!container) return;
      const meta = {
        restore: { icon: "fa-solid fa-rotate-left", iconColor: "text-success", label: "Files restored" },
        delete: { icon: "fa-solid fa-circle-xmark", iconColor: "text-error", label: "Files permanently deleted" },
        empty: { icon: "fa-solid fa-trash", iconColor: "text-error", label: "Files permanently deleted" }
      };
      const m = meta[action] || { icon: "fa-solid fa-circle-check", iconColor: "", label: "Items processed" };
      if (msgEl) msgEl.textContent = count > 0 ? "Trash operation complete." : "No items were processed.";
      if (count === 0) {
        container.innerHTML = '<p class="text-muted">No items were processed.</p>';
      } else {
        container.innerHTML = `
				<div class="cwpm-stats-grid">
					<div class="cwpm-stat-card">
						<span class="cwpm-stat-card__icon icon ${m.iconColor}" aria-hidden="true"><i class="${m.icon}"></i></span>
						<div class="cwpm-stat-card__body">
							<span class="cwpm-stat-card__value">${count}</span>
							<span class="cwpm-stat-card__label">${m.label}</span>
						</div>
					</div>
				</div>`;
      }
      (_a = window.Modal) == null ? void 0 : _a.open("cwpm-clean-done-modal");
    }
    /* ─── showDisclaimerModal ─────────────────────────────────────── */
    showDisclaimerModal(onConfirm) {
      var _a;
      const modal = "cwpm-trash-disclaimer-modal";
      const checkbox = document.querySelector("#cwpm-trash-disclaimer-checkbox");
      const oldProceed = document.querySelector("#cwpm-trash-disclaimer-proceed");
      const oldCancel = document.querySelector("#cwpm-trash-disclaimer-cancel");
      const newProceed = oldProceed ? oldProceed.cloneNode(true) : null;
      const newCancel = oldCancel ? oldCancel.cloneNode(true) : null;
      if (oldProceed && newProceed) oldProceed.parentNode.replaceChild(newProceed, oldProceed);
      if (oldCancel && newCancel) oldCancel.parentNode.replaceChild(newCancel, oldCancel);
      if (checkbox) {
        checkbox.checked = false;
      }
      if (newProceed) {
        newProceed.disabled = true;
      }
      const onCheckboxChange = () => {
        if (newProceed) newProceed.disabled = !checkbox.checked;
      };
      if (checkbox) {
        checkbox.addEventListener("change", onCheckboxChange);
      }
      if (newProceed) {
        newProceed.addEventListener("click", () => {
          var _a2;
          if (checkbox) checkbox.removeEventListener("change", onCheckboxChange);
          (_a2 = window.Modal) == null ? void 0 : _a2.close(modal);
          onConfirm();
        });
      }
      if (newCancel) {
        newCancel.addEventListener("click", () => {
          var _a2;
          if (checkbox) checkbox.removeEventListener("change", onCheckboxChange);
          (_a2 = window.Modal) == null ? void 0 : _a2.close(modal);
        });
      }
      (_a = window.Modal) == null ? void 0 : _a.open(modal);
    }
    /* ─── wireActionsModal ───────────────────────────────────────── */
    wireActionsModal() {
      const trigger = document.querySelector("#cwpm-trash-actions-trigger");
      const modal = "cwpm-trash-actions-modal";
      const cards = document.querySelectorAll("#cwpm-trash-actions-modal .cwpm-action-card");
      const descEl = document.querySelector("#cwpm-trash-action-cards-desc");
      if (!trigger) return;
      const actionMeta = {
        "restore": { label: "Restore File", color: "var(--green)" },
        "permanent-delete": { label: "Permanently Delete", color: "var(--red)" }
      };
      const tooltip = Helpers.createActionsTooltip(trigger, () => {
        if (!this._selectedAction) return [];
        const meta = actionMeta[this._selectedAction] || { label: this._selectedAction, color: "var(--gray)" };
        return [{ action: this._selectedAction, label: meta.label, color: meta.color }];
      });
      trigger.addEventListener("click", () => {
        var _a;
        tooltip.hidden = true;
        (_a = window.Modal) == null ? void 0 : _a.open(modal);
      });
      cards.forEach((card) => {
        card.addEventListener("click", () => {
          const action = card.dataset.action;
          if (this._selectedAction === action) {
            this._selectedAction = "";
            card.classList.remove("cwpm-action-card--selected");
          } else {
            cards.forEach((c) => c.classList.remove("cwpm-action-card--selected"));
            this._selectedAction = action;
            card.classList.add("cwpm-action-card--selected");
            if (descEl) descEl.textContent = card.dataset.desc || "";
          }
          this._syncActionCount();
        });
      });
    }
    /* ─── wireRunActions ─────────────────────────────────────────── */
    wireRunActions() {
      const runBtn = document.querySelector("#cwpm-trash-run-actions");
      if (!runBtn) return;
      runBtn.addEventListener("click", () => {
        this.runBulkAction();
      });
    }
    /* ─── _syncActionCount ───────────────────────────────────────── */
    _syncActionCount() {
      const countBadge = document.querySelector("#cwpm-trash-actions-count");
      if (countBadge) {
        const count = this._selectedAction ? 1 : 0;
        countBadge.textContent = count;
        countBadge.hidden = count === 0;
      }
    }
    /* ─── setRowBusy ─────────────────────────────────────────────── */
    setRowBusy(row, busy) {
      if (!row) return;
      row.querySelectorAll("button").forEach((btn) => {
        btn.disabled = busy;
      });
      row.classList.toggle("cwpm-row--loading", busy);
    }
  };

  // assets/src/js/classes/LogManager.js
  var LogManager = class {
    constructor(ajaxClient, router) {
      this.ajaxClient = ajaxClient;
      this.router = router;
    }
    init() {
      const navBtn = document.querySelector("#cwpm-logs-nav");
      const logsView = document.querySelector("#cwpm-logs-view");
      const autoDeleteCb = document.querySelector("#cwpm-logs-auto-delete");
      if (!navBtn || !logsView) return;
      navBtn.addEventListener("click", () => {
        Helpers.clearNavActiveState();
        navBtn.classList.add("active");
        this.router.show("logs");
        this.loadLogs();
      });
      autoDeleteCb == null ? void 0 : autoDeleteCb.addEventListener("change", () => {
        this.saveAutoDelete(autoDeleteCb.checked);
      });
    }
    async saveAutoDelete(enabled) {
      try {
        await this.ajaxClient.post("cwpm_save_log_settings", {
          auto_delete_logs: enabled ? "1" : "0"
        });
      } catch (e) {
        console.error("Failed to save log auto-delete setting:", e);
      }
    }
    /* --- loadLogs ---------------------------------------------------- */
    async loadLogs() {
      const tbody = document.querySelector("#cwpm-logs-tbody");
      if (!tbody) return;
      tbody.innerHTML = '<tr><td colspan="4" class="cwpm-logs-view__loading"><span class="cwpm-row-spinner" aria-hidden="true"></span> Loading\u2026</td></tr>';
      try {
        const data = await this.ajaxClient.post("cwpm_log_list", {});
        this.renderTable(data.results || []);
        this.updateStats(data.results || []);
      } catch (e) {
        tbody.innerHTML = '<tr><td colspan="4" class="text-error">Failed to load logs.</td></tr>';
      }
    }
    /* --- renderTable ------------------------------------------------- */
    renderTable(logs) {
      const tbody = document.querySelector("#cwpm-logs-tbody");
      const emptyState = document.querySelector("#cwpm-logs-empty-state");
      const table = document.querySelector("#cwpm-logs-table");
      if (!tbody) return;
      if (logs.length === 0) {
        tbody.innerHTML = "";
        table == null ? void 0 : table.style.setProperty("display", "none");
        emptyState == null ? void 0 : emptyState.style.removeProperty("display");
        return;
      }
      table == null ? void 0 : table.style.removeProperty("display");
      emptyState == null ? void 0 : emptyState.style.setProperty("display", "none");
      tbody.innerHTML = logs.map((log) => {
        const dateFormatted = this._formatDate(log.date);
        return `<tr data-filename="${Helpers.escHtml(log.filename)}">
				<td class="cwpm-logs__date">${Helpers.escHtml(dateFormatted)}</td>
				<td class="text-muted">${Helpers.escHtml(log.filename)}</td>
				<td class="text-muted">${Helpers.escHtml(log.size_formatted)}</td>
				<td class="cwpm-logs__actions">
					<button type="button" class="cwpm-logs__action-btn cwpm-logs__action-btn--download"
						data-action="download" title="Download">
						<i class="fa-solid fa-download"></i>
					</button>
					<button type="button" class="cwpm-logs__action-btn cwpm-logs__action-btn--delete"
						data-action="delete" title="Delete">
						<i class="fa-solid fa-trash"></i>
					</button>
				</td>
			</tr>`;
      }).join("");
      tbody.querySelectorAll(".cwpm-logs__action-btn--download").forEach((btn) => {
        btn.addEventListener("click", () => {
          const row = btn.closest("tr");
          this.downloadLog(row.dataset.filename);
        });
      });
      tbody.querySelectorAll(".cwpm-logs__action-btn--delete").forEach((btn) => {
        btn.addEventListener("click", () => {
          const row = btn.closest("tr");
          this.deleteLog(row.dataset.filename, row);
        });
      });
    }
    
    updateStats(logs) {
      const countEl = document.querySelector("#cwpm-logs-count");
      const sizeEl = document.querySelector("#cwpm-logs-size");
      if (countEl) countEl.textContent = logs.length;
      if (sizeEl) {
        const totalBytes = logs.reduce((sum, log) => sum + log.size, 0);
        sizeEl.textContent = Helpers.formatSize(totalBytes);
      }
    }
    
    async downloadLog(filename) {
      try {
        const data = await this.ajaxClient.post("cwpm_log_download", { filename });
        const decoded = data.encoding === "base64" ? atob(data.contents) : data.contents;
        const bytes = Uint8Array.from(decoded, (c) => c.charCodeAt(0));
        const blob = new Blob([bytes], { type: "text/plain" });
        const url = URL.createObjectURL(blob);
        const a = document.createElement("a");
        a.href = url;
        a.download = data.filename;
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        URL.revokeObjectURL(url);
      } catch (e) {
        console.error("Failed to download log:", e);
      }
    }
    
    async deleteLog(filename, row) {
      if (row) {
        row.style.opacity = "0.5";
        row.style.pointerEvents = "none";
      }
      try {
        await this.ajaxClient.post("cwpm_log_delete", { filename });
        this.loadLogs();
      } catch (e) {
        if (row) {
          row.style.opacity = "";
          row.style.pointerEvents = "";
        }
        console.error("Failed to delete log:", e);
      }
    }
    /* --- _formatDate ------------------------------------------------- */
    _formatDate(dateStr) {
      if (!dateStr) return "";
      const d = /* @__PURE__ */ new Date(dateStr + "T00:00:00");
      if (isNaN(d.getTime())) return dateStr;
      return d.toLocaleDateString(void 0, {
        month: "short",
        day: "numeric",
        year: "numeric"
      });
    }
  };

  
  var ICONS = {
    supported: "fa-circle-check",
    partial: "fa-triangle-exclamation",
    unsupported: "fa-circle-xmark"
  };
  var STATE_MODIFIERS = {
    supported: "cwpm-compat-cell--supported",
    partial: "cwpm-compat-cell--partial",
    unsupported: "cwpm-compat-cell--unsupported"
  };
  var CompatNotice = class {
    


    constructor(ajaxClient) {
      this.ajax = ajaxClient;
      this.wrapper = document.getElementById("cwpm-compat-notice");
      this.grid = document.getElementById("cwpm-compat-notice-grid");
    }
    async init() {
      if (!this.wrapper || !this.grid) return;
      try {
        const res = await this.ajax.post("cwpm_compat_status");
        const plugins = res && res.plugins || [];
        if (!plugins.length) return;
        this._render(plugins);
        this.wrapper.style.display = "";
      } catch (err) {
        console.error("[CWPM] CompatNotice load failed:", err);
      }
    }
    _render(plugins) {
      this.grid.innerHTML = "";
      for (const p of plugins) {
        this.grid.appendChild(this._buildCell(p));
      }
    }
    /**
     * @param {{slug: string, name: string, label: string, state: string}} p
     */
    _buildCell(p) {
      const state = p.state || "supported";
      const modifier = STATE_MODIFIERS[state] || STATE_MODIFIERS.supported;
      const iconCls = ICONS[state] || ICONS.supported;
      const cell = document.createElement("div");
      cell.className = "cwpm-compat-cell " + modifier;
      cell.dataset.slug = p.slug;
      cell.dataset.state = state;
      const name = document.createElement("span");
      name.className = "cwpm-compat-cell__name";
      name.textContent = p.name;
      const status = document.createElement("span");
      status.className = "cwpm-compat-cell__status";
      const label = document.createElement("span");
      label.className = "cwpm-compat-cell__label";
      label.textContent = p.label;
      const icon = document.createElement("i");
      icon.className = "fa-solid " + iconCls + " cwpm-compat-cell__icon";
      icon.setAttribute("aria-hidden", "true");
      status.append(label, icon);
      cell.append(name, status);
      cell.dispatchEvent(new CustomEvent("cwpm:compat-cell-rendered", {
        bubbles: true,
        detail: { cell, plugin: p, iconEl: icon, labelEl: label }
      }));
      return cell;
    }
  };

  
  var App = class {
    constructor(config) {
      this.config = config;
      this.ajaxClient = new AjaxClient(config);
      this.router = null;
      this.wizard = null;
      this.thumbnailManager = null;
      this.mediaScanner = null;
      this.trashManager = null;
      this.logManager = null;
      this.compatNotice = null;
    }
    async init() {
      this.wizard = new StepWizard();
      this.wizard.init();
      this.thumbnailManager = new ThumbnailManager(this.ajaxClient);
      this.thumbnailManager.init();
      this.router = new ViewRouter();
      this.router.register("trash", document.querySelector("#cwpm-trash-view"));
      this.router.register("logs", document.querySelector("#cwpm-logs-view"));
      this.trashManager = new TrashManager(this.ajaxClient, this.router);
      this.trashManager.init();
      this.logManager = new LogManager(this.ajaxClient, this.router);
      this.logManager.init();
      this.compatNotice = new CompatNotice(this.ajaxClient);
      this.compatNotice.init();
      const scanNavBtn = document.querySelector("#cwpm-nav-scan");
      if (scanNavBtn) {
        scanNavBtn.addEventListener("click", () => {
          Helpers.clearNavActiveState();
          scanNavBtn.classList.add("active");
          if (this.router.activeView) {
            this.router.back(() => this.wizard.goTo(1));
          } else {
            this.wizard.goTo(1);
          }
        });
      }
      this.mediaScanner = new MediaScanner(this.wizard, this.ajaxClient);
      this.mediaScanner.init();
      const urlParams = new URLSearchParams(window.location.search);
      const initialView = urlParams.get("view");
      if (initialView) {
        const viewStepMap = { scan: 1, results: 3 };
        if (viewStepMap[initialView]) {
          const targetStep = viewStepMap[initialView];
          this.wizard.goTo(targetStep);
          document.dispatchEvent(new CustomEvent("cwpm:nav-step", { detail: { step: targetStep } }));
          Helpers.clearNavActiveState();
          scanNavBtn == null ? void 0 : scanNavBtn.classList.add("active");
        } else if (this.router.views.has(initialView)) {
          this.router.show(initialView);
          if (initialView === "trash") {
            this.trashManager.loadTrash();
          } else if (initialView === "logs") {
            this.logManager.loadLogs();
          }
        }
      } else {
        Helpers.clearNavActiveState();
        scanNavBtn == null ? void 0 : scanNavBtn.classList.add("active");
      }
    }
  };

  
  var $ = jquery_default;
  $(function() {
    const config = window.CWPMConfig || {};
    initThemeSwitcher(config);
    initApp(config);
  });
  function initThemeSwitcher(config) {
    if (typeof ThemeSwitcher === "undefined") {
      return;
    }
    if (!localStorage.getItem("cwpm-theme") && config.theme) {
      localStorage.setItem("cwpm-theme", config.theme);
    }
    const sw = new ThemeSwitcher({
      container: "#theme-switcher",
      root: ".color-punch-root",
      storageKey: "cwpm-theme"
    });
    sw.init();
    const root = document.querySelector(".color-punch-root");
    if (!root) {
      return;
    }
    const observer = new MutationObserver((mutations) => {
      for (const mutation of mutations) {
        if (mutation.attributeName === "data-theme") {
          const theme = root.getAttribute("data-theme");
          persistTheme(theme, config);
        }
      }
    });
    observer.observe(root, { attributes: true, attributeFilter: ["data-theme"] });
  }
  function persistTheme(theme, config) {
    fetch(config.ajaxUrl, {
      method: "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      body: new URLSearchParams({
        action: "cwpm_save_theme",
        nonce: config.nonce,
        theme
      })
    }).catch(() => {
    });
  }
  function initApp(config) {
    const app = new App(config);
    app.init().catch((err) => console.error("[CWPM] Failed to initialize app:", err));
    window.CWPMApp = app;
  }
})();
