Search StashDB with configurable columns, left-side adaptive images, fixed backend pagination, recent tag caches, maximize/minimize options, single-click tag page scraping, explicit subheadings, and fully clickable result cards.
// ==UserScript==
// @name StashDB Multi-Tag Searcher
// @namespace http://tampermonkey.net/
// @version 1.1
// @description Search StashDB with configurable columns, left-side adaptive images, fixed backend pagination, recent tag caches, maximize/minimize options, single-click tag page scraping, explicit subheadings, and fully clickable result cards.
// @author You
// @match https://stashdb.org/*
// @license Custom / Free Redistribution for Modifications Only (No Exact Mirroring)
// @grant GM_setValue
// @grant GM_getValue
// ==/UserScript==
(function() {
'use strict';
// Global tracking configuration variables for pagination routines
let currentActiveInputString = "";
let currentActivePage = 1;
let currentActiveResolvedLabels = ""; // Holds resolved string across pagination updates
const ITEMS_PER_PAGE = 20;
// Create main UI container - positioned ~1 inch from the top right
const searchBox = document.createElement('div');
searchBox.id = "stashdb-multi-tag-box";
searchBox.style = "position: fixed; top: 96px; right: 20px; background: #191c1f; color: #f8f9fa; padding: 15px; border-radius: 6px; z-index: 9999; border: 1px solid #343a40; width: 300px; box-shadow: 0 4px 12px rgba(0,0,0,0.5); font-family: sans-serif; transition: all 0.2s ease-in-out;";
searchBox.innerHTML = `
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
<h4 style="margin:0; font-size: 14px; text-transform: uppercase; letter-spacing: 0.5px; color: #00adb5;">Multi-Tag Search</h4>
<div style="display: flex; gap: 8px; align-items: center;">
<button id="btn-multi-config" style="background: none; border: none; color: #6c757d; cursor: pointer; font-size: 12px; transition: 0.2s;" onmouseover="this.style.color='#00adb5'" onmouseout="this.style.color='#6c757d'">⚙ Settings</button>
<button id="btn-multi-toggle" style="background: none; border: none; color: #6c757d; cursor: pointer; font-size: 14px; font-weight: bold; transition: 0.2s; padding: 0 4px;" onmouseover="this.style.color='#00adb5'" onmouseout="this.style.color='#6c757d'">_</button>
</div>
</div>
<div id="multi-search-content">
<div id="multi-config-drawer" style="display: none; background: #212529; padding: 10px; border: 1px solid #343a40; border-radius: 4px; margin-bottom: 10px;">
<label style="font-size: 11px; color: #a5b1c2; display: block; margin-bottom: 4px;">StashDB API Key (JWT Token):</label>
<input type="password" id="cfg-api-key" placeholder="Paste Token..." style="width: 100%; background: #191c1f; color: #fff; border: 1px solid #495057; padding: 4px; border-radius: 4px; font-size: 11px; box-sizing: border-box; margin-bottom: 8px;">
<label style="font-size: 11px; color: #a5b1c2; display: block; margin-bottom: 4px;">Max Grid Columns Layout:</label>
<select id="cfg-grid-cols" style="width: 100%; background: #191c1f; color: #fff; border: 1px solid #495057; padding: 4px; border-radius: 4px; font-size: 11px; box-sizing: border-box; margin-bottom: 10px;">
<option value="max">Max (Auto-Responsive Layout)</option>
<option value="1">1 Column Layout</option>
<option value="2">2 Columns Layout</option>
<option value="3">3 Columns Layout</option>
<option value="4">4 Columns Layout</option>
<option value="5">5 Columns Layout</option>
<option value="6">6 Columns Layout</option>
</select>
<button id="btn-save-cfg" style="width: 100%; background: #28a745; color: white; border: none; padding: 5px; cursor: pointer; border-radius: 4px; font-size: 11px; font-weight: bold;">Save Configurations</button>
</div>
<input type="text" id="multi-tags" placeholder="Paste Tag UUIDs (comma separated)" style="width: 100%; margin-bottom: 6px; background: #212529; color: #fff; border: 1px solid #495057; padding: 6px; border-radius: 4px; box-sizing: border-box; font-size: 12px;">
<button id="btn-grab-tag" style="width: 100%; margin-bottom: 10px; border: none; padding: 5px; border-radius: 4px; font-size: 11px; font-weight: bold; cursor: pointer; transition: 0.2s;">✚ Grab Active Tag</button>
<div style="display: flex; gap: 8px; margin-bottom: 8px;">
<button id="btn-multi-search" style="flex: 2; background: #00adb5; color: white; border: none; padding: 8px; cursor: pointer; border-radius: 4px; font-weight: bold; font-size: 13px;">Search Scenes</button>
<button id="btn-multi-clear" style="flex: 1; background: #495057; color: white; border: none; padding: 8px; cursor: pointer; border-radius: 4px; font-size: 13px;">Clear</button>
</div>
<div id="multi-status" style="font-size: 11px; color: #6c757d; margin-bottom: 12px; min-height: 14px; line-height: 1.3;"></div>
<div style="border-top: 1px solid #343a40; padding-top: 10px; margin-top: 5px; margin-bottom: 10px;">
<h5 style="margin: 0 0 8px 0; font-size: 12px; text-transform: uppercase; color: #6c757d; letter-spacing: 0.5px;">Recent Tags</h5>
<div id="multi-recent-tags" style="display: flex; flex-wrap: wrap; gap: 4px; max-height: 85px; overflow-y: auto;"></div>
</div>
<div style="border-top: 1px solid #343a40; padding-top: 10px; margin-top: 5px;">
<h5 style="margin: 0 0 8px 0; font-size: 12px; text-transform: uppercase; color: #6c757d; letter-spacing: 0.5px;">Last 5 Searches</h5>
<div id="multi-history" style="display: flex; flex-direction: column; gap: 4px; max-height: 150px; overflow-y: auto;"></div>
</div>
</div>
`;
document.body.appendChild(searchBox);
// Initializations
renderHistoryList();
renderRecentTagsList();
checkApiKeyStatus();
applyUIWindowState();
evaluateGrabButtonContext();
// Contextual Inspector: Watch URLs inside complex client-side Single Page App tracking frameworks
let lastKnownUrlPath = window.location.href;
setInterval(() => {
if (window.location.href !== lastKnownUrlPath) {
lastKnownUrlPath = window.location.href;
evaluateGrabButtonContext();
}
}, 1000);
// Contextual Evaluator Engine
function getActiveTagIdFromUrl() {
const matches = window.location.href.match(/stashdb\.org\/tags\/([a-f0-9\-]{36})/i);
return matches ? matches[1] : null;
}
function evaluateGrabButtonContext() {
const grabBtn = document.getElementById('btn-grab-tag');
if (!grabBtn) return;
const tagId = getActiveTagIdFromUrl();
if (tagId) {
grabBtn.disabled = false;
grabBtn.style.background = "#17a2b8";
grabBtn.style.color = "white";
grabBtn.style.cursor = "pointer";
grabBtn.style.opacity = "1";
grabBtn.title = `Extract current Tag UUID: ${tagId}`;
} else {
grabBtn.disabled = true;
grabBtn.style.background = "#2d3238";
grabBtn.style.color = "#6c757d";
grabBtn.style.cursor = "not-allowed";
grabBtn.style.opacity = "0.6";
grabBtn.title = "Navigate to an individual StashDB Tag page to activate this button.";
}
}
// Event Listener: Contextual Scraper Routine
document.getElementById('btn-grab-tag').addEventListener('click', async () => {
const tagId = getActiveTagIdFromUrl();
const userApiKey = checkApiKeyStatus();
if (!tagId || !userApiKey) return;
const statusDiv = document.getElementById('multi-status');
statusDiv.innerHTML = "Scraping active tag information...";
const inputField = document.getElementById('multi-tags');
let currentVal = inputField.value.trim();
if (currentVal === "") {
inputField.value = tagId;
} else {
const existingArray = currentVal.split(',').map(x => x.trim());
if (!existingArray.includes(tagId)) {
inputField.value = currentVal + ", " + tagId;
}
}
const graphqlQuery = {
query: `query FindTag($id: ID!) { findTag(id: $id) { name } }`,
variables: { id: tagId }
};
try {
const response = await fetch("https://stashdb.org/graphql", {
method: "POST",
headers: { "Content-Type": "application/json", "Accept": "application/json", "ApiKey": userApiKey },
body: JSON.stringify(graphqlQuery)
});
if (response.ok) {
const data = await response.json();
if (data?.data?.findTag?.name) {
const tagName = data.data.findTag.name;
saveToRecentTags([{ id: tagId, name: tagName }]);
statusDiv.innerHTML = `<span style='color:#28a745;'>Grabbed tag: <strong>${tagName}</strong></span>`;
return;
}
}
} catch (e) { console.error(e); }
saveToRecentTags([{ id: tagId, name: tagId.substring(0, 8) }]);
statusDiv.innerHTML = "<span style='color:#28a745;'>Grabbed tag alphanumeric identity hash.</span>";
});
// Event Listener: Minimize / Maximize UI panel Toggle
document.getElementById('btn-multi-toggle').addEventListener('click', () => {
const isCollapsed = sessionStorage.getItem('stashdb_ui_collapsed') === 'true';
sessionStorage.setItem('stashdb_ui_collapsed', !isCollapsed);
applyUIWindowState();
});
function applyUIWindowState() {
const content = document.getElementById('multi-search-content');
const toggleBtn = document.getElementById('btn-multi-toggle');
const isCollapsed = sessionStorage.getItem('stashdb_ui_collapsed') === 'true';
if (isCollapsed) {
content.style.display = 'none';
searchBox.style.width = '180px';
toggleBtn.innerText = '▢';
toggleBtn.title = 'Maximize Panel';
} else {
content.style.display = 'block';
searchBox.style.width = '300px';
toggleBtn.innerText = '_';
toggleBtn.title = 'Minimize Panel';
}
}
// Event Listener: Toggle Settings Drawer Display
document.getElementById('btn-multi-config').addEventListener('click', () => {
const drawer = document.getElementById('multi-config-drawer');
drawer.style.display = drawer.style.display === 'none' ? 'block' : 'none';
document.getElementById('cfg-api-key').value = GM_getValue("stashdb_user_apikey", "");
document.getElementById('cfg-grid-cols').value = GM_getValue("stashdb_grid_columns", "max");
});
// Event Listener: Save Options
document.getElementById('btn-save-cfg').addEventListener('click', () => {
const keyInput = document.getElementById('cfg-api-key').value.trim();
const colInput = document.getElementById('cfg-grid-cols').value;
GM_setValue("stashdb_user_apikey", keyInput);
GM_setValue("stashdb_grid_columns", colInput);
document.getElementById('multi-config-drawer').style.display = 'none';
document.getElementById('multi-status').innerHTML = "<span style='color: #28a745;'>Configurations saved!</span>";
evaluateGrabButtonContext();
});
// Event Listener: Execute Query
document.getElementById('btn-multi-search').addEventListener('click', () => {
const tagInputField = document.getElementById('multi-tags');
if (!tagInputField.value.trim()) return;
currentActiveInputString = tagInputField.value;
currentActivePage = 1;
currentActiveResolvedLabels = "";
const mainContentArea = document.getElementById('page-content') || document.querySelector('.main') || document.querySelector('.container-fluid');
if (mainContentArea) mainContentArea.innerHTML = "";
tagInputField.value = "";
executeMultiSearch();
});
// Event Listener: Clear field
document.getElementById('btn-multi-clear').addEventListener('click', () => {
document.getElementById('multi-tags').value = "";
document.getElementById('multi-status').innerHTML = "Input cleared.";
});
function checkApiKeyStatus() {
const activeKey = GM_getValue("stashdb_user_apikey", "");
if (!activeKey) {
document.getElementById('multi-status').innerHTML = "<span style='color: #ffc107; font-weight: 500;'>⚠️ API Key Required! Click 'Settings' above to paste your StashDB key before searching.</span>";
return false;
}
return activeKey;
}
async function resolveTagNames(tagIds, userApiKey) {
const resolvedObjects = [];
const labelNames = [];
for (const id of tagIds) {
const graphqlQuery = {
query: `query FindTag($id: ID!) { findTag(id: $id) { name } }`,
variables: { id: id }
};
try {
const response = await fetch("https://stashdb.org/graphql", {
method: "POST",
headers: { "Content-Type": "application/json", "Accept": "application/json", "ApiKey": userApiKey },
body: JSON.stringify(graphqlQuery)
});
if (response.ok) {
const data = await response.json();
if (data?.data?.findTag?.name) {
const tagName = data.data.findTag.name;
labelNames.push(tagName);
resolvedObjects.push({ id, name: tagName });
continue;
}
}
} catch (e) { console.error(e); }
labelNames.push(id.substring(0, 8));
resolvedObjects.push({ id, name: id.substring(0, 8) });
}
saveToRecentTags(resolvedObjects);
return labelNames.join(" + ");
}
function switchMultiSearchPage(targetPageNum) {
currentActivePage = targetPageNum;
executeMultiSearch();
window.scrollTo({ top: 0, behavior: 'smooth' });
}
// Core Logic Query Runner
async function executeMultiSearch() {
const userApiKey = checkApiKeyStatus();
if (!userApiKey) return;
const tagIds = currentActiveInputString.split(',').map(id => id.trim()).filter(id => id.length > 0);
const statusDiv = document.getElementById('multi-status');
if (tagIds.length === 0) {
statusDiv.innerHTML = "<span style='color: #ffc107;'>Please enter at least one UUID.</span>";
return;
}
statusDiv.innerHTML = `Loading Page ${currentActivePage}...`;
if (currentActivePage === 1 && !currentActiveResolvedLabels) {
currentActiveResolvedLabels = await resolveTagNames(tagIds, userApiKey);
saveToSearchHistory(currentActiveInputString, currentActiveResolvedLabels);
}
const graphqlQuery = {
query: `query QueryScenesByTags($input: SceneQueryInput!) {
queryScenes(input: $input) {
count
scenes {
id
title
date
images {
url
}
studio {
name
}
}
}
}`,
variables: {
input: {
page: currentActivePage,
per_page: ITEMS_PER_PAGE,
tags: {
value: tagIds,
modifier: "INCLUDES_ALL"
}
}
}
};
try {
const response = await fetch("https://stashdb.org/graphql", {
method: "POST",
headers: { "Content-Type": "application/json", "Accept": "application/json", "ApiKey": userApiKey },
body: JSON.stringify(graphqlQuery)
});
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
const data = await response.json();
if (data.errors) throw new Error(data.errors[0].message);
const scenes = data?.data?.queryScenes?.scenes || [];
const count = data?.data?.queryScenes?.count || 0;
const totalPages = Math.ceil(count / ITEMS_PER_PAGE);
const mainContentArea = document.getElementById('page-content') || document.querySelector('.main') || document.querySelector('.container-fluid');
if (!mainContentArea) throw new Error("Could not find layout container.");
statusDiv.innerHTML = `Displaying page ${currentActivePage} matches.`;
if (scenes.length === 0) {
mainContentArea.innerHTML = `
<div style="padding: 40px; text-align: center; color: #dc3545;">
<h3>No Cross-Referenced Scenes Found</h3>
<p>No scenes in StashDB match all of those specific tag UUIDs simultaneously.</p>
</div>`;
return;
}
const userColSetting = GM_getValue("stashdb_grid_columns", "max");
const gridTemplateColumns = userColSetting === "max"
? "repeat(auto-fill, minmax(320px, 1fr))"
: `repeat(${userColSetting}, minmax(0, 1fr))`;
let paginationHTML = "";
if (totalPages > 1) {
paginationHTML = `
<div style="display: flex; justify-content: center; align-items: center; gap: 10px; margin-top: 30px; padding: 20px 0; border-top: 1px solid #343a40;">
<button id="p-nav-first" ${currentActivePage === 1 ? 'disabled style="opacity:0.3; cursor:not-allowed;"' : ''} style="background: #212529; border: 1px solid #495057; color: white; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size:12px;">« First</button>
<button id="p-nav-prev" ${currentActivePage === 1 ? 'disabled style="opacity:0.3; cursor:not-allowed;"' : ''} style="background: #212529; border: 1px solid #495057; color: white; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size:12px;">‹ Prev</button>
<span style="color: #a5b1c2; font-size: 13px; font-weight: 500;">Page <strong>${currentActivePage}</strong> of <strong>${totalPages}</strong></span>
<button id="p-nav-next" ${currentActivePage === totalPages ? 'disabled style="opacity:0.3; cursor:not-allowed;"' : ''} style="background: #212529; border: 1px solid #495057; color: white; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size:12px;">Next ›</button>
<button id="p-nav-last" ${currentActivePage === totalPages ? 'disabled style="opacity:0.3; cursor:not-allowed;"' : ''} style="background: #212529; border: 1px solid #495057; color: white; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size:12px;">Last »</button>
</div>
`;
}
mainContentArea.innerHTML = `
<div style="padding: 20px;">
<div style="border-bottom: 1px solid #343a40; padding-bottom: 15px; margin-bottom: 20px;">
<div style="display: flex; justify-content: space-between; align-items: center;">
<h2 style="margin: 0; font-size: 24px; color: #fff;">Multi-Tag Search Results <span style="font-size: 16px; color: #6c757d;">(Showing ${scenes.length} of ${count} total items)</span></h2>
<button onclick="window.location.reload()" style="background: #343a40; color: white; border: 1px solid #495057; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 13px;">Reset Page</button>
</div>
<div style="margin-top: 8px; font-size: 14px; color: #a5b1c2; font-weight: 500; word-break: break-word;">
<span style="color: #00adb5; font-weight: bold; text-transform: uppercase; font-size: 12px; letter-spacing: 0.5px; margin-right: 4px;">Tags Included:</span> ${currentActiveResolvedLabels}
</div>
</div>
<div style="display: grid; grid-template-columns: ${gridTemplateColumns}; gap: 20px;">
${scenes.map(s => {
const studioName = s.studio ? s.studio.name : "Unknown Studio";
const releaseDate = s.date ? s.date : "Date Unknown";
const imageSrc = (s.images && s.images.length > 0) ? s.images[0].url : 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><rect width="100" height="100" fill="%232d3238"/></svg>';
return `
<a href="/scenes/${s.id}" target="_blank" class="search-result-card" style="text-decoration: none; display: block; color: inherit; transition: background-color 0.15s ease;">
<div style="background: #1f2327; border: 1px solid #2d3238; border-radius: 6px; overflow: hidden; display: flex; box-shadow: 0 2px 5px rgba(0,0,0,0.2); min-height: 110px;" onmouseover="this.style.backgroundColor='#2d3238';" onmouseout="this.style.backgroundColor='#1f2327';">
<div style="flex: 0 0 35%; max-width: 140px; background: #111; display: flex; align-items: center; justify-content: center; border-right: 1px solid #2d3238;">
<img src="${imageSrc}" style="width: 100%; height: 100%; object-fit: cover;" alt="">
</div>
<div style="flex: 1; padding: 12px; display: flex; flex-direction: column; justify-content: space-between; min-width: 0;">
<div>
<div style="font-size: 10px; text-transform: uppercase; color: #00adb5; font-weight: bold; margin-bottom: 4px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">${studioName}</div>
<div style="color: #f8f9fa; font-size: 14px; font-weight: 600; line-height: 1.3; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; margin-bottom: 4px;">
${s.title || 'Untitled Scene'}
</div>
</div>
<div style="font-size: 11px; color: #6c757d; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
<span>Released: ${releaseDate}</span>
</div>
</div>
</div>
</a>
`;
}).join('')}
</div>
${paginationHTML}
</div>
`;
if (totalPages > 1) {
if (currentActivePage !== 1) {
document.getElementById('p-nav-first').addEventListener('click', () => switchMultiSearchPage(1));
document.getElementById('p-nav-prev').addEventListener('click', () => switchMultiSearchPage(currentActivePage - 1));
}
if (currentActivePage !== totalPages) {
document.getElementById('p-nav-next').addEventListener('click', () => switchMultiSearchPage(currentActivePage + 1));
document.getElementById('p-nav-last').addEventListener('click', () => switchMultiSearchPage(totalPages));
}
}
} catch (error) {
console.error(error);
statusDiv.innerHTML = `<span style='color: #dc3545;'>Error loading.</span>`;
}
}
// Engine: Manage Recent Individual Tags Caches inside localStorage
function saveToRecentTags(newTagObjects) {
let tags = [];
try { tags = JSON.parse(localStorage.getItem('stashdb_recent_individual_tags')) || []; } catch(e) {}
newTagObjects.forEach(newTag => {
tags = tags.filter(t => t.id !== newTag.id);
tags.unshift(newTag);
});
if (tags.length > 20) tags = tags.slice(0, 20);
localStorage.setItem('stashdb_recent_individual_tags', JSON.stringify(tags));
renderRecentTagsList();
}
function renderRecentTagsList() {
const container = document.getElementById('multi-recent-tags');
if (!container) return;
let tags = [];
try { tags = JSON.parse(localStorage.getItem('stashdb_recent_individual_tags')) || []; } catch(e) {}
if (tags.length === 0) {
container.innerHTML = "<p style='color: #495057; margin: 0; font-size: 11px; font-style: italic;'>No tags recorded yet.</p>";
return;
}
container.innerHTML = tags.map(t => {
return `
<span class="recent-tag-pill" data-uuid="${t.id}" title="Click to add UUID: ${t.id}" style="display: inline-block; background: #212529; color: #a5b1c2; border: 1px solid #343a40; border-radius: 3px; padding: 2px 6px; font-size: 11px; cursor: pointer; transition: 0.15s; white-space: nowrap; max-width: 120px; overflow: hidden; text-overflow: ellipsis;" onmouseover="this.style.background='#00adb5'; this.style.color='#fff';" onmouseout="this.style.background='#212529'; this.style.color='#a5b1c2';">
${t.name}
</span>
`;
}).join('');
container.querySelectorAll('.recent-tag-pill').forEach(pill => {
pill.addEventListener('click', function() {
const uuid = this.getAttribute('data-uuid');
const inputField = document.getElementById('multi-tags');
let currentVal = inputField.value.trim();
if (currentVal === "") {
inputField.value = uuid;
} else {
const existingArray = currentVal.split(',').map(x => x.trim());
if (!existingArray.includes(uuid)) {
inputField.value = currentVal + ", " + uuid;
}
}
});
});
}
// Engine: History Stack Management
function saveToSearchHistory(queryStr, labelStr) {
let history = [];
try { history = JSON.parse(localStorage.getItem('stashdb_tag_search_history_v2')) || []; } catch(e) {}
history = history.filter(item => item.query !== queryStr);
history.unshift({ query: queryStr, label: labelStr });
if (history.length > 5) history.pop();
localStorage.setItem('stashdb_tag_search_history_v2', JSON.stringify(history));
renderHistoryList();
}
function renderHistoryList() {
const container = document.getElementById('multi-history');
let history = [];
try { history = JSON.parse(localStorage.getItem('stashdb_tag_search_history_v2')) || []; } catch(e) {}
if (history.length === 0) {
container.innerHTML = "<p style='color: #495057; margin: 0; font-size: 11px; font-style: italic;'>No search history yet.</p>";
return;
}
container.innerHTML = history.map((item, index) => {
const shortDisplay = item.label.length > 38 ? item.label.substring(0, 35) + "..." : item.label;
const borderStyle = index === 0
? "border: 1px solid #ffc107; box-shadow: 0 0 4px rgba(255,193,7,0.2);"
: "border: 1px solid #2d3238;";
return `
<div class="history-item" data-query="${encodeURIComponent(item.query)}" data-label="${encodeURIComponent(item.label)}" title="UUIDs: ${item.query}" style="color: #a5b1c2; font-size: 11px; cursor: pointer; padding: 4px 6px; background: #212529; border-radius: 3px; ${borderStyle} overflow: hidden; text-overflow: ellipsis; white-space: nowrap; transition: 0.2s;" onmouseover="this.style.background='#2d3238'; this.style.color='#00adb5';" onmouseout="this.style.background='#212529'; this.style.color='#a5b1c2';">
${index + 1}. ${shortDisplay}
</div>
`;
}).join('');
const items = container.querySelectorAll('.history-item');
items.forEach(el => {
el.addEventListener('click', function() {
const targets = decodeURIComponent(this.getAttribute('data-query'));
const label = decodeURIComponent(this.getAttribute('data-label'));
currentActiveInputString = targets;
currentActivePage = 1;
currentActiveResolvedLabels = label;
const mainContentArea = document.getElementById('page-content') || document.querySelector('.main') || document.querySelector('.container-fluid');
if (mainContentArea) mainContentArea.innerHTML = "";
document.getElementById('multi-tags').value = "";
executeMultiSearch();
});
});
}
})();