diff --git a/TODO.md b/TODO.md index 4643a2d..f0a788f 100644 --- a/TODO.md +++ b/TODO.md @@ -255,6 +255,22 @@ - backend debug broadcasts ## Recent Change Log +- Date: `2026-03-16` +- What changed: + - Hid the expanded query-variant chip list from the search UI while leaving backend/debug query visibility intact. + - Reworked the result modal action panel so its primary CTA now follows source capability: `Direct Download` for Google Video and `Open Source` for Envato/Artgrid. + - Added a secondary action button area plus internal scrolling for long Source Summary text so long descriptions no longer push action buttons off-screen. + - Switched Google Video modal rendering from iframe-style embed-first behavior to a webpage-like preview panel, while keeping Artgrid/Envato on the existing media-mode path. + - Bumped the frontend asset version again to force browsers onto the updated modal behavior. +- Why it changed: + - The remaining user issues were now primarily UX/layout issues: too little visible result choice, hidden buttons under long summaries, query-variant clutter, and modal actions that did not match what each source can actually do. +- How it was verified: + - `go test ./...` + - `bash scripts/selftest.sh` +- What is still risky or incomplete: + - Live browser validation is still needed to confirm the widened search pool feels better in real queries and that the new Google Video panel feels sufficiently “webpage-like” in practice. + - `node` is still unavailable in this environment, so no frontend static syntax/build step was added here. + - Date: `2026-03-16` - What changed: - Expanded search breadth moderately by increasing base query count, collector query budgets, per-source caps, enrichment scope, and final visible result target while keeping Gemini review cap at `16`. diff --git a/frontend/app.js b/frontend/app.js index 897416f..2067ce9 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -52,6 +52,7 @@ const resultModalGoogleText = document.getElementById("resultModalGoogleText"); const resultModalFallbackLabel = document.getElementById("resultModalFallbackLabel"); const resultModalOpenExternal = document.getElementById("resultModalOpenExternal"); const resultModalDownload = document.getElementById("resultModalDownload"); +const resultModalSecondaryAction = document.getElementById("resultModalSecondaryAction"); const closeResultModal = document.getElementById("closeResultModal"); const resultModalReady = Boolean( resultModal && @@ -69,6 +70,7 @@ const resultModalReady = Boolean( resultModalFallbackLabel && resultModalOpenExternal && resultModalDownload && + resultModalSecondaryAction && closeResultModal, ); @@ -254,18 +256,7 @@ function syncRanges() { } function renderQueryVariants(queries = []) { - queryVariants.innerHTML = ""; - if (!queries.length) { - queryVariants.classList.add("hidden"); - return; - } - for (const item of queries) { - const badge = document.createElement("span"); - badge.className = "rounded-full border border-white/10 bg-white/[0.03] px-3 py-1 text-xs uppercase tracking-[0.18em] text-zinc-300"; - badge.textContent = item; - queryVariants.appendChild(badge); - } - queryVariants.classList.remove("hidden"); + queryVariants.classList.add("hidden"); } function syncPlatformButtons() { @@ -493,7 +484,7 @@ function showResultModalGooglePanel(item, message = "") { resultModalFallbackLabel.textContent = item.source || "Preview Fallback"; resultModalGoogleImage.src = hasUsableThumbnail(item.thumbnailUrl) ? item.thumbnailUrl : PREVIEW_PLACEHOLDER; resultModalGoogleImage.alt = item.title || ""; - resultModalGoogleText.textContent = message || item.previewBlockedReason || item.snippet || item.reason || "Preview fallback is being shown."; + resultModalGoogleText.textContent = message || item.previewBlockedReason || item.snippet || item.reason || "Open source page or use the primary action."; setHidden(resultModalGooglePanel, false, "flex"); } @@ -596,12 +587,17 @@ function openResultModal(item) { resultModalReason.textContent = summarizeReason(item.reason) || "AI 노트가 없습니다."; resultModalSnippet.textContent = item.snippet || "원본 페이지에서 사용할 수 있는 설명이 없습니다."; resultModalOpenExternal.href = item.link || "#"; - const canDirectDownload = item.source === "Google Video" && item.link; - resultModalDownload.classList.toggle("hidden", !canDirectDownload); + resultModalDownload.classList.toggle("hidden", !item.actionType); + resultModalDownload.textContent = item.actionLabel || "Open Source"; + const showSecondary = Boolean(item.secondaryActionLabel && item.link); + resultModalSecondaryAction.classList.toggle("hidden", !showSecondary); + resultModalSecondaryAction.textContent = item.secondaryActionLabel || "Open Source"; resetResultModalMedia(); const embedURL = buildResultModalEmbedURL(item); const fallbackReason = item.previewBlockedReason || "Embedded view was unavailable, switched to fallback preview."; - if (item.mediaMode === "embed" && embedURL && embedURL !== "about:blank") { + if (item.source === "Google Video" && item.mediaMode === "thumbnail") { + showResultModalGooglePanel(item, item.snippet || "Open source page or download directly."); + } else if (item.mediaMode === "embed" && embedURL && embedURL !== "about:blank") { showResultModalFrame(embedURL); const timeout = window.setTimeout(() => { logEvent("result:modal:iframe_timeout", { title: item.title, source: item.source, embedURL }); @@ -782,13 +778,23 @@ if (resultModalReady) { return; } const currentItem = activeResultItem; - try { - closeResultViewer(); - await prepareDirectDownload(currentItem.link); - } catch (error) { - downloadResult.textContent = error.message; - logEvent("download:preview:error", { message: error.message, data: error.data || null, source: currentItem?.source || "" }); + if (currentItem.actionType === "download") { + try { + closeResultViewer(); + await prepareDirectDownload(currentItem.link); + } catch (error) { + downloadResult.textContent = error.message; + logEvent("download:preview:error", { message: error.message, data: error.data || null, source: currentItem?.source || "" }); + } + return; } + window.open(currentItem.link, "_blank", "noopener,noreferrer"); + }); + resultModalSecondaryAction.addEventListener("click", () => { + if (!activeResultItem?.link) { + return; + } + window.open(activeResultItem.link, "_blank", "noopener,noreferrer"); }); } previewModal.addEventListener("click", (event) => { diff --git a/frontend/index.html b/frontend/index.html index 3a10d86..bf35605 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -46,7 +46,7 @@ - +
@@ -184,13 +184,20 @@

AI Note

-
- -
+
+
+ + +
+

Source Summary

-

+
+

+
@@ -216,6 +223,6 @@ - + diff --git a/frontend/style.css b/frontend/style.css index 0eda61e..f4d58a6 100644 --- a/frontend/style.css +++ b/frontend/style.css @@ -41,6 +41,15 @@ body { border-radius: 9999px; } +.result-summary-scroll::-webkit-scrollbar { + width: 10px; +} + +.result-summary-scroll::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.16); + border-radius: 9999px; +} + .debug-entry { border-bottom: 1px solid rgba(255, 255, 255, 0.06); padding: 10px 8px;