diff --git a/assets/styles/04-pages/highlights.css b/assets/styles/04-pages/highlights.css index 52d4f8a..1564f79 100644 --- a/assets/styles/04-pages/highlights.css +++ b/assets/styles/04-pages/highlights.css @@ -133,6 +133,90 @@ background-color: var(--color-accent-400); } +/* Sidebar highlights - compact cards */ +.highlights-sidebar { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.highlight-card-compact { + background: var(--color-bg-light); + padding: var(--spacing-2); + transition: transform 0.2s, box-shadow 0.2s; +} + +.highlight-card-compact:hover { + transform: translateY(-2px); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); +} + +.highlight-header-compact { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 0.75rem; + font-size: 0.85rem; + gap: 0.5rem; +} + +.highlight-content-compact { + font-size: 0.95rem; + line-height: 1.4; + color: var(--color-text); +} + +.highlight-mark-compact { + background: var(--color-accent-300); + padding: 0.1em 0.2em; + font-weight: 500; + border-radius: 2px; +} + +.context-text-compact { + margin-bottom: 0.5rem; + font-style: normal; + color: var(--color-text-mid); +} + +.sidebar-section { + margin-bottom: 2rem; +} + +.sidebar-heading { + font-size: 1.1rem; + font-weight: 600; + margin-bottom: 1rem; +} + +/* Mobile highlights - hidden by default, shown when sidebar is not visible */ +.highlights-mobile-section { + display: none; + margin-top: 3rem; + padding-top: 2rem; + border-top: 2px solid var(--color-border); +} + +.highlights-mobile-heading { + font-size: 1.3rem; + font-weight: 600; + margin-bottom: 1.5rem; + color: var(--color-text); +} + +.highlights-mobile-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: 1.5rem; +} + +/* Show mobile highlights when sidebar is hidden (1200px and below) */ +@media (max-width: 1200px) { + .highlights-mobile-section { + display: block; + } +} + @media (max-width: 768px) { .highlights-grid { column-count: 1; @@ -141,6 +225,10 @@ .highlight-card { margin-bottom: 1.5rem; } + + .highlights-mobile-grid { + grid-template-columns: 1fr; + } } @media (min-width: 769px) and (max-width: 1400px) { diff --git a/src/Service/HighlightService.php b/src/Service/HighlightService.php index b290ae0..1d11ab2 100644 --- a/src/Service/HighlightService.php +++ b/src/Service/HighlightService.php @@ -77,7 +77,7 @@ class HighlightService 'count' => count($highlights) ]); - return array_map(function (Highlight $highlight) { + $mappedHighlights = array_map(function (Highlight $highlight) { return [ 'content' => $highlight->getContent(), 'created_at' => $highlight->getCreatedAt(), @@ -85,6 +85,28 @@ class HighlightService 'context' => $highlight->getContext(), ]; }, $highlights); + + // Deduplicate highlights based on content and pubkey + // Keep the most recent highlight for each content+pubkey combination + $deduplicated = []; + $seen = []; + + foreach ($mappedHighlights as $highlight) { + $key = md5($highlight['content'] . '|' . $highlight['pubkey']); + + if (!isset($seen[$key])) { + $seen[$key] = true; + $deduplicated[] = $highlight; + } + } + + $this->logger->info('Deduplicated highlights', [ + 'coordinate' => $articleCoordinate, + 'original_count' => count($mappedHighlights), + 'deduplicated_count' => count($deduplicated) + ]); + + return $deduplicated; } /** diff --git a/templates/pages/article.html.twig b/templates/pages/article.html.twig index b90baea..ae566c0 100644 --- a/templates/pages/article.html.twig +++ b/templates/pages/article.html.twig @@ -168,10 +168,91 @@ }" /> + + {# Mobile highlights - shown after comments on narrow screens #} + {% if highlights is defined and highlights|length > 0 %} +
+

Highlights ({{ highlights|length }})

+
+ {% for highlight in highlights %} +
+
+ {% if highlight.pubkey %} + + {% else %} + Anonymous + {% endif %} + + {{ highlight.created_at|date('M j') }} + +
+
+ {% if highlight.context %} + {% set htmlContext = highlight.context|markdown_to_html %} + {% if highlight.content in highlight.context %} + {% set wrapper = '' ~ highlight.content ~ '' %} + {% set rendered = htmlContext|replace({ (highlight.content): wrapper }) %} + {{ rendered|raw }} + {% else %} +
{{ htmlContext|raw }}
+ {{ highlight.content }} + {% endif %} + {% else %} + {{ highlight.content }} + {% endif %} +
+
+ {% endfor %} +
+
+ {% endif %} {% endblock %} {% block aside %} + {% if highlights is defined and highlights|length > 0 %} + + {% endif %} + {#

Suggestions

#} {# #} {% endblock %}