From 7647aadb63eaeeeeb77fcde9f80f0eea94ed8ebf Mon Sep 17 00:00:00 2001 From: isUnknown Date: Mon, 8 Dec 2025 12:29:55 +0100 Subject: [PATCH] feat: improve page highlight with orange color and template grouping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add --color-page-highlight CSS variable (#ff8a50) - Change page edge highlight from blue to orange - Keep border visible while PagePopup is open - Highlight all pages using the same template (data-page-type) - Display dynamic page count in PagePopup header - Emit close event from PagePopup for proper cleanup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- public/assets/css/src/_variables.scss | 2 ++ src/App.vue | 52 +++++++++++++++++++++++---- src/components/PagePopup.vue | 22 ++++++------ 3 files changed, 57 insertions(+), 19 deletions(-) diff --git a/public/assets/css/src/_variables.scss b/public/assets/css/src/_variables.scss index 43bb561..c632964 100644 --- a/public/assets/css/src/_variables.scss +++ b/public/assets/css/src/_variables.scss @@ -4,6 +4,8 @@ --color-browngray-200: #d0c4ba; --color-browngray-300: #b5a9a1; + --color-page-highlight: #ff8a50; + --border-radius: 0.2rem; --space-xs: 0.5rem; diff --git a/src/App.vue b/src/App.vue index 81b2d0f..ba2b2f1 100644 --- a/src/App.vue +++ b/src/App.vue @@ -19,7 +19,9 @@ provide('activeTab', activeTab); // Page interaction state const hoveredPage = ref(null); +const selectedPages = ref([]); // Pages with active border (when popup is open) const EDGE_THRESHOLD = 30; // px from edge to trigger hover +const PAGE_HIGHLIGHT_COLOR = '#ff8a50'; let savedScrollPercentage = 0; const currentFrameIndex = ref(1); // 1 or 2, which iframe is currently visible @@ -51,6 +53,15 @@ const isNearPageEdge = (pageElement, mouseX, mouseY) => { ); }; +// Get all pages using the same template as the given page +const getPagesWithSameTemplate = (page, doc) => { + const pageType = page.getAttribute('data-page-type') || 'default'; + const allPages = doc.querySelectorAll('.pagedjs_page'); + return Array.from(allPages).filter( + (p) => (p.getAttribute('data-page-type') || 'default') === pageType + ); +}; + // Handle mouse movement in iframe const handleIframeMouseMove = (event) => { const pages = event.target.ownerDocument.querySelectorAll('.pagedjs_page'); @@ -65,20 +76,28 @@ const handleIframeMouseMove = (event) => { // Update hover state if (foundPage !== hoveredPage.value) { - // Remove highlight from previous page - if (hoveredPage.value) { + // Remove highlight from previous page (only if not in selectedPages) + if (hoveredPage.value && !selectedPages.value.includes(hoveredPage.value)) { hoveredPage.value.style.outline = ''; } - // Add highlight to new page - if (foundPage) { - foundPage.style.outline = '2px solid rgba(97, 175, 239, 0.3)'; + // Add highlight to new page (only if not already selected) + if (foundPage && !selectedPages.value.includes(foundPage)) { + foundPage.style.outline = `2px solid ${PAGE_HIGHLIGHT_COLOR}50`; } hoveredPage.value = foundPage; } }; +// Clear selection highlight from all selected pages +const clearSelectedPages = () => { + selectedPages.value.forEach((page) => { + page.style.outline = ''; + }); + selectedPages.value = []; +}; + // Handle click in iframe const handleIframeClick = (event) => { const element = event.target; @@ -86,7 +105,19 @@ const handleIframeClick = (event) => { // Check if clicking near a page edge if (hoveredPage.value) { event.stopPropagation(); - pagePopup.value.open(hoveredPage.value, event); + + // Clear previous selection + clearSelectedPages(); + + // Get all pages with same template and highlight them + const doc = event.target.ownerDocument; + const sameTemplatePages = getPagesWithSameTemplate(hoveredPage.value, doc); + sameTemplatePages.forEach((page) => { + page.style.outline = `2px solid ${PAGE_HIGHLIGHT_COLOR}`; + }); + selectedPages.value = sameTemplatePages; + + pagePopup.value.open(hoveredPage.value, event, sameTemplatePages.length); elementPopup.value.close(); return; } @@ -94,15 +125,22 @@ const handleIframeClick = (event) => { // Only show popup for elements inside the page template const isInsidePage = element.closest('.pagedjs_page'); if (!isInsidePage) { + clearSelectedPages(); elementPopup.value.close(); pagePopup.value.close(); return; } + clearSelectedPages(); elementPopup.value.handleIframeClick(event); pagePopup.value.close(); }; +// Expose clearSelectedPages for PagePopup to call when closing +const handlePagePopupClose = () => { + clearSelectedPages(); +}; + const renderPreview = async (shouldReloadFromFile = false) => { if (isTransitioning.value) return; isTransitioning.value = true; @@ -228,7 +266,7 @@ onMounted(() => renderPreview(true)); - +