Converge reader navigation buttons

This commit is contained in:
Richard R 2026-06-01 11:05:43 -06:00
parent d2d06e15b6
commit fdceed6067
4 changed files with 23 additions and 21 deletions

View file

@ -20,6 +20,7 @@ import { RateLimitBanner } from '@/components/auth/RateLimitBanner';
import { useAuthRateLimit } from '@/contexts/AuthRateLimitContext'; import { useAuthRateLimit } from '@/contexts/AuthRateLimitContext';
import { useFeatureFlag } from '@/contexts/RuntimeConfigContext'; import { useFeatureFlag } from '@/contexts/RuntimeConfigContext';
import { useUnmountCleanupRef } from '@/hooks/useUnmountCleanupRef'; import { useUnmountCleanupRef } from '@/hooks/useUnmountCleanupRef';
import { buttonClass } from '@/components/ui';
import { useEpubDocument } from './useEpubDocument'; import { useEpubDocument } from './useEpubDocument';
export default function EPUBPage() { export default function EPUBPage() {
@ -173,9 +174,9 @@ export default function EPUBPage() {
<p className="text-danger mb-4">{error}</p> <p className="text-danger mb-4">{error}</p>
<Link <Link
href="/app" href="/app"
className="inline-flex items-center px-3 py-1 bg-surface text-foreground rounded-lg hover:bg-accent-wash transition duration-base ease-standard hover:text-accent" className={buttonClass({ variant: 'secondary', size: 'md', className: 'gap-2' })}
> >
<svg className="w-4 h-4 mr-2 text-soft" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-4 h-4 text-soft" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
</svg> </svg>
Back to Documents Back to Documents
@ -190,10 +191,10 @@ export default function EPUBPage() {
left={ left={
<Link <Link
href="/app" href="/app"
className="inline-flex items-center py-1 px-2 rounded-md border border-line bg-surface text-foreground text-xs hover:bg-accent-wash transition duration-base ease-standard hover:text-accent" className={buttonClass({ variant: 'secondary', size: 'sm', className: 'gap-2' })}
aria-label="Back to documents" aria-label="Back to documents"
> >
<svg className="w-3 h-3 mr-2" fill="currentColor" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-3 h-3" fill="currentColor" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
</svg> </svg>
Documents Documents

View file

@ -18,6 +18,7 @@ import { AudiobookExportModal } from '@/components/AudiobookExportModal';
import { useAuthRateLimit } from '@/contexts/AuthRateLimitContext'; import { useAuthRateLimit } from '@/contexts/AuthRateLimitContext';
import { useFeatureFlag } from '@/contexts/RuntimeConfigContext'; import { useFeatureFlag } from '@/contexts/RuntimeConfigContext';
import { useUnmountCleanupRef } from '@/hooks/useUnmountCleanupRef'; import { useUnmountCleanupRef } from '@/hooks/useUnmountCleanupRef';
import { buttonClass } from '@/components/ui';
import type { TTSAudiobookChapter } from '@/types/tts'; import type { TTSAudiobookChapter } from '@/types/tts';
import type { AudiobookGenerationSettings } from '@/types/client'; import type { AudiobookGenerationSettings } from '@/types/client';
import { useHtmlDocument } from './useHtmlDocument'; import { useHtmlDocument } from './useHtmlDocument';
@ -160,9 +161,9 @@ export default function HTMLPage() {
<p className="text-danger mb-4">{error}</p> <p className="text-danger mb-4">{error}</p>
<Link <Link
href="/app" href="/app"
className="inline-flex items-center px-3 py-1 bg-surface text-foreground rounded-lg hover:bg-accent-wash transition duration-base ease-standard hover:text-accent" className={buttonClass({ variant: 'secondary', size: 'md', className: 'gap-2' })}
> >
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
</svg> </svg>
Back to Documents Back to Documents
@ -177,10 +178,10 @@ export default function HTMLPage() {
left={ left={
<Link <Link
href="/app" href="/app"
className="inline-flex items-center py-1 px-2 rounded-md border border-line bg-surface text-foreground text-xs hover:bg-accent-wash transition duration-base ease-standard hover:text-accent" className={buttonClass({ variant: 'secondary', size: 'sm', className: 'gap-2' })}
aria-label="Back to documents" aria-label="Back to documents"
> >
<svg className="w-3 h-3 mr-2" fill="currentColor" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-3 h-3" fill="currentColor" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
</svg> </svg>
Documents Documents

View file

@ -20,6 +20,7 @@ import { RateLimitBanner } from '@/components/auth/RateLimitBanner';
import { useAuthRateLimit } from '@/contexts/AuthRateLimitContext'; import { useAuthRateLimit } from '@/contexts/AuthRateLimitContext';
import { useFeatureFlag } from '@/contexts/RuntimeConfigContext'; import { useFeatureFlag } from '@/contexts/RuntimeConfigContext';
import { LoadingSpinner } from '@/components/Spinner'; import { LoadingSpinner } from '@/components/Spinner';
import { buttonClass } from '@/components/ui';
import { import {
FORCE_REPARSE_CONFIRM_MESSAGE, FORCE_REPARSE_CONFIRM_MESSAGE,
FORCE_REPARSE_CONFIRM_TEXT, FORCE_REPARSE_CONFIRM_TEXT,
@ -255,9 +256,9 @@ export default function PDFViewerPage() {
<Link <Link
href="/app" href="/app"
onClick={handleBackToDocuments} onClick={handleBackToDocuments}
className="inline-flex items-center px-3 py-1 bg-surface text-foreground rounded-lg hover:bg-accent-wash transition duration-base ease-standard hover:text-accent" className={buttonClass({ variant: 'secondary', size: 'md', className: 'gap-2' })}
> >
<svg className="w-4 h-4 mr-2 text-soft" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-4 h-4 text-soft" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
</svg> </svg>
Back to Documents Back to Documents
@ -384,10 +385,10 @@ export default function PDFViewerPage() {
<Link <Link
href="/app" href="/app"
onClick={handleBackToDocuments} onClick={handleBackToDocuments}
className="inline-flex items-center py-1 px-2 rounded-md border border-line bg-surface text-foreground text-xs hover:bg-accent-wash transition duration-base ease-standard hover:text-accent" className={buttonClass({ variant: 'secondary', size: 'sm', className: 'gap-2' })}
aria-label="Back to documents" aria-label="Back to documents"
> >
<svg className="w-3 h-3 mr-2" fill="currentColor" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-3 h-3" fill="currentColor" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
</svg> </svg>
Documents Documents

View file

@ -9,6 +9,7 @@ import { useEPUBTheme, getThemeStyles } from '@/hooks/epub/useEPUBTheme';
import { useEPUBResize } from '@/hooks/epub/useEPUBResize'; import { useEPUBResize } from '@/hooks/epub/useEPUBResize';
import { DotsVerticalIcon, ChevronLeftIcon, ChevronRightIcon } from '@/components/icons/Icons'; import { DotsVerticalIcon, ChevronLeftIcon, ChevronRightIcon } from '@/components/icons/Icons';
import type { EpubDocumentState } from '@/app/(app)/epub/[id]/useEpubDocument'; import type { EpubDocumentState } from '@/app/(app)/epub/[id]/useEpubDocument';
import { ToolbarButton } from '@/components/ui';
const ReactReader = dynamic(() => import('react-reader').then(mod => mod.ReactReader), { const ReactReader = dynamic(() => import('react-reader').then(mod => mod.ReactReader), {
ssr: false, ssr: false,
@ -160,36 +161,34 @@ export function EPUBViewer({ className = '', epubState }: EPUBViewerProps) {
<div className={`h-full flex flex-col relative z-0 ${className}`} ref={containerRef}> <div className={`h-full flex flex-col relative z-0 ${className}`} ref={containerRef}>
<div className="flex items-center justify-between px-2 py-1 border-b border-line-soft bg-surface text-xs text-soft"> <div className="flex items-center justify-between px-2 py-1 border-b border-line-soft bg-surface text-xs text-soft">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<button <ToolbarButton
type="button" type="button"
onClick={() => setIsTocOpen(open => !open)} onClick={() => setIsTocOpen(open => !open)}
className="inline-flex items-center py-1 px-1 rounded-md border border-line bg-surface text-foreground text-xs hover:bg-accent-wash transition duration-base ease-standard transform hover:text-accent"
aria-label={isTocOpen ? 'Hide chapters' : 'Show chapters'} aria-label={isTocOpen ? 'Hide chapters' : 'Show chapters'}
className="px-1"
> >
<DotsVerticalIcon className="w-4 h-4" /> <DotsVerticalIcon className="w-4 h-4" />
</button> </ToolbarButton>
<button <ToolbarButton
type="button" type="button"
onClick={() => handleLocationChanged('prev')} onClick={() => handleLocationChanged('prev')}
className="inline-flex items-center py-1 px-2 rounded-md border border-line bg-surface text-foreground text-xs hover:bg-accent-wash transition duration-base ease-standard transform hover:text-accent"
aria-label="Previous section" aria-label="Previous section"
> >
<ChevronLeftIcon className="w-4 h-4" /> <ChevronLeftIcon className="w-4 h-4" />
</button> </ToolbarButton>
</div> </div>
{currDocPages !== undefined && typeof currDocPage === 'number' && ( {currDocPages !== undefined && typeof currDocPage === 'number' && (
<span className="px-2 tabular-nums"> <span className="px-2 tabular-nums">
{currDocPage} / {currDocPages} {currDocPage} / {currDocPages}
</span> </span>
)} )}
<button <ToolbarButton
type="button" type="button"
onClick={() => handleLocationChanged('next')} onClick={() => handleLocationChanged('next')}
className="inline-flex items-center py-1 px-2 rounded-md border border-line bg-surface text-foreground text-xs hover:bg-accent-wash transition duration-base ease-standard transform hover:text-accent"
aria-label="Next section" aria-label="Next section"
> >
<ChevronRightIcon className="w-4 h-4" /> <ChevronRightIcon className="w-4 h-4" />
</button> </ToolbarButton>
</div> </div>
{isTocOpen && tocRef.current && tocRef.current.length > 0 && ( {isTocOpen && tocRef.current && tocRef.current.length > 0 && (
<div className="border-b border-line-soft bg-background text-xs overflow-y-auto max-h-64 p-2"> <div className="border-b border-line-soft bg-background text-xs overflow-y-auto max-h-64 p-2">