feat: add click-to-scroll for highlights

- Clicking a highlight in the main text scrolls to it in the sidebar
- Selected highlight is visually highlighted with border and shadow
- Add selectedHighlightId state management in Bookmarks component
- Add click handlers to mark elements in ContentPanel
- Add isSelected prop to HighlightItem with scroll-into-view
- Add CSS styles for selected highlight state
- Set cursor to pointer on clickable highlights

Users can now click on highlighted text to jump to the corresponding
highlight in the right sidebar for easy navigation.
This commit is contained in:
Gigi
2025-10-04 22:21:43 +01:00
parent 1bcaa1998d
commit 05636046a8
5 changed files with 42 additions and 6 deletions

View File

@@ -29,6 +29,7 @@ const Bookmarks: React.FC<BookmarksProps> = ({ relayPool, onLogout }) => {
const [isHighlightsCollapsed, setIsHighlightsCollapsed] = useState(false)
const [viewMode, setViewMode] = useState<ViewMode>('cards')
const [showUnderlines, setShowUnderlines] = useState(true)
const [selectedHighlightId, setSelectedHighlightId] = useState<string | undefined>(undefined)
const activeAccount = Hooks.useActiveAccount()
const accountManager = Hooks.useAccountManager()
@@ -125,6 +126,7 @@ const Bookmarks: React.FC<BookmarksProps> = ({ relayPool, onLogout }) => {
selectedUrl={selectedUrl}
highlights={highlights}
showUnderlines={showUnderlines}
onHighlightClick={setSelectedHighlightId}
/>
</div>
<div className="pane highlights">
@@ -136,6 +138,7 @@ const Bookmarks: React.FC<BookmarksProps> = ({ relayPool, onLogout }) => {
onSelectUrl={handleSelectUrl}
selectedUrl={selectedUrl}
onToggleUnderlines={setShowUnderlines}
selectedHighlightId={selectedHighlightId}
/>
</div>
</div>

View File

@@ -14,6 +14,7 @@ interface ContentPanelProps {
selectedUrl?: string
highlights?: Highlight[]
showUnderlines?: boolean
onHighlightClick?: (highlightId: string) => void
}
const ContentPanel: React.FC<ContentPanelProps> = ({
@@ -23,7 +24,8 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
markdown,
selectedUrl,
highlights = [],
showUnderlines = true
showUnderlines = true,
onHighlightClick
}) => {
const contentRef = useRef<HTMLDivElement>(null)
@@ -136,13 +138,27 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
if (originalHTML !== highlightedHTML) {
console.log('✅ Applied highlights to DOM')
contentRef.current.innerHTML = highlightedHTML
// Add click handlers to all highlight marks
if (onHighlightClick) {
const marks = contentRef.current.querySelectorAll('mark.content-highlight')
marks.forEach(mark => {
const highlightId = mark.getAttribute('data-highlight-id')
if (highlightId) {
mark.addEventListener('click', () => {
onHighlightClick(highlightId)
})
;(mark as HTMLElement).style.cursor = 'pointer'
}
})
}
} else {
console.log('⚠️ No changes made to DOM')
}
})
return () => cancelAnimationFrame(rafId)
}, [relevantHighlights, html, markdown, showUnderlines])
}, [relevantHighlights, html, markdown, showUnderlines, onHighlightClick])
const highlightedMarkdown = useMemo(() => {
if (!markdown || relevantHighlights.length === 0) return markdown

View File

@@ -1,4 +1,4 @@
import React from 'react'
import React, { useEffect, useRef } from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faQuoteLeft, faLink, faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons'
import { Highlight } from '../types/highlights'
@@ -7,9 +7,17 @@ import { formatDistanceToNow } from 'date-fns'
interface HighlightItemProps {
highlight: Highlight
onSelectUrl?: (url: string) => void
isSelected?: boolean
}
export const HighlightItem: React.FC<HighlightItemProps> = ({ highlight, onSelectUrl }) => {
export const HighlightItem: React.FC<HighlightItemProps> = ({ highlight, onSelectUrl, isSelected }) => {
const itemRef = useRef<HTMLDivElement>(null)
useEffect(() => {
if (isSelected && itemRef.current) {
itemRef.current.scrollIntoView({ behavior: 'smooth', block: 'center' })
}
}, [isSelected])
const handleLinkClick = (url: string, e: React.MouseEvent) => {
if (onSelectUrl) {
e.preventDefault()
@@ -27,7 +35,7 @@ export const HighlightItem: React.FC<HighlightItemProps> = ({ highlight, onSelec
const sourceLink = getSourceLink()
return (
<div className="highlight-item">
<div ref={itemRef} className={`highlight-item ${isSelected ? 'selected' : ''}`} data-highlight-id={highlight.id}>
<div className="highlight-quote-icon">
<FontAwesomeIcon icon={faQuoteLeft} />
</div>

View File

@@ -12,6 +12,7 @@ interface HighlightsPanelProps {
onSelectUrl?: (url: string) => void
selectedUrl?: string
onToggleUnderlines?: (show: boolean) => void
selectedHighlightId?: string
}
export const HighlightsPanel: React.FC<HighlightsPanelProps> = ({
@@ -21,7 +22,8 @@ export const HighlightsPanel: React.FC<HighlightsPanelProps> = ({
onToggleCollapse,
onSelectUrl,
selectedUrl,
onToggleUnderlines
onToggleUnderlines,
selectedHighlightId
}) => {
const [showUnderlines, setShowUnderlines] = useState(true)
@@ -121,6 +123,7 @@ export const HighlightsPanel: React.FC<HighlightsPanelProps> = ({
key={highlight.id}
highlight={highlight}
onSelectUrl={onSelectUrl}
isSelected={highlight.id === selectedHighlightId}
/>
))}
</div>