This commit is contained in:
+82
-25
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user