diff --git a/web/admin.html b/web/admin.html index 59fe9a6..154ee95 100644 --- a/web/admin.html +++ b/web/admin.html @@ -67,6 +67,93 @@ flex-direction: column !important; z-index: 10000 !important; } + + /* Film Strip Styles */ + .prompt-film-strip { + --film-strip-thumbnail-size: 112px; + --film-strip-gap: 8px; + display: flex; + flex-wrap: nowrap; + gap: var(--film-strip-gap); + padding: 8px 0; + overflow-x: auto; + scrollbar-width: thin; + scrollbar-color: #4b5563 transparent; + } + + .prompt-film-strip::-webkit-scrollbar { + height: 4px; + } + + .prompt-film-strip::-webkit-scrollbar-track { + background: transparent; + } + + .prompt-film-strip::-webkit-scrollbar-thumb { + background: #4b5563; + border-radius: 2px; + } + + .film-strip-thumbnail { + flex-shrink: 0; + width: var(--film-strip-thumbnail-size); + height: var(--film-strip-thumbnail-size); + border-radius: 6px; + overflow: hidden; + cursor: pointer; + border: 2px solid transparent; + transition: all 0.15s ease; + background: #1f2937; + } + + .film-strip-thumbnail:hover { + border-color: #3b82f6; + transform: scale(1.05); + } + + .film-strip-thumbnail img { + width: 100%; + height: 100%; + object-fit: cover; + } + + .film-strip-thumbnail--more { + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient(135deg, #374151 0%, #1f2937 100%); + color: #9ca3af; + font-size: 12px; + font-weight: 600; + } + + .film-strip-thumbnail--more:hover { + background: linear-gradient(135deg, #4b5563 0%, #374151 100%); + color: #e5e7eb; + } + + .film-strip-empty { + color: #6b7280; + font-size: 12px; + font-style: italic; + padding: 8px 0; + } + + /* Film strip size variants */ + .film-strip-size-small { + --film-strip-thumbnail-size: 72px; + --film-strip-gap: 6px; + } + + .film-strip-size-medium { + --film-strip-thumbnail-size: 112px; + --film-strip-gap: 8px; + } + + .film-strip-size-large { + --film-strip-thumbnail-size: 144px; + --film-strip-gap: 10px; + }
@@ -1633,11 +1720,36 @@ No sentences, no commentary, no captions. Only tags. Keep length short but descr container.innerHTML = sortedPrompts.map((prompt) => this.renderPromptItem(prompt)).join(""); this.selectedPrompts.clear(); this.updateBulkActionButtons(); - + // Add hover behavior to all star ratings sortedPrompts.forEach(prompt => { this.addStarHoverBehavior(prompt.id); }); + + // Load film strips for each prompt (async, non-blocking) + this.loadAllFilmStrips(sortedPrompts); + } + + async loadAllFilmStrips(prompts) { + // Load filmstrips in batches to avoid overwhelming the server + const batchSize = 5; + for (let i = 0; i < prompts.length; i += batchSize) { + const batch = prompts.slice(i, i + batchSize); + await Promise.all(batch.map(prompt => this.loadFilmStripForPrompt(prompt.id))); + } + } + + async loadFilmStripForPrompt(promptId) { + const container = document.getElementById(`filmStrip-${promptId}`); + if (!container) return; + + try { + const images = await this.loadFilmStripImages(promptId); + container.innerHTML = this.createFilmStrip(images, promptId); + } catch (error) { + console.error(`Error loading filmstrip for prompt ${promptId}:`, error); + container.innerHTML = `