doom scroll and viewer improvements
- move play/pause to clicking the video directly; remove dedicated button - replace emoji mute icons with flat minimal SVGs - add view-in-library button in doom scroll that navigates to the file's directory and opens it in the regular viewer - add display text overlay button in doom scroll and image lightbox; shows extracted text (translated by default when available) in a semi-transparent box at the bottom; toggle between translated/original - hide tag panel by default in image lightbox and video player modal Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -16,9 +16,7 @@ interface Props {
|
||||
|
||||
export default function ImageLightbox({ url, name, onClose, onPrev, onNext, itemKey, onTagsChanged, onAiTag }: Props) {
|
||||
const overlayRef = useRef<HTMLDivElement>(null)
|
||||
const [showTags, setShowTags] = useState(
|
||||
() => !!itemKey && typeof window !== 'undefined' && window.innerWidth >= 1280
|
||||
)
|
||||
const [showTags, setShowTags] = useState(false)
|
||||
const [aiTagging, setAiTagging] = useState(false)
|
||||
const [aiTagError, setAiTagError] = useState<string | null>(null)
|
||||
const [tagRefreshKey, setTagRefreshKey] = useState(0)
|
||||
@@ -30,9 +28,16 @@ export default function ImageLightbox({ url, name, onClose, onPrev, onNext, item
|
||||
const [extractError, setExtractError] = useState<string | null>(null)
|
||||
const [retranslating, setRetranslating] = useState(false)
|
||||
|
||||
// Text overlay state
|
||||
const [showTextOverlay, setShowTextOverlay] = useState(false)
|
||||
const [showOriginal, setShowOriginal] = useState(false)
|
||||
|
||||
// Determine if this is an image file (for text extraction controls)
|
||||
const isImage = /\.(jpe?g|png|gif|webp|bmp|tiff?)$/i.test(name)
|
||||
|
||||
// Derived: what text to display in the overlay
|
||||
const displayText = (translatedText && !showOriginal) ? translatedText : extractedText
|
||||
|
||||
// Fetch existing AI fields on mount / item change
|
||||
useEffect(() => {
|
||||
if (!itemKey) return
|
||||
@@ -76,6 +81,31 @@ export default function ImageLightbox({ url, name, onClose, onPrev, onNext, item
|
||||
{name}
|
||||
</span>
|
||||
<div className="flex items-center gap-2 flex-shrink-0">
|
||||
{/* Text overlay button — only shown when extracted text exists */}
|
||||
{extractedText && (
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); setShowTextOverlay((v) => !v) }}
|
||||
className="w-12 h-12 rounded-full flex items-center justify-center transition-colors"
|
||||
style={{
|
||||
backgroundColor: showTextOverlay ? 'var(--accent)' : 'var(--surface)',
|
||||
color: showTextOverlay ? '#fff' : 'var(--text-primary)',
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
if (!showTextOverlay) (e.currentTarget as HTMLElement).style.backgroundColor = 'var(--surface-hover)'
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
if (!showTextOverlay) (e.currentTarget as HTMLElement).style.backgroundColor = 'var(--surface)'
|
||||
}}
|
||||
aria-label={showTextOverlay ? 'Hide text' : 'Show text'}
|
||||
title="Display text"
|
||||
>
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<line x1="3" y1="6" x2="21" y2="6"/>
|
||||
<line x1="3" y1="12" x2="15" y2="12"/>
|
||||
<line x1="3" y1="18" x2="18" y2="18"/>
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
{itemKey && (
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); setShowTags((v) => !v) }}
|
||||
@@ -179,6 +209,29 @@ export default function ImageLightbox({ url, name, onClose, onPrev, onNext, item
|
||||
›
|
||||
</button>
|
||||
)}
|
||||
{/* Text overlay */}
|
||||
{showTextOverlay && displayText && (
|
||||
<div
|
||||
className="absolute bottom-4 left-4 right-4 z-10 rounded-xl p-4"
|
||||
style={{ backgroundColor: 'rgba(0,0,0,0.75)' }}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{extractedText && translatedText && (
|
||||
<div className="flex justify-end mb-2">
|
||||
<button
|
||||
onClick={() => setShowOriginal((v) => !v)}
|
||||
className="text-xs px-2 py-0.5 rounded-full"
|
||||
style={{ backgroundColor: 'rgba(255,255,255,0.15)', color: 'rgba(255,255,255,0.7)' }}
|
||||
>
|
||||
{showOriginal ? 'Show Translation' : 'Show Original'}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
<p className="text-sm whitespace-pre-wrap" style={{ color: 'rgba(255,255,255,0.9)' }}>
|
||||
{displayText}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* Tag panel */}
|
||||
<div
|
||||
@@ -343,6 +396,29 @@ export default function ImageLightbox({ url, name, onClose, onPrev, onNext, item
|
||||
›
|
||||
</button>
|
||||
)}
|
||||
{/* Text overlay */}
|
||||
{showTextOverlay && displayText && (
|
||||
<div
|
||||
className="absolute bottom-4 left-4 right-4 z-10 rounded-xl p-4"
|
||||
style={{ backgroundColor: 'rgba(0,0,0,0.75)' }}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{extractedText && translatedText && (
|
||||
<div className="flex justify-end mb-2">
|
||||
<button
|
||||
onClick={() => setShowOriginal((v) => !v)}
|
||||
className="text-xs px-2 py-0.5 rounded-full"
|
||||
style={{ backgroundColor: 'rgba(255,255,255,0.15)', color: 'rgba(255,255,255,0.7)' }}
|
||||
>
|
||||
{showOriginal ? 'Show Translation' : 'Show Original'}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
<p className="text-sm whitespace-pre-wrap" style={{ color: 'rgba(255,255,255,0.9)' }}>
|
||||
{displayText}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user