Improve modal fit and Gemini search refinement
build-push / docker (push) Successful in 4m8s

This commit is contained in:
AI Assistant
2026-03-17 12:30:50 +09:00
parent 0b68feff80
commit 556d4d6c1b
8 changed files with 353 additions and 77 deletions
+82 -25
View File
@@ -85,6 +85,8 @@ const activePlatforms = new Set(["envato", "artgrid", "google video"]);
const hlsInstances = new WeakMap();
const debugEntries = [];
const summaryTranslationCache = new Map();
const summaryTranslationInflight = new Map();
let cardSummaryObserver = null;
const PREVIEW_PLACEHOLDER = "https://placehold.co/1280x720/0a0a0a/ffffff?text=Preview";
function proxiedPreviewURL(src) {
@@ -120,9 +122,6 @@ function summarizeReason(reason) {
if (!text) {
return "";
}
if (text === "Ranked candidate pending stronger visual evidence.") {
return "Preview evidence pending";
}
if (text === "Fallback due to missing provider preview.") {
return "Provider preview missing";
}
@@ -491,34 +490,84 @@ function showResultModalGooglePanel(item, message = "") {
}
async function translateSummaryForModal(item, originalText, requestId) {
const translated = await translateSummaryText(originalText);
if (!translated) {
return;
}
if (activeResultItem?.link === item.link && activeResultModalSummaryRequest === requestId) {
resultModalSnippet.textContent = translated;
logEvent("result:modal:summary_translated", { title: item.title, source: item.source });
}
}
async function translateSummaryText(originalText) {
const trimmed = String(originalText || "").trim();
if (!trimmed) {
return;
return "";
}
if (summaryTranslationCache.has(trimmed)) {
if (activeResultItem?.link === item.link && activeResultModalSummaryRequest === requestId) {
resultModalSnippet.textContent = summaryTranslationCache.get(trimmed);
return summaryTranslationCache.get(trimmed);
}
if (summaryTranslationInflight.has(trimmed)) {
return summaryTranslationInflight.get(trimmed);
}
const request = (async () => {
try {
const data = await api("/api/translate/summary", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ text: trimmed }),
});
const translated = String(data.translatedText || "").trim();
if (translated) {
summaryTranslationCache.set(trimmed, translated);
}
return translated;
} catch {
return "";
} finally {
summaryTranslationInflight.delete(trimmed);
}
})();
summaryTranslationInflight.set(trimmed, request);
try {
return await request;
} catch {
return "";
}
}
async function translateCardSummary(node) {
if (!node || node.dataset.summaryTranslated === "true") {
return;
}
try {
const data = await api("/api/translate/summary", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ text: trimmed }),
});
const translated = String(data.translatedText || "").trim();
if (!translated) {
return;
}
summaryTranslationCache.set(trimmed, translated);
if (activeResultItem?.link === item.link && activeResultModalSummaryRequest === requestId) {
resultModalSnippet.textContent = translated;
logEvent("result:modal:summary_translated", { title: item.title, source: item.source });
}
} catch (error) {
logEvent("result:modal:summary_translate_failed", { title: item.title, source: item.source, message: error.message });
node.dataset.summaryTranslated = "true";
const originalText = node.dataset.summaryOriginal || "";
const translated = await translateSummaryText(originalText);
if (!translated) {
return;
}
const summaryNode = node.querySelector(".result-reason");
if (summaryNode) {
summaryNode.textContent = translated;
}
}
function ensureCardSummaryObserver() {
if (cardSummaryObserver || typeof IntersectionObserver === "undefined") {
return;
}
cardSummaryObserver = new IntersectionObserver((entries) => {
for (const entry of entries) {
if (!entry.isIntersecting) {
continue;
}
cardSummaryObserver.unobserve(entry.target);
void translateCardSummary(entry.target);
}
}, { rootMargin: "160px 0px" });
}
function fallbackResultModalMedia(item, reason) {
@@ -536,6 +585,7 @@ function fallbackResultModalMedia(item, reason) {
function renderResults(results) {
searchResults.innerHTML = "";
ensureCardSummaryObserver();
if (!results.length) {
searchResults.innerHTML = `<div class="rounded-3xl border border-white/10 bg-black/30 p-5 text-sm text-zinc-400">No results matched the current search sources.</div>`;
return;
@@ -561,9 +611,11 @@ function renderResults(results) {
mediaFallback.textContent = item.source === "Envato" || item.source === "Artgrid" ? `${item.source} preview unavailable` : "Preview unavailable";
}
node.querySelector("h3").textContent = item.title;
node.querySelector(".result-snippet").textContent = summarizeReason(item.reason) || item.snippet || item.source || "";
node.querySelector(".result-reason").textContent = item.snippet ? `Source: ${item.snippet}` : (item.previewBlockedReason || "");
node.querySelector(".result-snippet").textContent = summarizeReason(item.reason) || item.source || "";
node.querySelector(".result-reason").textContent = item.snippet || item.previewBlockedReason || "";
node.querySelector(".source-badge").textContent = item.source;
node.dataset.summaryOriginal = item.snippet || "";
node.dataset.summaryTranslated = "false";
node.addEventListener("click", () => openResultModal(item));
previewVideo.poster = usableThumbnail ? item.thumbnailUrl : "";
if (item.previewVideoUrl) {
@@ -582,6 +634,11 @@ function renderResults(results) {
overlays.forEach((overlay) => overlay.classList.remove("hidden"));
});
}
if (cardSummaryObserver && item.snippet) {
cardSummaryObserver.observe(node);
} else if (item.snippet) {
void translateCardSummary(node);
}
searchResults.appendChild(node);
}
}
+8 -7
View File
@@ -149,7 +149,7 @@
</div>
</div>
<div id="resultModal" class="fixed inset-0 z-50 hidden items-center justify-center bg-black/80 px-3 py-3 sm:px-4 sm:py-4">
<div id="resultModal" class="fixed inset-0 z-50 hidden items-start justify-center overflow-y-auto bg-black/80 px-2 py-2 sm:px-4 sm:py-4">
<div class="result-modal-shell flex w-full max-w-6xl min-h-0 flex-col overflow-hidden rounded-3xl border border-white/10 bg-zinc-950 shadow-2xl">
<div class="flex items-center justify-between border-b border-white/10 px-5 py-4">
<div class="min-w-0">
@@ -180,9 +180,11 @@
</div>
</div>
<div class="result-modal-details grid min-h-0 gap-4 px-4 py-4 sm:gap-5 sm:px-5 sm:py-5 lg:grid-cols-[1.6fr_0.8fr]">
<div class="min-h-[200px] rounded-2xl border border-white/10 bg-white/[0.03] p-5">
<div class="flex min-h-[220px] min-w-0 flex-col rounded-2xl border border-white/10 bg-white/[0.03] p-5">
<p class="text-xs uppercase tracking-[0.25em] text-zinc-500">AI Note</p>
<p id="resultModalReason" class="mt-3 whitespace-pre-wrap text-sm leading-7 text-zinc-200"></p>
<div class="result-panel-scroll mt-3 min-h-0 flex-1 overflow-y-auto pr-2">
<p id="resultModalReason" class="whitespace-pre-wrap text-sm leading-7 text-zinc-200"></p>
</div>
</div>
<div class="flex min-h-[240px] min-w-0 flex-col rounded-2xl border border-white/10 bg-white/[0.03] p-5">
<div class="mb-4 flex flex-col gap-3">
@@ -212,17 +214,16 @@
<div class="media-fallback absolute inset-0 hidden items-center justify-center bg-[radial-gradient(circle_at_top,#2b3342,transparent_60%),linear-gradient(180deg,#111827,#05070b)] p-5 text-center text-xs uppercase tracking-[0.24em] text-zinc-300">
Preview unavailable
</div>
<div class="preview-overlay absolute left-3 top-3 rounded-full border border-white/20 bg-black/60 px-3 py-1 text-[11px] uppercase tracking-[0.25em] text-white">AI Recommended</div>
<div class="source-badge preview-overlay absolute bottom-3 left-3 rounded-full bg-white px-3 py-1 text-[11px] font-medium uppercase tracking-[0.2em] text-black"></div>
</div>
<div class="space-y-2 p-5">
<h3 class="line-clamp-2 text-base font-medium text-white"></h3>
<p class="result-snippet line-clamp-3 text-sm text-zinc-400"></p>
<p class="result-reason line-clamp-2 text-xs tracking-[0.02em] text-zinc-500"></p>
<p class="result-snippet line-clamp-2 text-sm text-zinc-300"></p>
<p class="result-reason line-clamp-3 text-xs tracking-[0.02em] text-zinc-500"></p>
</div>
</button>
</template>
<script src="/app.js?v=20260317a" defer></script>
<script src="/app.js?v=20260317b" defer></script>
</body>
</html>
+23 -6
View File
@@ -50,20 +50,37 @@ body {
border-radius: 9999px;
}
.result-panel-scroll::-webkit-scrollbar {
width: 10px;
}
.result-panel-scroll::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.16);
border-radius: 9999px;
}
.result-modal-shell {
max-height: calc(100vh - 1.5rem);
height: min(calc(100dvh - 1rem), 920px);
margin: auto;
}
.result-modal-shell > * {
min-height: 0;
}
.result-modal-media-frame {
max-height: min(48vh, 32rem);
max-height: min(42dvh, 28rem);
}
.result-modal-details {
flex: 1 1 auto;
overflow: hidden;
min-height: 0;
align-items: stretch;
}
#resultModalSnippet {
#resultModalSnippet,
#resultModalReason {
white-space: pre-wrap;
word-break: break-word;
}
@@ -93,16 +110,16 @@ body {
@media (max-height: 900px) {
.result-modal-media-frame {
max-height: min(40vh, 26rem);
max-height: min(34dvh, 22rem);
}
}
@media (max-height: 720px) {
.result-modal-shell {
max-height: calc(100vh - 1rem);
height: min(calc(100dvh - 0.5rem), 780px);
}
.result-modal-media-frame {
max-height: min(34vh, 18rem);
max-height: min(28dvh, 15rem);
}
}