Files
ComfyAssets-ComfyUI_PromptM…/web/admin.html
T
Vito Sansevero b616a26df1 style: Phase 3 typography and spacing pass
- Reduce heading sizes: text-xl/2xl/3xl → text-sm font-semibold
- Tighten modal padding: p-6 → p-4, px-6 py-4 → px-4 py-2
- Compact empty states: py-12 → py-6, py-8 → py-4
- Reduce space-y-6 → space-y-3 in metadata panels
- Shrink stat card numbers: text-xl → text-lg
- Shrink decorative emojis: text-2xl/3xl → text-lg
- Keep lightbox nav buttons at text-xl for usability
2026-02-08 06:39:00 -08:00

1213 lines
74 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PromptManager - Admin Dashboard</title>
<script src="/prompt_manager/js/theme-bridge.js"></script>
<link rel="stylesheet" href="css/comfyui-theme.css">
<link rel="stylesheet" href="lib/tailwind/styles.css">
<link rel="stylesheet" href="lib/viewerjs/viewer.min.css">
<script src="lib/viewerjs/viewer.min.js"></script>
</head>
<body class="bg-pm-primary text-pm min-h-screen text-[13px]">
<div class="min-h-screen flex flex-col">
<!-- Header -->
<header class="bg-pm-surface border-b border-pm px-4 py-2" style="min-height: 2.5rem;">
<div class="max-w-7xl mx-auto">
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<h1 class="text-sm font-semibold text-pm">
PromptManager Admin
</h1>
</div>
<div class="hidden sm:flex items-center gap-1.5">
<div class="w-2 h-2 bg-pm-success rounded-full animate-pulse"></div>
<span class="text-pm-success text-xs">Online</span>
</div>
</div>
</div>
</header>
<!-- Dashboard View -->
<div id="dashboardView">
<!-- Stats Grid -->
<div class="px-4 py-3">
<div class="max-w-7xl mx-auto">
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3">
<div class="bg-pm-surface rounded-pm-lg p-3 border border-pm shadow-pm-sm">
<div class="flex items-center justify-between">
<div>
<p class="text-pm-secondary text-xs font-medium uppercase tracking-wide">Total Prompts</p>
<p class="text-lg font-bold text-pm-accent" id="totalPrompts">-</p>
</div>
</div>
</div>
<div class="bg-pm-surface rounded-pm-lg p-3 border border-pm shadow-pm-sm">
<div class="flex items-center justify-between">
<div>
<p class="text-pm-secondary text-xs font-medium uppercase tracking-wide">Categories</p>
<p class="text-lg font-bold text-pm" id="totalCategories">-</p>
</div>
</div>
</div>
<div class="bg-pm-surface rounded-pm-lg p-3 border border-pm shadow-pm-sm cursor-pointer hover:border-pm-accent transition-colors"
onclick="window.location.hash = '#/tags'" title="Click to manage tags">
<div class="flex items-center justify-between">
<div>
<p class="text-pm-secondary text-xs font-medium uppercase tracking-wide">Unique Tags</p>
<p class="text-lg font-bold text-pm-success" id="totalTags">-</p>
</div>
</div>
</div>
<div class="bg-pm-surface rounded-pm-lg p-3 border border-pm shadow-pm-sm">
<div class="flex items-center justify-between">
<div>
<p class="text-pm-secondary text-xs font-medium uppercase tracking-wide">Avg Rating</p>
<p class="text-lg font-bold text-pm-warning" id="avgRating">-</p>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Search Controls (Sticky) -->
<div class="sticky top-0 z-50 bg-pm-primary/95 backdrop-blur-sm border-b border-pm-subtle">
<div class="px-4 py-3">
<div class="max-w-7xl mx-auto">
<div class="bg-pm-surface rounded-pm-lg p-4 border border-pm">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-6 gap-3 mb-3">
<div class="lg:col-span-2">
<label class="block text-xs font-medium text-pm-secondary mb-1 uppercase tracking-wide">Search Text</label>
<input type="text" id="searchText" placeholder="Search prompt content..."
class="w-full px-2.5 py-1.5 bg-pm-input border border-pm rounded-pm-sm text-pm text-[13px] placeholder:text-pm-muted focus:outline-none">
</div>
<div>
<label class="block text-xs font-medium text-pm-secondary mb-1 uppercase tracking-wide">Category</label>
<select id="searchCategory"
class="w-full px-2.5 py-1.5 bg-pm-input border border-pm rounded-pm-sm text-pm text-[13px] focus:outline-none">
<option value="">All Categories</option>
</select>
</div>
<div>
<label class="block text-xs font-medium text-pm-secondary mb-1 uppercase tracking-wide">Tags</label>
<input type="text" id="searchTags" placeholder="tag1, tag2..."
class="w-full px-2.5 py-1.5 bg-pm-input border border-pm rounded-pm-sm text-pm text-[13px] placeholder:text-pm-muted focus:outline-none">
</div>
<div>
<label class="block text-xs font-medium text-pm-secondary mb-1 uppercase tracking-wide">Sort By</label>
<select id="sortBy"
class="w-full px-2.5 py-1.5 bg-pm-input border border-pm rounded-pm-sm text-pm text-[13px] focus:outline-none">
<option value="created_desc">Newest First</option>
<option value="created_asc">Oldest First</option>
<option value="rating_desc">Highest Rated</option>
<option value="rating_asc">Lowest Rated</option>
<option value="text_asc">A-Z</option>
<option value="text_desc">Z-A</option>
</select>
</div>
<div class="flex items-end">
<button id="searchBtn"
class="w-full bg-pm-accent hover:bg-pm-accent-hover text-pm-accent-fg font-medium py-1.5 px-3 rounded-pm-sm text-[13px] transition-colors flex items-center justify-center gap-1.5">
<span>Search</span>
</button>
</div>
</div>
<!-- Bulk Actions -->
<div class="flex flex-wrap items-center gap-2 pt-3 border-t border-pm">
<label class="flex items-center gap-1.5 text-pm">
<input type="checkbox" id="selectAll" class="w-3.5 h-3.5 accent-[var(--pm-accent)]">
<span class="text-xs font-medium">Select All</span>
</label>
<button id="bulkDeleteBtn" disabled
class="px-2.5 py-1 text-pm-error bg-transparent hover:bg-pm-error-tint disabled:opacity-25 disabled:cursor-not-allowed text-xs font-medium rounded-pm-sm transition-colors border border-transparent hover:border-pm">
Delete Selected
</button>
<button id="bulkTagBtn" disabled
class="px-2.5 py-1 bg-pm-surface border border-pm hover:bg-pm-hover disabled:opacity-25 disabled:cursor-not-allowed text-pm text-xs font-medium rounded-pm-sm transition-colors">
Add Tags
</button>
<button id="bulkCategoryBtn" disabled
class="px-2.5 py-1 bg-pm-surface border border-pm hover:bg-pm-hover disabled:opacity-25 disabled:cursor-not-allowed text-pm text-xs font-medium rounded-pm-sm transition-colors">
Set Category
</button>
<div class="w-px h-4 bg-pm-border mx-1"></div>
<button id="addPromptBtn"
class="px-2.5 py-1 bg-pm-accent hover:bg-pm-accent-hover text-pm-accent-fg text-xs font-medium rounded-pm-sm transition-colors">
Add Prompt
</button>
<button id="statsBtn"
class="px-2.5 py-1 bg-pm-surface border border-pm hover:bg-pm-hover text-pm text-xs font-medium rounded-pm-sm transition-colors">
Stats
</button>
<button id="exportBtn"
class="px-2.5 py-1 bg-pm-surface border border-pm hover:bg-pm-hover text-pm text-xs font-medium rounded-pm-sm transition-colors">
Export
</button>
<button id="settingsBtn"
class="px-2.5 py-1 bg-pm-surface border border-pm hover:bg-pm-hover text-pm text-xs font-medium rounded-pm-sm transition-colors">
Settings
</button>
<button id="diagnosticsBtn"
class="px-2.5 py-1 bg-pm-surface border border-pm hover:bg-pm-hover text-pm text-xs font-medium rounded-pm-sm transition-colors">
Diagnostics
</button>
<button id="maintenanceBtn"
class="px-2.5 py-1 bg-pm-surface border border-pm hover:bg-pm-hover text-pm text-xs font-medium rounded-pm-sm transition-colors">
Maintenance
</button>
<button id="backupBtn"
class="px-2.5 py-1 bg-pm-surface border border-pm hover:bg-pm-hover text-pm text-xs font-medium rounded-pm-sm transition-colors">
Backup DB
</button>
<button id="restoreBtn"
class="px-2.5 py-1 bg-pm-surface border border-pm hover:bg-pm-hover text-pm text-xs font-medium rounded-pm-sm transition-colors">
Restore DB
</button>
<button id="scanBtn"
class="px-2.5 py-1 bg-pm-surface border border-pm hover:bg-pm-hover text-pm text-xs font-medium rounded-pm-sm transition-colors">
Scan Images
</button>
<button id="autoTagBtn"
class="px-2.5 py-1 bg-pm-surface border border-pm hover:bg-pm-hover text-pm text-xs font-medium rounded-pm-sm transition-colors">
Auto Tag
</button>
<button id="logsBtn"
class="px-2.5 py-1 bg-pm-surface border border-pm hover:bg-pm-hover text-pm text-xs font-medium rounded-pm-sm transition-colors">
View Logs
</button>
<button id="metadataBtn"
class="px-2.5 py-1 bg-pm-surface border border-pm hover:bg-pm-hover text-pm text-xs font-medium rounded-pm-sm transition-colors">
Metadata
</button>
<button id="galleryBtn"
class="px-2.5 py-1 bg-pm-surface border border-pm hover:bg-pm-hover text-pm text-xs font-medium rounded-pm-sm transition-colors">
Gallery
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Results Section -->
<div class="flex-1 px-4 pb-4">
<div class="max-w-7xl mx-auto h-full">
<div class="bg-pm-surface rounded-pm-lg border border-pm h-full flex flex-col">
<!-- Results Header -->
<div class="flex items-center justify-between p-3 border-b border-pm">
<div class="flex items-center gap-3">
<h2 class="text-[13px] font-semibold text-pm" id="resultsTitle">Recent Prompts</h2>
<div class="flex items-center gap-1.5">
<label class="text-xs text-pm-secondary">Show:</label>
<select id="limitSelector" class="px-2 py-0.5 bg-pm-input border border-pm rounded-pm-sm text-pm text-xs">
<option value="25">25</option>
<option value="50" selected>50</option>
<option value="100">100</option>
<option value="200">200</option>
<option value="500">500</option>
</select>
<span class="text-xs text-pm-secondary">per page</span>
</div>
</div>
<span class="text-xs font-medium text-pm-secondary bg-pm-input px-2 py-0.5 rounded-full" id="resultsCount">Loading...</span>
</div>
<!-- Loading State -->
<div id="loadingState" class="flex-1 flex items-center justify-center">
<div class="text-center">
<div class="w-8 h-8 border-3 border-pm-accent/30 border-t-pm-accent rounded-full animate-spin mx-auto mb-3"></div>
<p class="text-pm-secondary text-xs">Loading prompts...</p>
</div>
</div>
<!-- Results List -->
<div id="resultsList" class="flex-1 overflow-y-auto p-3 space-y-2 hidden">
</div>
<!-- Pagination Controls -->
<div id="paginationControls" class="border-t border-pm p-3 hidden">
<div class="flex items-center justify-between">
<div class="text-xs text-pm-secondary">
Showing <span id="showingStart">1</span> to <span id="showingEnd">50</span> of <span id="totalResults">0</span> prompts
</div>
<div class="flex items-center gap-1.5">
<button id="firstPageBtn" class="px-2 py-0.5 bg-pm-input hover:bg-pm-hover text-pm rounded-pm-sm disabled:opacity-25 disabled:cursor-not-allowed text-xs">
First
</button>
<button id="prevPageBtn" class="px-2 py-0.5 bg-pm-input hover:bg-pm-hover text-pm rounded-pm-sm disabled:opacity-25 disabled:cursor-not-allowed text-xs">
Previous
</button>
<span class="text-xs text-pm-secondary">
Page <span id="currentPage">1</span> of <span id="totalPages">1</span>
</span>
<button id="nextPageBtn" class="px-2 py-0.5 bg-pm-input hover:bg-pm-hover text-pm rounded-pm-sm disabled:opacity-25 disabled:cursor-not-allowed text-xs">
Next
</button>
<button id="lastPageBtn" class="px-2 py-0.5 bg-pm-input hover:bg-pm-hover text-pm rounded-pm-sm disabled:opacity-25 disabled:cursor-not-allowed text-xs">
Last
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div><!-- /dashboardView -->
<!-- Tags Page View -->
<div id="tagsPageView" class="hidden flex-1 flex flex-col">
<!-- Tags Header -->
<div class="px-4 py-2 border-b border-pm-subtle bg-pm-surface/50">
<div class="max-w-7xl mx-auto flex items-center justify-between">
<div class="flex items-center gap-3">
<button onclick="window.location.hash=''" class="text-pm-secondary hover:text-pm transition-colors text-xs flex items-center gap-1">
<span>&larr;</span><span>Dashboard</span>
</button>
<h2 class="text-[13px] font-semibold text-pm">Tags</h2>
<span id="tagsTotalBadge" class="text-xs font-medium text-pm-secondary bg-pm-input px-2 py-0.5 rounded-full">0 tags</span>
</div>
<div id="filterModeToggle" class="hidden items-center gap-1.5">
<span class="text-xs text-pm-secondary">Filter:</span>
<button id="filterModeAnd" class="px-2 py-0.5 text-xs font-medium rounded-pm-sm bg-pm-accent text-pm-accent-fg">AND</button>
<button id="filterModeOr" class="px-2 py-0.5 text-xs font-medium rounded-pm-sm bg-pm-input text-pm hover:bg-pm-hover">OR</button>
</div>
</div>
</div>
<!-- Two-Panel Layout -->
<div class="flex-1 flex overflow-hidden">
<!-- Left Panel: Tag List -->
<div class="w-72 flex-shrink-0 border-r border-pm-subtle flex flex-col bg-pm-surface/30">
<!-- Tag Search & Sort -->
<div class="p-3 space-y-2 border-b border-pm-subtle">
<input type="text" id="tagSearchInput" placeholder="Search tags..."
class="w-full px-2.5 py-1.5 bg-pm-input border border-pm rounded-pm-sm text-pm text-[13px] focus:outline-none">
<select id="tagSortSelect" class="w-full px-2.5 py-1.5 bg-pm-input border border-pm rounded-pm-sm text-pm text-[13px]">
<option value="alpha_asc">A &rarr; Z</option>
<option value="alpha_desc">Z &rarr; A</option>
<option value="count_desc">Most Used</option>
<option value="count_asc">Least Used</option>
</select>
</div>
<!-- Tag List -->
<div id="tagsListContainer" class="flex-1 overflow-y-auto">
<div id="tagsList" class="p-1.5 space-y-0.5"></div>
<div id="tagsLoader" class="text-center">
<div class="tags-loader-spinner p-3 hidden">
<div class="w-5 h-5 border-2 border-pm-accent/30 border-t-pm-accent rounded-full animate-spin mx-auto"></div>
</div>
</div>
</div>
</div>
<!-- Right Panel: Prompts Display -->
<div class="flex-1 flex flex-col overflow-hidden">
<!-- Active Tags Pills -->
<div id="activeTagsPills" class="px-4 py-2 border-b border-pm-subtle hidden">
<div class="flex flex-wrap gap-1.5 items-center">
<span class="text-xs text-pm-secondary mr-0.5">Filtering:</span>
<div id="tagPillsContainer" class="flex flex-wrap gap-1.5"></div>
<span id="tagPromptsCountBadge" class="text-xs text-pm-muted ml-auto"></span>
</div>
</div>
<!-- Prompts Grid -->
<div id="tagPromptsContainer" class="flex-1 overflow-y-auto">
<div id="tagPromptsGrid" class="p-4 grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-3"></div>
<div id="tagPromptsLoader" class="text-center">
<div class="prompts-loader-spinner p-3 hidden">
<div class="w-5 h-5 border-2 border-pm-accent/30 border-t-pm-accent rounded-full animate-spin mx-auto"></div>
</div>
<button id="loadMorePromptsBtn" class="hidden px-3 py-1.5 my-2 text-xs text-pm-secondary hover:text-pm-accent border border-pm hover:border-pm-accent rounded-pm-sm transition-colors">
Load more prompts
</button>
</div>
</div>
<!-- Empty State -->
<div id="tagPromptsEmpty" class="flex-1 flex items-center justify-center">
<div class="text-center text-pm-muted">
<p class="text-sm font-medium">Select a tag to view prompts</p>
<p class="text-xs mt-1">Click a tag from the list or use checkboxes for multi-tag filtering</p>
</div>
</div>
</div>
</div>
<!-- Context Menu for Tag Operations -->
<div id="tagContextMenu" class="hidden fixed z-50 bg-pm-surface border border-pm rounded-pm-md shadow-pm py-1 min-w-[160px]">
</div>
</div><!-- /tagsPageView -->
</div>
<!-- Settings Modal -->
<div id="settingsModal" class="fixed inset-0 bg-black/60 backdrop-blur-[2px] hidden items-center justify-center z-50">
<div class="bg-pm-surface rounded-pm-lg p-4 max-w-lg w-full mx-4 border border-pm max-h-[90vh] overflow-y-auto shadow-pm">
<h3 class="text-sm font-semibold text-pm mb-4">Settings</h3>
<div class="space-y-4">
<!-- Gallery Settings Section -->
<div class="border-b border-pm pb-3">
<h4 class="text-xs font-semibold text-pm-secondary mb-3 uppercase tracking-wide">Gallery Settings</h4>
<div class="mb-3">
<label class="block text-xs font-medium text-pm-secondary mb-1">Image Scan Directory</label>
<input type="text" id="galleryRootPath" placeholder="Leave empty for auto-detect"
class="w-full px-2.5 py-1.5 bg-pm-input border border-pm rounded-pm-sm text-pm text-[13px] focus:outline-none">
<p class="text-xs text-pm-muted mt-1">Custom path to scan for images. Leave empty to use ComfyUI's default output directory.</p>
</div>
<div class="mb-3">
<div class="flex items-center justify-between">
<label class="block text-xs font-medium text-pm-secondary">Currently Monitoring</label>
<button id="refreshMonitoringStatus" class="text-xs text-pm-accent hover:text-pm-accent-hover">Refresh</button>
</div>
<div id="monitoringStatus" class="mt-1.5 px-2.5 py-1.5 bg-pm-input rounded-pm-sm text-xs text-pm-secondary font-mono">
Loading...
</div>
</div>
</div>
<!-- UI Settings Section -->
<div class="border-b border-pm pb-3">
<h4 class="text-xs font-semibold text-pm-secondary mb-3 uppercase tracking-wide">UI Settings</h4>
<div class="mb-3">
<label class="block text-xs font-medium text-pm-secondary mb-1">Search Results Auto-Hide (seconds)</label>
<input type="number" id="resultTimeout" min="0" max="300" value="30"
class="w-full px-2.5 py-1.5 bg-pm-input border border-pm rounded-pm-sm text-pm text-[13px] focus:outline-none">
<p class="text-xs text-pm-muted mt-1">Set to 0 to disable auto-hide.</p>
</div>
<div>
<label class="block text-xs font-medium text-pm-secondary mb-1">Web UI Display Mode</label>
<select id="webuiDisplayMode"
class="w-full px-2.5 py-1.5 bg-pm-input border border-pm rounded-pm-sm text-pm text-[13px] focus:outline-none">
<option value="popup">Popup Window (Current)</option>
<option value="newtab">New Tab</option>
</select>
<p class="text-xs text-pm-muted mt-1">Choose how the Web UI opens from ComfyUI nodes.</p>
</div>
</div>
</div>
<div class="flex justify-end gap-2 mt-4">
<button id="cancelSettings"
class="px-3 py-1.5 bg-pm-surface border border-pm hover:bg-pm-hover text-pm text-xs font-medium rounded-pm-sm transition-colors">
Cancel
</button>
<button id="saveSettings"
class="px-3 py-1.5 bg-pm-accent hover:bg-pm-accent-hover text-pm-accent-fg text-xs font-medium rounded-pm-sm transition-colors">
Save Settings
</button>
</div>
</div>
</div>
<!-- Bulk Tag Modal -->
<div id="bulkTagModal" class="fixed inset-0 bg-black/60 backdrop-blur-[2px] hidden items-center justify-center z-50">
<div class="bg-pm-surface rounded-pm-lg p-4 max-w-md w-full mx-4 border border-pm shadow-pm">
<h3 class="text-sm font-semibold text-pm mb-4">Add Tags to Selected Prompts</h3>
<div>
<label class="block text-xs font-medium text-pm-secondary mb-1">Tags (comma-separated)</label>
<input type="text" id="bulkTagInput" placeholder="tag1, tag2, tag3..."
class="w-full px-2.5 py-1.5 bg-pm-input border border-pm rounded-pm-sm text-pm text-[13px] placeholder:text-pm-muted focus:outline-none">
</div>
<div class="flex justify-end gap-2 mt-4">
<button id="cancelBulkTag"
class="px-3 py-1.5 bg-pm-surface border border-pm hover:bg-pm-hover text-pm text-xs font-medium rounded-pm-sm transition-colors">
Cancel
</button>
<button id="confirmBulkTag"
class="px-3 py-1.5 bg-pm-accent hover:bg-pm-accent-hover text-pm-accent-fg text-xs font-medium rounded-pm-sm transition-colors">
Add Tags
</button>
</div>
</div>
</div>
<!-- Individual Tag Modal -->
<div id="individualTagModal" class="fixed inset-0 bg-black/60 backdrop-blur-[2px] hidden items-center justify-center z-50">
<div class="bg-pm-surface rounded-pm-lg p-4 max-w-md w-full mx-4 border border-pm shadow-pm">
<h3 class="text-sm font-semibold text-pm mb-4">Add Tags to Prompt</h3>
<div>
<label class="block text-xs font-medium text-pm-secondary mb-1">Tags (comma-separated)</label>
<input type="text" id="individualTagInput" placeholder="tag1, tag2, tag3..."
class="w-full px-2.5 py-1.5 bg-pm-input border border-pm rounded-pm-sm text-pm text-[13px] placeholder:text-pm-muted focus:outline-none">
</div>
<div class="flex justify-end gap-2 mt-4">
<button id="cancelIndividualTag"
class="px-3 py-1.5 bg-pm-surface border border-pm hover:bg-pm-hover text-pm text-xs font-medium rounded-pm-sm transition-colors">
Cancel
</button>
<button id="confirmIndividualTag"
class="px-3 py-1.5 bg-pm-accent hover:bg-pm-accent-hover text-pm-accent-fg text-xs font-medium rounded-pm-sm transition-colors">
Add Tags
</button>
</div>
</div>
</div>
<!-- Bulk Category Modal -->
<div id="bulkCategoryModal" class="fixed inset-0 bg-black/60 backdrop-blur-[2px] hidden items-center justify-center z-50">
<div class="bg-pm-surface rounded-pm-lg p-4 max-w-md w-full mx-4 border border-pm shadow-pm">
<h3 class="text-sm font-semibold text-pm mb-4">Set Category for Selected Prompts</h3>
<div>
<label class="block text-xs font-medium text-pm-secondary mb-1">Category</label>
<input type="text" id="bulkCategoryInput" placeholder="Enter category name..."
class="w-full px-2.5 py-1.5 bg-pm-input border border-pm rounded-pm-sm text-pm text-[13px] placeholder:text-pm-muted focus:outline-none">
</div>
<div class="flex justify-end gap-2 mt-4">
<button id="cancelBulkCategory"
class="px-3 py-1.5 bg-pm-surface border border-pm hover:bg-pm-hover text-pm text-xs font-medium rounded-pm-sm transition-colors">
Cancel
</button>
<button id="confirmBulkCategory"
class="px-3 py-1.5 bg-pm-accent hover:bg-pm-accent-hover text-pm-accent-fg text-xs font-medium rounded-pm-sm transition-colors">
Set Category
</button>
</div>
</div>
</div>
<!-- Database Restore Modal -->
<div id="restoreModal" class="fixed inset-0 bg-black/60 backdrop-blur-[2px] hidden items-center justify-center z-50">
<div class="bg-pm-surface rounded-pm-lg p-4 max-w-md w-full mx-4 border border-pm shadow-pm">
<h3 class="text-sm font-semibold text-pm mb-4">Restore Database</h3>
<div class="mb-3">
<div class="bg-pm-warning-tint border border-pm-warning/40 rounded-pm-md p-3 mb-3">
<h4 class="text-pm-warning font-medium text-xs">Warning</h4>
<p class="text-pm text-xs mt-1">This will replace your current database. A backup will be created automatically.</p>
</div>
<label class="block text-xs font-medium text-pm-secondary mb-1">Select Database File (.db)</label>
<input type="file" id="restoreFileInput" accept=".db"
class="w-full px-2.5 py-1.5 bg-pm-input border border-pm rounded-pm-sm text-pm text-[13px] file:mr-3 file:py-1 file:px-3 file:rounded-pm-sm file:border-0 file:text-xs file:font-medium file:bg-pm-accent file:text-pm-accent-fg">
</div>
<div class="flex justify-end gap-2 mt-4">
<button id="cancelRestore"
class="px-3 py-1.5 bg-pm-surface border border-pm hover:bg-pm-hover text-pm text-xs font-medium rounded-pm-sm transition-colors">
Cancel
</button>
<button id="confirmRestore"
class="px-3 py-1.5 bg-pm-warning hover:opacity-80 text-black text-xs font-medium rounded-pm-sm transition-colors">
Restore Database
</button>
</div>
</div>
</div>
<!-- Diagnostics Modal -->
<div id="diagnosticsModal" class="fixed inset-0 bg-black/60 backdrop-blur-[2px] hidden items-center justify-center z-50">
<div class="bg-pm-surface rounded-pm-lg max-w-4xl w-full max-h-[90vh] overflow-hidden mx-4 border border-pm shadow-pm">
<div class="p-4 border-b border-pm">
<div class="flex items-center justify-between">
<h3 class="text-sm font-semibold text-pm">Gallery System Diagnostics</h3>
<button onclick="window.admin.closeDiagnostics()" class="p-1 rounded-pm-sm hover:bg-pm-hover transition-colors">
<svg class="w-4 h-4 text-pm-secondary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
</div>
<div class="p-4 overflow-y-auto max-h-[calc(90vh-120px)]">
<div class="flex items-center justify-between mb-3">
<button id="runDiagnosticsBtn" class="px-2.5 py-1.5 bg-pm-accent hover:bg-pm-accent-hover text-pm-accent-fg text-xs font-medium rounded-pm-sm transition-colors">
Run Diagnostics
</button>
<button id="testImageLinkBtn" class="px-2.5 py-1.5 bg-pm-surface border border-pm hover:bg-pm-hover text-pm text-xs font-medium rounded-pm-sm transition-colors">
Test Image Link
</button>
</div>
<div id="diagnosticsContent" class="space-y-3">
<div class="text-center py-6 text-pm-secondary text-xs">
Click "Run Diagnostics" to check the gallery system status
</div>
</div>
</div>
</div>
</div>
<!-- Maintenance Modal -->
<div id="maintenanceModal" class="fixed inset-0 bg-black/60 backdrop-blur-[2px] hidden items-center justify-center z-50">
<div class="bg-pm-surface rounded-pm-lg max-w-4xl w-full max-h-[90vh] overflow-hidden mx-4 border border-pm shadow-pm">
<div class="p-4 border-b border-pm">
<div class="flex items-center justify-between">
<h3 class="text-sm font-semibold text-pm">Database Maintenance</h3>
<button onclick="window.admin.closeMaintenance()" class="p-1 rounded-pm-sm hover:bg-pm-hover transition-colors">
<svg class="w-4 h-4 text-pm-secondary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
</div>
<div class="p-4 overflow-y-auto max-h-[calc(90vh-120px)]">
<!-- Maintenance Options -->
<div class="space-y-3 mb-4">
<h4 class="text-xs font-semibold text-pm-secondary uppercase tracking-wide mb-3">Select Maintenance Operations:</h4>
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
<label class="flex items-center gap-2.5 p-3 bg-pm-input rounded-pm-md hover:bg-pm-hover cursor-pointer">
<input type="checkbox" id="cleanup_duplicates" class="maintenance-option w-3.5 h-3.5 accent-[var(--pm-accent)]" checked>
<div class="flex-1">
<div class="text-pm text-xs font-medium">Remove Duplicates</div>
<div class="text-[11px] text-pm-secondary">Find and remove duplicate prompts based on text content</div>
</div>
</label>
<label class="flex items-center gap-2.5 p-3 bg-pm-input rounded-pm-md hover:bg-pm-hover cursor-pointer">
<input type="checkbox" id="vacuum" class="maintenance-option w-3.5 h-3.5 accent-[var(--pm-accent)]" checked>
<div class="flex-1">
<div class="text-pm text-xs font-medium">Vacuum Database</div>
<div class="text-[11px] text-pm-secondary">Optimize database file size and performance</div>
</div>
</label>
<label class="flex items-center gap-2.5 p-3 bg-pm-input rounded-pm-md hover:bg-pm-hover cursor-pointer">
<input type="checkbox" id="cleanup_orphaned_images" class="maintenance-option w-3.5 h-3.5 accent-[var(--pm-accent)]" checked>
<div class="flex-1">
<div class="text-pm text-xs font-medium">Clean Orphaned Images</div>
<div class="text-[11px] text-pm-secondary">Remove image records for files that no longer exist</div>
</div>
</label>
<label class="flex items-center gap-2.5 p-3 bg-pm-input rounded-pm-md hover:bg-pm-hover cursor-pointer">
<input type="checkbox" id="check_hash_duplicates" class="maintenance-option w-3.5 h-3.5 accent-[var(--pm-accent)]">
<div class="flex-1">
<div class="text-pm text-xs font-medium">Check Hash Duplicates</div>
<div class="text-[11px] text-pm-secondary">Identify prompts with duplicate hash values</div>
</div>
</label>
<label class="flex items-center gap-2.5 p-3 bg-pm-input rounded-pm-md hover:bg-pm-hover cursor-pointer">
<input type="checkbox" id="check_consistency" class="maintenance-option w-3.5 h-3.5 accent-[var(--pm-accent)]">
<div class="flex-1">
<div class="text-pm text-xs font-medium">Consistency Check</div>
<div class="text-[11px] text-pm-secondary">Verify database integrity and data consistency</div>
</div>
</label>
<label class="flex items-center gap-2.5 p-3 bg-pm-input rounded-pm-md hover:bg-pm-hover cursor-pointer">
<input type="checkbox" id="statistics" class="maintenance-option w-3.5 h-3.5 accent-[var(--pm-accent)]">
<div class="flex-1">
<div class="text-pm text-xs font-medium">Database Statistics</div>
<div class="text-[11px] text-pm-secondary">Generate detailed database statistics report</div>
</div>
</label>
<label class="flex items-center gap-2.5 p-3 bg-pm-input rounded-pm-md hover:bg-pm-hover cursor-pointer">
<input type="checkbox" id="prune_orphaned_prompts" class="maintenance-option w-3.5 h-3.5 accent-[var(--pm-accent)]">
<div class="flex-1">
<div class="text-pm text-xs font-medium">Prune Orphaned Prompts</div>
<div class="text-[11px] text-pm-secondary">Remove prompts that have no linked images</div>
</div>
</label>
</div>
</div>
<!-- Action Buttons -->
<div class="flex items-center justify-between mb-4">
<div class="flex gap-2">
<button id="runMaintenanceBtn" class="px-3 py-1.5 bg-pm-accent hover:bg-pm-accent-hover text-pm-accent-fg text-xs font-medium rounded-pm-sm transition-colors">
Run Maintenance
</button>
<button id="selectAllMaintenanceBtn" class="px-2.5 py-1.5 bg-pm-surface border border-pm hover:bg-pm-hover text-pm text-xs font-medium rounded-pm-sm transition-colors">
Select All
</button>
<button id="clearAllMaintenanceBtn" class="px-2.5 py-1.5 bg-pm-surface border border-pm hover:bg-pm-hover text-pm text-xs font-medium rounded-pm-sm transition-colors">
Clear All
</button>
</div>
</div>
<!-- Results -->
<div id="maintenanceResults" class="space-y-3">
<div class="text-center py-6 text-pm-secondary text-xs">
Click "Run Maintenance" to perform selected operations
</div>
</div>
</div>
</div>
</div>
<!-- Gallery Modal -->
<div id="galleryModal" class="fixed inset-0 bg-black/75 backdrop-blur-[2px] hidden items-center justify-center z-50">
<div class="bg-pm-surface rounded-pm-lg max-w-6xl w-full max-h-[90vh] overflow-hidden mx-4 border border-pm shadow-pm">
<div class="p-4 border-b border-pm">
<div class="flex items-center justify-between">
<h3 class="text-sm font-semibold text-pm" id="galleryTitle">Generated Images</h3>
<button onclick="window.admin.closeGallery()" class="p-1 rounded-pm-sm hover:bg-pm-hover transition-colors">
<svg class="w-4 h-4 text-pm-secondary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
</div>
<div class="p-4 overflow-y-auto max-h-[calc(90vh-120px)]">
<div id="galleryContent" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
</div>
<div id="galleryEmpty" class="text-center py-8 hidden">
<h4 class="text-sm font-medium text-pm mb-1">No images found</h4>
<p class="text-pm-muted text-xs">No images have been generated with this prompt yet.</p>
</div>
<div id="galleryLoading" class="text-center py-8">
<div class="w-8 h-8 border-3 border-pm-accent/30 border-t-pm-accent rounded-full animate-spin mx-auto mb-3"></div>
<p class="text-pm-secondary text-xs">Loading images...</p>
</div>
</div>
</div>
</div>
<!-- Scan Modal -->
<div id="scanModal" class="fixed inset-0 bg-black/60 backdrop-blur-[2px] hidden items-center justify-center z-50">
<div class="bg-pm-surface rounded-pm-lg p-4 max-w-lg w-full mx-4 border border-pm shadow-pm">
<h3 class="text-sm font-semibold text-pm mb-4">Scan ComfyUI Output Images</h3>
<div class="space-y-3">
<div class="bg-pm-warning-tint border border-pm-warning/40 rounded-pm-md p-3">
<h4 class="text-pm-warning font-medium text-xs mb-1.5">Important Information</h4>
<ul class="text-pm text-xs space-y-0.5">
<li>This will scan all PNG files in your ComfyUI output directory</li>
<li>Depending on size, this could take several minutes</li>
<li>This is experimental — we recommend backing up first</li>
<li>Only images with ComfyUI metadata will be processed</li>
<li>Duplicate prompts will be automatically detected and skipped</li>
</ul>
</div>
<div class="bg-pm-info-tint border border-pm-info/40 rounded-pm-md p-3">
<div class="flex items-center justify-between">
<span class="text-pm text-xs font-medium">Backup Database First (Recommended)</span>
<button id="quickBackupBtn"
class="px-2.5 py-1 bg-pm-accent hover:bg-pm-accent-hover text-pm-accent-fg text-xs font-medium rounded-pm-sm transition-colors">
Backup Now
</button>
</div>
</div>
<div id="scanProgress" class="hidden space-y-2">
<div class="bg-pm-input rounded-pm-md p-3">
<div class="flex items-center justify-between mb-1.5">
<span class="text-pm text-xs font-medium">Scanning Progress</span>
<span id="scanStatusText" class="text-pm-secondary text-xs">Preparing...</span>
</div>
<div class="w-full bg-pm-primary rounded-full h-1.5">
<div id="scanProgressBar" class="bg-pm-accent h-1.5 rounded-full transition-all duration-300" style="width: 0%"></div>
</div>
<div class="flex justify-between text-[11px] text-pm-muted mt-1">
<span id="scanCount">0 files processed</span>
<span id="scanFound">0 prompts found</span>
</div>
</div>
</div>
</div>
<div class="flex justify-end gap-2 mt-4">
<button id="cancelScan"
class="px-3 py-1.5 bg-pm-surface border border-pm hover:bg-pm-hover text-pm text-xs font-medium rounded-pm-sm transition-colors">
Cancel
</button>
<button id="startScan"
class="px-3 py-1.5 bg-pm-accent hover:bg-pm-accent-hover text-pm-accent-fg text-xs font-medium rounded-pm-sm transition-colors">
Start Scan
</button>
</div>
</div>
</div>
<!-- Logs Modal -->
<div id="logsModal" class="fixed inset-0 bg-black/60 backdrop-blur-[2px] hidden items-center justify-center z-50">
<div class="bg-pm-surface rounded-pm-lg max-w-6xl w-full max-h-[90vh] overflow-hidden mx-4 border border-pm shadow-pm">
<div class="p-4 border-b border-pm">
<div class="flex items-center justify-between">
<h3 class="text-sm font-semibold text-pm">System Logs</h3>
<button onclick="window.admin.closeLogs()" class="p-1 rounded-pm-sm hover:bg-pm-hover transition-colors">
<svg class="w-4 h-4 text-pm-secondary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
</div>
<!-- Log Controls -->
<div class="p-4 border-b border-pm">
<div class="grid grid-cols-1 md:grid-cols-5 gap-3 mb-3">
<div>
<label class="block text-xs font-medium text-pm-secondary mb-1">Log Level</label>
<select id="logLevel" class="w-full px-2.5 py-1.5 bg-pm-input border border-pm rounded-pm-sm text-pm text-[13px] focus:outline-none">
<option value="">All Levels</option>
<option value="DEBUG">Debug</option>
<option value="INFO">Info</option>
<option value="WARNING">Warning</option>
<option value="ERROR">Error</option>
<option value="CRITICAL">Critical</option>
</select>
</div>
<div>
<label class="block text-xs font-medium text-pm-secondary mb-1">Limit</label>
<select id="logLimit" class="w-full px-2.5 py-1.5 bg-pm-input border border-pm rounded-pm-sm text-pm text-[13px] focus:outline-none">
<option value="50">Last 50</option>
<option value="100" selected>Last 100</option>
<option value="200">Last 200</option>
<option value="500">Last 500</option>
</select>
</div>
<div class="flex items-end">
<button id="refreshLogsBtn" class="w-full px-2.5 py-1.5 bg-pm-accent hover:bg-pm-accent-hover text-pm-accent-fg text-xs font-medium rounded-pm-sm transition-colors">
Refresh
</button>
</div>
<div class="flex items-end">
<button id="downloadLogsBtn" class="w-full px-2.5 py-1.5 bg-pm-surface border border-pm hover:bg-pm-hover text-pm text-xs font-medium rounded-pm-sm transition-colors">
Download
</button>
</div>
<div class="flex items-end">
<button id="clearLogsBtn" class="w-full px-2.5 py-1.5 text-pm-error bg-transparent hover:bg-pm-error-tint border border-pm text-xs font-medium rounded-pm-sm transition-colors">
Clear Logs
</button>
</div>
</div>
<!-- Log Stats -->
<div class="grid grid-cols-2 md:grid-cols-5 gap-3 mb-3">
<div class="bg-pm-input rounded-pm-md p-2">
<div class="text-[11px] text-pm-secondary">Total Logs</div>
<div class="text-sm font-semibold text-pm-accent" id="logStatsTotal">-</div>
</div>
<div class="bg-pm-input rounded-pm-md p-2">
<div class="text-[11px] text-pm-secondary">Errors</div>
<div class="text-sm font-semibold text-pm-error" id="logStatsErrors">-</div>
</div>
<div class="bg-pm-input rounded-pm-md p-2">
<div class="text-[11px] text-pm-secondary">Warnings</div>
<div class="text-sm font-semibold text-pm-warning" id="logStatsWarnings">-</div>
</div>
<div class="bg-pm-input rounded-pm-md p-2">
<div class="text-[11px] text-pm-secondary">Current Level</div>
<div class="text-sm font-semibold text-pm-success" id="logStatsLevel">-</div>
</div>
<div class="bg-pm-input rounded-pm-md p-2">
<div class="text-[11px] text-pm-secondary">Log Size</div>
<div class="text-sm font-semibold text-pm" id="logStatsSize">-</div>
</div>
</div>
<!-- Log Settings -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-3">
<div>
<label class="block text-xs font-medium text-pm-secondary mb-1">Set Log Level</label>
<select id="setLogLevel" class="w-full px-2.5 py-1.5 bg-pm-input border border-pm rounded-pm-sm text-pm text-[13px] focus:outline-none">
<option value="DEBUG">Debug</option>
<option value="INFO" selected>Info</option>
<option value="WARNING">Warning</option>
<option value="ERROR">Error</option>
<option value="CRITICAL">Critical</option>
</select>
</div>
<div class="flex items-center">
<label class="flex items-center gap-1.5 text-pm">
<input type="checkbox" id="consoleLogging" checked class="w-3.5 h-3.5 accent-[var(--pm-accent)]">
<span class="text-xs">Console Logging</span>
</label>
</div>
<div class="flex items-end">
<button id="updateLogConfigBtn" class="w-full px-2.5 py-1.5 bg-pm-accent hover:bg-pm-accent-hover text-pm-accent-fg text-xs font-medium rounded-pm-sm transition-colors">
Update Config
</button>
</div>
</div>
</div>
<!-- Log Viewer -->
<div class="flex-1 overflow-hidden">
<div class="h-96 overflow-y-auto bg-pm-primary font-mono text-xs">
<div id="logsContainer" class="p-3 space-y-0.5">
<div class="text-center py-6 text-pm-muted text-xs">
Click "Refresh" to load logs
</div>
</div>
</div>
</div>
<!-- Log Files -->
<div class="p-4 border-t border-pm">
<h4 class="text-xs font-semibold text-pm-secondary uppercase tracking-wide mb-3">Log Files</h4>
<div id="logFilesList" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-2">
</div>
</div>
</div>
</div>
<!-- Add Prompt Modal -->
<div id="addPromptModal" class="fixed inset-0 bg-black/60 backdrop-blur-[2px] hidden items-center justify-center z-50">
<div class="bg-pm-surface rounded-pm-lg p-4 max-w-2xl w-full mx-4 border border-pm max-h-[90vh] overflow-y-auto shadow-pm">
<div class="flex items-center justify-between mb-4">
<h3 class="text-sm font-semibold text-pm">Add New Prompt</h3>
<button onclick="window.admin.hideModal('addPromptModal')" class="p-1 rounded-pm-sm hover:bg-pm-hover transition-colors">
<svg class="w-4 h-4 text-pm-secondary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
<!-- Prompt Text -->
<div class="mb-4">
<label class="block text-xs font-medium text-pm-secondary mb-1">Prompt Text <span class="text-pm-error">*</span></label>
<textarea id="addPromptText" rows="4"
class="w-full px-2.5 py-1.5 bg-pm-input border border-pm rounded-pm-sm text-pm text-[13px] resize-none focus:outline-none"
placeholder="Enter your prompt text here..."></textarea>
</div>
<!-- Category and Rating Row -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-3 mb-4">
<div>
<label class="block text-xs font-medium text-pm-secondary mb-1">Category</label>
<div class="relative">
<input type="text" id="addPromptCategory" list="addPromptCategoryList"
class="w-full px-2.5 py-1.5 bg-pm-input border border-pm rounded-pm-sm text-pm text-[13px] focus:outline-none"
placeholder="Select or type category...">
<datalist id="addPromptCategoryList"></datalist>
</div>
</div>
<div>
<label class="block text-xs font-medium text-pm-secondary mb-1">Rating</label>
<div class="flex items-center gap-0.5" id="addPromptRating">
<button type="button" class="rating-star text-lg text-pm-muted hover:text-pm-warning transition-colors" data-rating="1">☆</button>
<button type="button" class="rating-star text-lg text-pm-muted hover:text-pm-warning transition-colors" data-rating="2">☆</button>
<button type="button" class="rating-star text-lg text-pm-muted hover:text-pm-warning transition-colors" data-rating="3">☆</button>
<button type="button" class="rating-star text-lg text-pm-muted hover:text-pm-warning transition-colors" data-rating="4">☆</button>
<button type="button" class="rating-star text-lg text-pm-muted hover:text-pm-warning transition-colors" data-rating="5">☆</button>
<button type="button" class="ml-1.5 text-[11px] text-pm-muted hover:text-pm" id="clearAddPromptRatingBtn">Clear</button>
</div>
<input type="hidden" id="addPromptRatingValue" value="">
</div>
</div>
<!-- Tags -->
<div class="mb-4">
<label class="block text-xs font-medium text-pm-secondary mb-1">Tags</label>
<div class="relative">
<div id="addPromptTagsContainer" class="flex flex-wrap gap-1.5 p-2 bg-pm-input border border-pm rounded-pm-sm min-h-[36px]">
<input type="text" id="addPromptTagInput"
class="flex-1 min-w-[150px] bg-transparent border-none outline-none text-pm text-[13px] placeholder:text-pm-muted"
placeholder="Type to add tags...">
</div>
<div id="addPromptTagSuggestions" class="absolute left-0 right-0 top-full mt-1 bg-pm-surface border border-pm rounded-pm-md shadow-pm hidden z-10 max-h-48 overflow-y-auto"></div>
</div>
<p class="text-pm-muted text-[11px] mt-1">Press Enter or comma to add a tag. Click a tag to remove it.</p>
</div>
<!-- Notes -->
<div class="mb-4">
<label class="block text-xs font-medium text-pm-secondary mb-1">Notes</label>
<textarea id="addPromptNotes" rows="2"
class="w-full px-2.5 py-1.5 bg-pm-input border border-pm rounded-pm-sm text-pm text-[13px] resize-none focus:outline-none"
placeholder="Optional notes about this prompt..."></textarea>
</div>
<!-- Protection Option -->
<div class="mb-4">
<label class="flex items-start p-3 bg-pm-input rounded-pm-md cursor-pointer hover:bg-pm-hover border border-pm">
<input type="checkbox" id="addPromptProtected" checked class="w-3.5 h-3.5 mt-0.5 accent-[var(--pm-accent)]">
<div class="ml-2.5">
<span class="text-pm text-xs font-medium">Protect from orphan cleanup</span>
<p class="text-pm-secondary text-[11px] mt-0.5">Keep this prompt even if it has no linked images.</p>
</div>
</label>
</div>
<!-- Action Buttons -->
<div class="flex justify-end gap-2">
<button onclick="window.admin.hideModal('addPromptModal')"
class="px-3 py-1.5 bg-pm-surface border border-pm hover:bg-pm-hover text-pm text-xs font-medium rounded-pm-sm transition-colors">
Cancel
</button>
<button id="saveNewPromptBtn"
class="px-3 py-1.5 bg-pm-accent hover:bg-pm-accent-hover text-pm-accent-fg text-xs font-medium rounded-pm-sm transition-colors">
Add Prompt
</button>
</div>
</div>
</div>
<!-- Auto Tag Modal -->
<div id="autoTagModal" class="fixed inset-0 bg-black/60 backdrop-blur-[2px] hidden items-center justify-center z-50">
<div class="bg-pm-surface rounded-pm-lg p-4 max-w-lg w-full mx-4 border border-pm shadow-pm">
<div class="flex items-center justify-between mb-4">
<h3 class="text-sm font-semibold text-pm">Auto Tag Images</h3>
<button onclick="hideModal('autoTagModal')" class="p-1 rounded-pm-sm hover:bg-pm-hover transition-colors">
<svg class="w-4 h-4 text-pm-secondary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
<!-- Model Selection -->
<div class="mb-4">
<label class="block text-xs font-medium text-pm-secondary mb-2">Model Selection</label>
<div class="space-y-2">
<label class="flex items-center justify-between p-3 bg-pm-input rounded-pm-md cursor-pointer hover:bg-pm-hover border border-pm">
<div class="flex items-center">
<input type="radio" name="autoTagModel" value="gguf" checked class="w-3.5 h-3.5 accent-[var(--pm-accent)]">
<div class="ml-2.5">
<span class="text-pm text-xs font-medium">GGUF Model</span>
<span class="text-pm-secondary text-[11px] ml-1.5">(Recommended, ~4.5GB)</span>
</div>
</div>
<div id="ggufModelStatus" class="flex items-center gap-1.5">
<span class="text-pm-secondary text-[11px]">Checking...</span>
</div>
</label>
<label class="flex items-center justify-between p-3 bg-pm-input rounded-pm-md cursor-pointer hover:bg-pm-hover border border-pm">
<div class="flex items-center">
<input type="radio" name="autoTagModel" value="hf" class="w-3.5 h-3.5 accent-[var(--pm-accent)]">
<div class="ml-2.5">
<span class="text-pm text-xs font-medium">HuggingFace Model</span>
<span class="text-pm-secondary text-[11px] ml-1.5">(~16GB)</span>
</div>
</div>
<div id="hfModelStatus" class="flex items-center gap-1.5">
<span class="text-pm-secondary text-[11px]">Checking...</span>
</div>
</label>
</div>
</div>
<!-- Prompt Template -->
<div class="mb-4">
<label class="block text-xs font-medium text-pm-secondary mb-1">Tag Generation Prompt</label>
<textarea id="autoTagPrompt" rows="6"
class="w-full px-2.5 py-1.5 bg-pm-input border border-pm rounded-pm-sm text-pm text-[13px] font-mono resize-none focus:outline-none"
placeholder="Enter prompt template...">Generate only comma-separated Danbooru tags (lowercase_underscores, no spaces).
Include: subject_count (1girl/1boy/etc), gender, ethnicity (asian, caucasian, etc), age_approx (teen, young_woman), body_attributes, face_features, hairstyle, appearance, clothing, accessories, pose, expression, action, camera_angle, camera_shot, scene_composition (wide_shot, long_shot, dutch_angle), environment, background_elements, lighting, mood.
Prioritize visual accuracy.
No sentences, no commentary, no captions. Only tags. Keep length short but descriptive.</textarea>
</div>
<!-- Tagged Images Handling -->
<div class="mb-4">
<label class="block text-xs font-medium text-pm-secondary mb-2">Already Tagged Images</label>
<div class="space-y-1.5">
<label class="flex items-center p-2.5 bg-pm-input rounded-pm-md cursor-pointer hover:bg-pm-hover border border-pm">
<input type="radio" name="autoTagMode" value="skip" checked class="w-3.5 h-3.5 accent-[var(--pm-accent)]">
<span class="ml-2.5 text-pm text-xs">Skip images with existing tags</span>
</label>
<label class="flex items-center p-2.5 bg-pm-input rounded-pm-md cursor-pointer hover:bg-pm-hover border border-pm">
<input type="radio" name="autoTagMode" value="retag" class="w-3.5 h-3.5 accent-[var(--pm-accent)]">
<span class="ml-2.5 text-pm text-xs">Re-tag all images</span>
</label>
</div>
<p class="text-pm-muted text-[11px] mt-1">Note: "auto-scanned" is not counted as a real tag</p>
</div>
<!-- Action Buttons -->
<div class="flex justify-end gap-2">
<button onclick="hideModal('autoTagModal')"
class="px-3 py-1.5 bg-pm-surface border border-pm hover:bg-pm-hover text-pm text-xs font-medium rounded-pm-sm transition-colors">
Cancel
</button>
<button id="startReviewBtn"
class="px-3 py-1.5 bg-pm-surface border border-pm hover:bg-pm-hover text-pm text-xs font-medium rounded-pm-sm transition-colors">
Review
</button>
<button id="startAutoTagBtn"
class="px-3 py-1.5 bg-pm-accent hover:bg-pm-accent-hover text-pm-accent-fg text-xs font-medium rounded-pm-sm transition-colors">
Auto Tag
</button>
</div>
</div>
</div>
<!-- Auto Tag Review Modal -->
<div id="autoTagReviewModal" class="fixed inset-0 bg-black/60 backdrop-blur-[2px] hidden items-center justify-center z-50">
<div class="bg-pm-surface rounded-pm-lg p-4 max-w-4xl w-full mx-4 border border-pm shadow-pm">
<div class="flex items-center justify-between mb-4">
<h3 class="text-sm font-semibold text-pm">
Review Tags
<span class="text-pm-secondary text-xs font-normal">(<span id="reviewCurrentIndex">1</span> of <span id="reviewTotalCount">0</span>)</span>
</h3>
<button onclick="window.admin.cancelReview()" class="p-1 rounded-pm-sm hover:bg-pm-hover transition-colors">
<svg class="w-4 h-4 text-pm-secondary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<!-- Image Preview -->
<div class="bg-pm-primary rounded-pm-md p-3">
<img id="reviewImage" src="" alt="Review image" class="w-full h-56 object-contain rounded-pm-sm">
<p id="reviewImageName" class="text-pm-secondary text-xs mt-1.5 text-center truncate">image_name.png</p>
</div>
<!-- Generated Tags -->
<div>
<label class="block text-xs font-medium text-pm-secondary mb-2">Generated Tags (click to remove)</label>
<div id="reviewTagsContainer" class="p-3 bg-pm-primary rounded-pm-md">
<div id="reviewTagsVisible" class="flex flex-wrap gap-1.5"></div>
<div id="reviewTagsAccordion" class="hidden mt-2">
<div id="reviewTagsHidden" class="flex flex-wrap gap-1.5 max-h-[180px] overflow-y-auto"></div>
</div>
<button id="reviewTagsToggle" class="hidden mt-2 text-xs text-pm-accent hover:text-pm-accent-hover transition-colors flex items-center gap-0.5">
<span id="reviewTagsToggleIcon">▼</span>
<span id="reviewTagsToggleText">Show all tags</span>
<span id="reviewTagsCount" class="text-pm-muted"></span>
</button>
</div>
<p class="text-pm-muted text-[11px] mt-1">Click the × on tags to remove them before applying</p>
</div>
</div>
<!-- Action Buttons -->
<div class="flex justify-between mt-4">
<button id="skipReviewBtn"
class="px-3 py-1.5 bg-pm-surface border border-pm hover:bg-pm-hover text-pm text-xs font-medium rounded-pm-sm transition-colors">
Skip
</button>
<div class="flex gap-2">
<button onclick="window.admin.cancelReview()"
class="px-3 py-1.5 text-pm-error bg-transparent hover:bg-pm-error-tint border border-pm text-xs font-medium rounded-pm-sm transition-colors">
Cancel All
</button>
<button id="applyReviewBtn"
class="px-3 py-1.5 bg-pm-accent hover:bg-pm-accent-hover text-pm-accent-fg text-xs font-medium rounded-pm-sm transition-colors">
Apply & Next
</button>
</div>
</div>
</div>
</div>
<!-- Auto Tag Progress Modal -->
<div id="autoTagProgressModal" class="fixed inset-0 bg-black/60 backdrop-blur-[2px] hidden items-center justify-center z-50">
<div class="bg-pm-surface rounded-pm-lg p-4 max-w-md w-full mx-4 border border-pm shadow-pm">
<h3 class="text-sm font-semibold text-pm mb-4">Auto Tagging Progress</h3>
<div class="mb-3">
<p class="text-pm-secondary text-xs">Processing:</p>
<p id="autoTagCurrentFile" class="text-pm text-xs font-medium truncate">Initializing...</p>
</div>
<div class="mb-4">
<div class="flex justify-between text-xs text-pm-secondary mb-1">
<span>Progress</span>
<span id="autoTagProgressPercent">0%</span>
</div>
<div class="w-full bg-pm-input rounded-full h-1.5">
<div id="autoTagProgressBar" class="bg-pm-accent h-1.5 rounded-full transition-all duration-300" style="width: 0%"></div>
</div>
</div>
<div class="grid grid-cols-3 gap-3 mb-4">
<div class="bg-pm-input rounded-pm-md p-2 text-center">
<div class="text-lg font-bold text-pm-accent" id="autoTagProcessed">0</div>
<div class="text-[11px] text-pm-secondary">Processed</div>
</div>
<div class="bg-pm-input rounded-pm-md p-2 text-center">
<div class="text-lg font-bold text-pm-success" id="autoTagApplied">0</div>
<div class="text-[11px] text-pm-secondary">Tags Applied</div>
</div>
<div class="bg-pm-input rounded-pm-md p-2 text-center">
<div class="text-lg font-bold text-pm-warning" id="autoTagSkipped">0</div>
<div class="text-[11px] text-pm-secondary">Skipped</div>
</div>
</div>
<div class="flex justify-center">
<button id="cancelAutoTagBtn"
class="px-3 py-1.5 text-pm-error bg-transparent hover:bg-pm-error-tint border border-pm text-xs font-medium rounded-pm-sm transition-colors">
Cancel
</button>
</div>
</div>
</div>
<!-- Auto Tag Loading Model Modal -->
<div id="autoTagLoadingModal" class="fixed inset-0 bg-black/60 backdrop-blur-[2px] hidden items-center justify-center z-50">
<div class="bg-pm-surface rounded-pm-lg p-4 max-w-md w-full mx-4 border border-pm shadow-pm text-center">
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-pm-accent mx-auto mb-3"></div>
<h3 class="text-sm font-semibold text-pm mb-1">Loading Model</h3>
<p id="autoTagLoadingStatus" class="text-pm-secondary text-xs">Initializing...</p>
<p class="text-pm-muted text-[11px] mt-3">This may take 30-60 seconds on first load</p>
</div>
</div>
<!-- Re-tag Confirmation Modal -->
<div id="retagConfirmModal" class="fixed inset-0 bg-black/60 backdrop-blur-[2px] hidden items-center justify-center z-50">
<div class="bg-pm-surface rounded-pm-lg p-4 max-w-lg w-full mx-4 border border-pm shadow-pm">
<h3 class="text-sm font-semibold text-pm mb-3">Image Already Tagged</h3>
<div class="mb-3">
<img id="retagPreviewImage" src="" alt="Preview" class="w-full h-40 object-contain bg-pm-primary rounded-pm-md">
<p id="retagImageName" class="text-pm-secondary text-xs mt-1.5 text-center truncate"></p>
</div>
<div class="mb-3">
<p class="text-pm text-xs mb-1.5">Current tags:</p>
<div id="retagExistingTags" class="flex flex-wrap gap-1 max-h-20 overflow-y-auto">
</div>
</div>
<p class="text-pm-secondary text-xs mb-4">This image already has tags. Would you like to generate new tags?</p>
<div class="flex gap-2 justify-end">
<button id="retagSkipBtn"
class="px-2.5 py-1.5 bg-pm-surface border border-pm hover:bg-pm-hover text-pm text-xs font-medium rounded-pm-sm transition-colors">
Skip
</button>
<button id="retagSkipAllBtn"
class="px-2.5 py-1.5 bg-pm-warning hover:opacity-80 text-black text-xs font-medium rounded-pm-sm transition-colors">
Skip All Tagged
</button>
<button id="retagConfirmBtn"
class="px-2.5 py-1.5 bg-pm-accent hover:bg-pm-accent-hover text-pm-accent-fg text-xs font-medium rounded-pm-sm transition-colors">
Re-tag
</button>
</div>
</div>
</div>
<!-- Auto Tag Download Modal -->
<div id="autoTagDownloadModal" class="fixed inset-0 bg-black/60 backdrop-blur-[2px] hidden items-center justify-center z-50">
<div class="bg-pm-surface rounded-pm-lg p-4 max-w-md w-full mx-4 border border-pm shadow-pm">
<h3 class="text-sm font-semibold text-pm mb-4">Downloading Model</h3>
<div class="mb-3">
<p class="text-pm-secondary text-xs">Downloading:</p>
<p id="downloadModelName" class="text-pm text-xs font-medium">GGUF Model</p>
</div>
<div class="mb-4">
<div class="flex justify-between text-xs text-pm-secondary mb-1">
<span id="downloadStatus">Preparing...</span>
<span id="downloadProgressPercent">0%</span>
</div>
<div class="w-full bg-pm-input rounded-full h-1.5">
<div id="downloadProgressBar" class="bg-pm-accent h-1.5 rounded-full transition-all duration-300" style="width: 0%"></div>
</div>
</div>
<div class="flex justify-center">
<button id="cancelDownloadBtn"
class="px-3 py-1.5 text-pm-error bg-transparent hover:bg-pm-error-tint border border-pm text-xs font-medium rounded-pm-sm transition-colors">
Cancel
</button>
</div>
</div>
</div>
<script src="/prompt_manager/js/admin.js"></script>
<script src="/prompt_manager/js/tags-page.js"></script>
</body>
</html>