feat: add toggle button to show/hide highlight underlines

- Add eye/eye-slash toggle button in highlights panel header
- Button only appears when there are highlights to show
- Clicking toggles underlines on/off in the main content panel
- When hidden, removes existing <mark> elements from DOM
- Add showUnderlines state management through Bookmarks component
- Style toggle button consistently with collapse button
- Add highlights-actions container for button group

Users can now toggle highlight visibility without losing the highlight list.
This commit is contained in:
Gigi
2025-10-04 21:54:18 +01:00
parent e2e5d59197
commit 842bfa5491
4 changed files with 66 additions and 16 deletions

View File

@@ -28,6 +28,7 @@ const Bookmarks: React.FC<BookmarksProps> = ({ relayPool, onLogout }) => {
const [isCollapsed, setIsCollapsed] = useState(false)
const [isHighlightsCollapsed, setIsHighlightsCollapsed] = useState(false)
const [viewMode, setViewMode] = useState<ViewMode>('cards')
const [showUnderlines, setShowUnderlines] = useState(true)
const activeAccount = Hooks.useActiveAccount()
const accountManager = Hooks.useAccountManager()
@@ -123,6 +124,7 @@ const Bookmarks: React.FC<BookmarksProps> = ({ relayPool, onLogout }) => {
markdown={readerContent?.markdown}
selectedUrl={selectedUrl}
highlights={highlights}
showUnderlines={showUnderlines}
/>
</div>
<div className="pane highlights">
@@ -133,6 +135,7 @@ const Bookmarks: React.FC<BookmarksProps> = ({ relayPool, onLogout }) => {
onToggleCollapse={() => setIsHighlightsCollapsed(!isHighlightsCollapsed)}
onSelectUrl={handleSelectUrl}
selectedUrl={selectedUrl}
onToggleUnderlines={setShowUnderlines}
/>
</div>
</div>

View File

@@ -13,6 +13,7 @@ interface ContentPanelProps {
markdown?: string
selectedUrl?: string
highlights?: Highlight[]
showUnderlines?: boolean
}
const ContentPanel: React.FC<ContentPanelProps> = ({
@@ -21,7 +22,8 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
html,
markdown,
selectedUrl,
highlights = []
highlights = [],
showUnderlines = true
}) => {
const contentRef = useRef<HTMLDivElement>(null)
@@ -88,13 +90,27 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
console.log('🔍 useEffect triggered:', {
hasContentRef: !!contentRef.current,
relevantHighlightsCount: relevantHighlights.length,
hasHtml: !!html
hasHtml: !!html,
showUnderlines
})
if (!contentRef.current || relevantHighlights.length === 0) {
if (!contentRef.current || relevantHighlights.length === 0 || !showUnderlines) {
console.log('⚠️ Skipping highlight application:', {
reason: !contentRef.current ? 'no contentRef' : 'no relevant highlights'
reason: !contentRef.current ? 'no contentRef' :
!showUnderlines ? 'underlines hidden' :
'no relevant highlights'
})
// If underlines are hidden, remove any existing highlights
if (!showUnderlines && contentRef.current) {
const marks = contentRef.current.querySelectorAll('mark.content-highlight')
marks.forEach(mark => {
const text = mark.textContent || ''
const textNode = document.createTextNode(text)
mark.parentNode?.replaceChild(textNode, mark)
})
}
return
}
@@ -119,7 +135,7 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
}, 100)
return () => clearTimeout(timer)
}, [relevantHighlights, html])
}, [relevantHighlights, html, showUnderlines])
const highlightedMarkdown = useMemo(() => {
if (!markdown || relevantHighlights.length === 0) return markdown

View File

@@ -1,6 +1,6 @@
import React, { useMemo } from 'react'
import React, { useMemo, useState } from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faChevronRight, faChevronLeft, faHighlighter } from '@fortawesome/free-solid-svg-icons'
import { faChevronRight, faChevronLeft, faHighlighter, faEye, faEyeSlash } from '@fortawesome/free-solid-svg-icons'
import { Highlight } from '../types/highlights'
import { HighlightItem } from './HighlightItem'
@@ -11,6 +11,7 @@ interface HighlightsPanelProps {
onToggleCollapse: () => void
onSelectUrl?: (url: string) => void
selectedUrl?: string
onToggleUnderlines?: (show: boolean) => void
}
export const HighlightsPanel: React.FC<HighlightsPanelProps> = ({
@@ -19,8 +20,17 @@ export const HighlightsPanel: React.FC<HighlightsPanelProps> = ({
isCollapsed,
onToggleCollapse,
onSelectUrl,
selectedUrl
selectedUrl,
onToggleUnderlines
}) => {
const [showUnderlines, setShowUnderlines] = useState(true)
const handleToggleUnderlines = () => {
const newValue = !showUnderlines
setShowUnderlines(newValue)
onToggleUnderlines?.(newValue)
}
// Filter highlights to show only those relevant to the current URL
const filteredHighlights = useMemo(() => {
if (!selectedUrl) return highlights
@@ -68,14 +78,26 @@ export const HighlightsPanel: React.FC<HighlightsPanelProps> = ({
<h3>Highlights</h3>
{!loading && <span className="count">({filteredHighlights.length})</span>}
</div>
<button
onClick={onToggleCollapse}
className="toggle-highlights-btn"
title="Collapse highlights panel"
aria-label="Collapse highlights panel"
>
<FontAwesomeIcon icon={faChevronRight} rotation={180} />
</button>
<div className="highlights-actions">
{filteredHighlights.length > 0 && (
<button
onClick={handleToggleUnderlines}
className="toggle-underlines-btn"
title={showUnderlines ? 'Hide underlines' : 'Show underlines'}
aria-label={showUnderlines ? 'Hide underlines' : 'Show underlines'}
>
<FontAwesomeIcon icon={showUnderlines ? faEye : faEyeSlash} />
</button>
)}
<button
onClick={onToggleCollapse}
className="toggle-highlights-btn"
title="Collapse highlights panel"
aria-label="Collapse highlights panel"
>
<FontAwesomeIcon icon={faChevronRight} rotation={180} />
</button>
</div>
</div>
{loading ? (

View File

@@ -1136,6 +1136,13 @@ body {
font-size: 0.875rem;
}
.highlights-actions {
display: flex;
align-items: center;
gap: 0.5rem;
}
.toggle-underlines-btn,
.toggle-highlights-btn {
background: transparent;
color: #ddd;
@@ -1151,11 +1158,13 @@ body {
transition: all 0.2s ease;
}
.toggle-underlines-btn:hover,
.toggle-highlights-btn:hover {
background: #2a2a2a;
color: #fff;
}
.toggle-underlines-btn:active,
.toggle-highlights-btn:active {
transform: translateY(1px);
}