phase 1: motion language
This commit is contained in:
parent
7bd798b9d1
commit
11e138ae36
41 changed files with 181 additions and 184 deletions
|
|
@ -173,7 +173,7 @@ export default function EPUBPage() {
|
|||
<p className="text-red-500 mb-4">{error}</p>
|
||||
<Link
|
||||
href="/app"
|
||||
className="inline-flex items-center px-3 py-1 bg-base text-foreground rounded-lg hover:bg-offbase transition-all duration-200 ease-in-out hover:scale-[1.04] hover:text-accent"
|
||||
className="inline-flex items-center px-3 py-1 bg-base text-foreground rounded-lg hover:bg-offbase transition duration-base ease-standard hover:text-accent"
|
||||
>
|
||||
<svg className="w-4 h-4 mr-2 text-muted" 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" />
|
||||
|
|
@ -190,7 +190,7 @@ export default function EPUBPage() {
|
|||
left={
|
||||
<Link
|
||||
href="/app"
|
||||
className="inline-flex items-center py-1 px-2 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase transition-all duration-200 ease-in-out hover:scale-[1.04] hover:text-accent"
|
||||
className="inline-flex items-center py-1 px-2 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase transition duration-base ease-standard hover:text-accent"
|
||||
aria-label="Back to documents"
|
||||
>
|
||||
<svg className="w-3 h-3 mr-2" fill="currentColor" stroke="currentColor" viewBox="0 0 24 24">
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ export default function HTMLPage() {
|
|||
<p className="text-red-500 mb-4">{error}</p>
|
||||
<Link
|
||||
href="/app"
|
||||
className="inline-flex items-center px-3 py-1 bg-base text-foreground rounded-lg hover:bg-offbase transition-all duration-200 ease-in-out hover:scale-[1.04] hover:text-accent"
|
||||
className="inline-flex items-center px-3 py-1 bg-base text-foreground rounded-lg hover:bg-offbase transition duration-base ease-standard hover:text-accent"
|
||||
>
|
||||
<svg className="w-4 h-4 mr-2" 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" />
|
||||
|
|
@ -177,7 +177,7 @@ export default function HTMLPage() {
|
|||
left={
|
||||
<Link
|
||||
href="/app"
|
||||
className="inline-flex items-center py-1 px-2 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase transition-all duration-200 ease-in-out hover:scale-[1.04] hover:text-accent"
|
||||
className="inline-flex items-center py-1 px-2 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase transition duration-base ease-standard hover:text-accent"
|
||||
aria-label="Back to documents"
|
||||
>
|
||||
<svg className="w-3 h-3 mr-2" fill="currentColor" stroke="currentColor" viewBox="0 0 24 24">
|
||||
|
|
|
|||
|
|
@ -255,7 +255,7 @@ export default function PDFViewerPage() {
|
|||
<Link
|
||||
href="/app"
|
||||
onClick={handleBackToDocuments}
|
||||
className="inline-flex items-center px-3 py-1 bg-base text-foreground rounded-lg hover:bg-offbase transition-all duration-200 ease-in-out hover:scale-[1.04] hover:text-accent"
|
||||
className="inline-flex items-center px-3 py-1 bg-base text-foreground rounded-lg hover:bg-offbase transition duration-base ease-standard hover:text-accent"
|
||||
>
|
||||
<svg className="w-4 h-4 mr-2 text-muted" 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" />
|
||||
|
|
@ -309,7 +309,7 @@ export default function PDFViewerPage() {
|
|||
|
||||
return (
|
||||
<div className="h-full w-full bg-base">
|
||||
<div className={`mx-auto flex h-full items-center px-4 py-6 transition-all duration-300 ease-out ${showDetailedParseLoader ? 'max-w-lg' : 'max-w-md'}`}>
|
||||
<div className={`mx-auto flex h-full items-center px-4 py-6 transition duration-slow ease-standard ${showDetailedParseLoader ? 'max-w-lg' : 'max-w-md'}`}>
|
||||
{showDetailedParseLoader ? (
|
||||
<div className="w-full rounded-xl border border-offbase bg-offbase/95 shadow-sm overflow-hidden">
|
||||
<div className="h-1 bg-[linear-gradient(90deg,var(--accent),transparent_80%)]" />
|
||||
|
|
@ -331,7 +331,7 @@ export default function PDFViewerPage() {
|
|||
</div>
|
||||
<div className="h-2 w-full rounded-full bg-offbase overflow-hidden">
|
||||
<div
|
||||
className="h-full bg-accent transition-all duration-300 ease-out"
|
||||
className="h-full bg-accent transition duration-slow ease-standard"
|
||||
style={{ width: `${hasMeasuredProgress ? progressPercent : 6}%` }}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -354,7 +354,7 @@ export default function PDFViewerPage() {
|
|||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="w-full rounded-xl border border-offbase bg-offbase/95 p-4 shadow-sm transition-all duration-300 ease-out overflow-hidden">
|
||||
<div className="w-full rounded-xl border border-offbase bg-offbase/95 p-4 shadow-sm transition duration-slow ease-standard overflow-hidden">
|
||||
<div className="h-0.5 -mx-4 -mt-4 mb-3 bg-[linear-gradient(90deg,var(--accent),transparent_75%)]" />
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
|
|
@ -384,7 +384,7 @@ export default function PDFViewerPage() {
|
|||
<Link
|
||||
href="/app"
|
||||
onClick={handleBackToDocuments}
|
||||
className="inline-flex items-center py-1 px-2 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase transition-all duration-200 ease-in-out hover:scale-[1.04] hover:text-accent"
|
||||
className="inline-flex items-center py-1 px-2 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase transition duration-base ease-standard hover:text-accent"
|
||||
aria-label="Back to documents"
|
||||
>
|
||||
<svg className="w-3 h-3 mr-2" fill="currentColor" stroke="currentColor" viewBox="0 0 24 24">
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ function SignInContent() {
|
|||
type="submit"
|
||||
disabled={isAnyLoading}
|
||||
onClick={handleSignIn}
|
||||
className={buttonClass({ variant: 'primary', size: 'md', className: 'w-full hover:scale-[1.02]' })}
|
||||
className={buttonClass({ variant: 'primary', size: 'md', className: 'w-full' })}
|
||||
>
|
||||
{loadingEmail ? <LoadingSpinner className="w-4 h-4 mx-auto" /> : 'Connect'}
|
||||
</Button>
|
||||
|
|
@ -202,7 +202,7 @@ function SignInContent() {
|
|||
className={buttonClass({
|
||||
variant: 'outline',
|
||||
size: 'md',
|
||||
className: 'w-full hover:scale-[1.02] flex items-center justify-center gap-2',
|
||||
className: 'w-full flex items-center justify-center gap-2',
|
||||
})}
|
||||
>
|
||||
{loadingGithub ? (
|
||||
|
|
@ -222,7 +222,7 @@ function SignInContent() {
|
|||
type="button"
|
||||
disabled={isAnyLoading}
|
||||
onClick={handleAnonymousContinue}
|
||||
className={buttonClass({ variant: 'outline', size: 'md', className: 'w-full hover:scale-[1.02]' })}
|
||||
className={buttonClass({ variant: 'outline', size: 'md', className: 'w-full' })}
|
||||
>
|
||||
{loadingAnonymous ? <LoadingSpinner className="w-4 h-4 mx-auto" /> : 'Continue anonymously'}
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ export default function SignUpPage() {
|
|||
type="submit"
|
||||
disabled={loading}
|
||||
onClick={handleSignUp}
|
||||
className={buttonClass({ variant: 'primary', size: 'md', className: 'w-full hover:scale-[1.02]' })}
|
||||
className={buttonClass({ variant: 'primary', size: 'md', className: 'w-full' })}
|
||||
>
|
||||
{loading ? <LoadingSpinner className="w-4 h-4 mx-auto" /> : 'Create Account'}
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -560,7 +560,7 @@ export function AudiobookExportModal({
|
|||
disabled={chapters.length > 0 || settingsLocked}
|
||||
>
|
||||
<div className="relative">
|
||||
<ListboxButton className="relative cursor-pointer rounded-lg bg-base py-1.5 pl-3 pr-10 text-left text-foreground focus:outline-none focus:ring-2 focus:ring-accent transform transition-transform duration-200 ease-in-out hover:scale-[1.01] hover:text-accent w-full">
|
||||
<ListboxButton className="relative cursor-pointer rounded-lg bg-base py-1.5 pl-3 pr-10 text-left text-foreground focus:outline-none focus:ring-2 focus:ring-accent transform transition-transform duration-base ease-standard hover:text-accent w-full">
|
||||
<span className="block truncate text-sm font-medium">{format.toUpperCase()}</span>
|
||||
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
|
||||
<ChevronUpDownIcon className="h-4 w-4 text-muted" />
|
||||
|
|
@ -568,7 +568,7 @@ export function AudiobookExportModal({
|
|||
</ListboxButton>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
leave="transition ease-in duration-100"
|
||||
leave="transition ease-standard duration-fast"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
|
|
@ -673,7 +673,7 @@ export function AudiobookExportModal({
|
|||
className="flex-1 inline-flex justify-center rounded-lg bg-accent px-3 py-2 text-sm
|
||||
font-medium text-background hover:bg-secondary-accent focus:outline-none
|
||||
focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2
|
||||
transform transition-transform duration-200 ease-in-out hover:scale-[1.01] disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100"
|
||||
transform transition-transform duration-base ease-standard disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
Start Generation
|
||||
</Button>
|
||||
|
|
@ -685,7 +685,7 @@ export function AudiobookExportModal({
|
|||
className="flex-1 inline-flex justify-center rounded-lg bg-accent px-3 py-2 text-sm
|
||||
font-medium text-background hover:bg-secondary-accent focus:outline-none
|
||||
focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2
|
||||
transform transition-transform duration-200 ease-in-out hover:scale-[1.01] disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100"
|
||||
transform transition-transform duration-base ease-standard disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
Resume
|
||||
</Button>
|
||||
|
|
@ -697,7 +697,7 @@ export function AudiobookExportModal({
|
|||
className="inline-flex justify-center rounded-lg border border-red-500 bg-transparent px-3 py-2 text-sm
|
||||
font-medium text-red-500 hover:bg-red-500 hover:text-background focus:outline-none
|
||||
focus-visible:ring-2 focus-visible:ring-red-500 focus-visible:ring-offset-2
|
||||
transform transition-transform duration-200 ease-in-out hover:scale-[1.01]"
|
||||
transform transition-transform duration-base ease-standard"
|
||||
title="Delete all generated chapters/pages for this document"
|
||||
>
|
||||
Reset
|
||||
|
|
@ -760,7 +760,7 @@ export function AudiobookExportModal({
|
|||
<Button
|
||||
onClick={() => handleRegenerateChapter(chapter)}
|
||||
disabled={regeneratingChapter !== null || chapter.status === 'generating' || isGenerating}
|
||||
className="inline-flex items-center justify-center rounded-full bg-offbase text-accent hover:bg-accent/20 p-1.5 focus:outline-none focus-visible:ring-2 focus-visible:ring-accent transform transition-transform duration-200 ease-in-out hover:scale-[1.04] disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
className="inline-flex items-center justify-center rounded-full bg-offbase text-accent hover:bg-accent/20 p-1.5 focus:outline-none focus-visible:ring-2 focus-visible:ring-accent transform transition-transform duration-base ease-standard disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
title={chapter.status === 'generating' ? 'Generating...' : 'Regenerate this chapter'}
|
||||
>
|
||||
<RefreshIcon className={`h-4 w-4 ${regeneratingChapter === chapter.index || chapter.status === 'generating' ? 'animate-spin' : ''}`} />
|
||||
|
|
@ -782,17 +782,17 @@ export function AudiobookExportModal({
|
|||
{((onRegenerateChapter && !isGenerating) || chapter.status === 'completed') && (
|
||||
<Menu as="div" className="relative inline-block text-left">
|
||||
<MenuButton
|
||||
className="inline-flex items-center justify-center rounded-md p-1.5 hover:bg-background focus:outline-none focus-visible:ring-2 focus-visible:ring-accent text-muted hover:text-foreground transform transition-transform duration-200 ease-in-out hover:scale-[1.04]"
|
||||
className="inline-flex items-center justify-center rounded-md p-1.5 hover:bg-background focus:outline-none focus-visible:ring-2 focus-visible:ring-accent text-muted hover:text-foreground transform transition-transform duration-base ease-standard"
|
||||
title="Chapter actions"
|
||||
>
|
||||
<DotsVerticalIcon className="h-5 w-5" />
|
||||
</MenuButton>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
enter="transition ease-standard duration-fast"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-75"
|
||||
leave="transition ease-standard duration-fast"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
|
|
@ -873,10 +873,10 @@ export function AudiobookExportModal({
|
|||
onClick={handleDownloadComplete}
|
||||
disabled={isCombining}
|
||||
className="w-full inline-flex justify-center items-center space-x-2 rounded-lg bg-accent px-3 py-1.5 text-sm
|
||||
disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100
|
||||
disabled:opacity-50 disabled:cursor-not-allowed
|
||||
font-medium text-background hover:bg-secondary-accent focus:outline-none
|
||||
focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2
|
||||
transform transition-transform duration-200 ease-in-out hover:scale-[1.04] hover:text-background"
|
||||
transform transition-transform duration-base ease-standard hover:text-background"
|
||||
>
|
||||
<DownloadIcon className="h-5 w-5" />
|
||||
<span>{isCombining ? 'Combining chapters...' : `Full Download (${format.toUpperCase()})`}</span>
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export function CodeBlock({ children }: { children: string }) {
|
|||
className={buttonClass({
|
||||
variant: 'secondary',
|
||||
size: 'icon',
|
||||
className: 'absolute top-2 right-2 h-7 w-7 opacity-0 group-hover:opacity-100 focus:opacity-100 hover:scale-[1.05]',
|
||||
className: 'absolute top-2 right-2 h-7 w-7 opacity-0 group-hover:opacity-100 focus:opacity-100',
|
||||
})}
|
||||
title="Copy to clipboard"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ export function ColorPicker({ value, field, label, onChange }: ColorPickerProps)
|
|||
<Popover className="relative flex items-center">
|
||||
<PopoverButton className="cursor-pointer group focus:outline-none">
|
||||
<div
|
||||
className="w-6 h-6 rounded-full border-2 transition-all duration-150 group-hover:scale-110 group-focus-visible:ring-2 group-focus-visible:ring-offset-1"
|
||||
className="w-6 h-6 rounded-full border-2 transition duration-fast group-focus-visible:ring-2 group-focus-visible:ring-offset-1"
|
||||
style={{
|
||||
backgroundColor: value,
|
||||
borderColor: isLightColor(value) ? '#00000022' : '#ffffff22',
|
||||
|
|
@ -92,7 +92,7 @@ export function ColorPicker({ value, field, label, onChange }: ColorPickerProps)
|
|||
anchor="bottom start"
|
||||
transition
|
||||
className="z-[60] mt-2 w-56 rounded-xl shadow-xl border border-offbase bg-background p-3 space-y-3
|
||||
transition duration-150 ease-out data-[closed]:opacity-0 data-[closed]:scale-95"
|
||||
transition duration-fast ease-standard data-[closed]:opacity-0 data-[closed]:scale-95"
|
||||
>
|
||||
{/* Label */}
|
||||
<div className="flex items-center justify-between">
|
||||
|
|
@ -107,7 +107,7 @@ export function ColorPicker({ value, field, label, onChange }: ColorPickerProps)
|
|||
className="p-1"
|
||||
aria-label="Open system color picker"
|
||||
>
|
||||
<PaletteIcon className="w-4 h-4 text-muted transform transition-transform duration-200 ease-in-out hover:scale-[1.09] hover:text-accent" />
|
||||
<PaletteIcon className="w-4 h-4 text-muted transform transition-transform duration-base ease-standard hover:text-accent" />
|
||||
</button>
|
||||
<input
|
||||
ref={nativeRef}
|
||||
|
|
@ -130,7 +130,7 @@ export function ColorPicker({ value, field, label, onChange }: ColorPickerProps)
|
|||
key={color}
|
||||
type="button"
|
||||
onClick={() => onChange(color)}
|
||||
className="group/swatch relative w-full aspect-square rounded-full transition-transform duration-100 hover:scale-125 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-1"
|
||||
className="group/swatch relative w-full aspect-square rounded-full transition-transform duration-fast focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-1"
|
||||
style={{
|
||||
backgroundColor: color,
|
||||
boxShadow: selected ? '0 0 0 2px var(--background), 0 0 0 4px var(--foreground)' : undefined,
|
||||
|
|
|
|||
|
|
@ -41,10 +41,10 @@ export function ConfirmDialog({
|
|||
>
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enter="ease-standard duration-slow"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leave="ease-standard duration-base"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
|
|
@ -55,14 +55,14 @@ export function ConfirmDialog({
|
|||
<div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4">
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enter="ease-standard duration-slow"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leave="ease-standard duration-base"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<DialogPanel role='dialog' className="w-full max-w-md transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition-all">
|
||||
<DialogPanel role='dialog' className="w-full max-w-md transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition">
|
||||
<DialogTitle
|
||||
as="h3"
|
||||
className="text-lg font-semibold leading-6 text-foreground"
|
||||
|
|
@ -79,7 +79,6 @@ export function ConfirmDialog({
|
|||
className={buttonClass({
|
||||
variant: 'outline',
|
||||
size: 'sm',
|
||||
className: 'hover:scale-[1.04]',
|
||||
})}
|
||||
onClick={onClose}
|
||||
>
|
||||
|
|
@ -90,7 +89,7 @@ export function ConfirmDialog({
|
|||
className={buttonClass({
|
||||
variant: isDangerous ? 'danger' : 'primary',
|
||||
size: 'sm',
|
||||
className: 'text-wrap hover:scale-[1.04]',
|
||||
className: 'text-wrap',
|
||||
})}
|
||||
onClick={onConfirm}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -35,10 +35,10 @@ export function CookieConsentBanner() {
|
|||
<Transition
|
||||
as="div"
|
||||
show={show}
|
||||
enter="transition ease-out duration-300 transform"
|
||||
enter="transition ease-standard duration-slow transform"
|
||||
enterFrom="translate-y-full opacity-0"
|
||||
enterTo="translate-y-0 opacity-100"
|
||||
leave="transition ease-in duration-200 transform"
|
||||
leave="transition ease-standard duration-base transform"
|
||||
leaveFrom="translate-y-0 opacity-100"
|
||||
leaveTo="translate-y-full opacity-0"
|
||||
className="fixed bottom-0 left-0 right-0 z-[60] px-4 pt-4 pb-[max(1rem,env(safe-area-inset-bottom))] md:px-6 md:pt-6 md:pb-[max(1.5rem,env(safe-area-inset-bottom))]"
|
||||
|
|
|
|||
|
|
@ -81,10 +81,10 @@ export function PrivacyModal({ isOpen, onAccept, onDismiss }: PrivacyModalProps)
|
|||
<Dialog as="div" className="relative z-[80]" onClose={onDismiss ?? (() => { })}>
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enter="ease-standard duration-slow"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leave="ease-standard duration-base"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
|
|
@ -95,14 +95,14 @@ export function PrivacyModal({ isOpen, onAccept, onDismiss }: PrivacyModalProps)
|
|||
<div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4">
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enter="ease-standard duration-slow"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leave="ease-standard duration-base"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<DialogPanel data-testid="privacy-modal" className="w-full max-w-md transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition-all">
|
||||
<DialogPanel data-testid="privacy-modal" className="w-full max-w-md transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition">
|
||||
<DialogTitle
|
||||
as="h3"
|
||||
className="text-lg font-semibold leading-6 text-foreground"
|
||||
|
|
@ -143,7 +143,7 @@ export function PrivacyModal({ isOpen, onAccept, onDismiss }: PrivacyModalProps)
|
|||
font-medium text-background hover:bg-secondary-accent
|
||||
disabled:opacity-50 disabled:cursor-not-allowed
|
||||
focus:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2
|
||||
transform transition-transform duration-200 ease-in-out enabled:hover:scale-[1.04]"
|
||||
transform transition-transform duration-base ease-standard"
|
||||
onClick={handleAccept}
|
||||
>
|
||||
Continue
|
||||
|
|
@ -195,10 +195,10 @@ export function showPrivacyModal(): void {
|
|||
<Dialog as="div" className="relative z-50" onClose={handleClose}>
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enter="ease-standard duration-slow"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leave="ease-standard duration-base"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
|
|
@ -209,14 +209,14 @@ export function showPrivacyModal(): void {
|
|||
<div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4">
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enter="ease-standard duration-slow"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leave="ease-standard duration-base"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<DialogPanel className="w-full max-w-md transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition-all">
|
||||
<DialogPanel className="w-full max-w-md transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition">
|
||||
<DialogTitle
|
||||
as="h3"
|
||||
className="text-lg font-semibold leading-6 text-foreground"
|
||||
|
|
@ -232,7 +232,7 @@ export function showPrivacyModal(): void {
|
|||
className="inline-flex justify-center rounded-lg bg-accent px-4 py-2 text-sm
|
||||
font-medium text-background hover:bg-secondary-accent focus:outline-none
|
||||
focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2
|
||||
transform transition-transform duration-200 ease-in-out hover:scale-[1.04]"
|
||||
transform transition-transform duration-base ease-standard"
|
||||
onClick={handleClose}
|
||||
>
|
||||
Close
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ export function ProgressCard({
|
|||
className={buttonClass({
|
||||
variant: 'ghost',
|
||||
size: 'xs',
|
||||
className: 'shrink-0 hover:bg-background/50 hover:scale-[1.04]',
|
||||
className: 'shrink-0 hover:bg-background/50',
|
||||
})}
|
||||
onClick={(e) => onCancel(e)}
|
||||
>
|
||||
|
|
@ -68,7 +68,7 @@ export function ProgressCard({
|
|||
{/* Progress bar */}
|
||||
<div className="w-full bg-background rounded-full overflow-hidden h-1.5">
|
||||
<div
|
||||
className="h-full bg-accent transition-all duration-300 ease-out"
|
||||
className="h-full bg-accent transition duration-slow ease-standard"
|
||||
style={{ width: `${progress}%` }}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -32,10 +32,10 @@ export function ProgressPopup({
|
|||
<Transition
|
||||
show={isOpen}
|
||||
as={Fragment}
|
||||
enter="transform transition ease-out duration-300"
|
||||
enter="transform transition ease-standard duration-slow"
|
||||
enterFrom="opacity-0 -translate-y-4"
|
||||
enterTo="opacity-100 translate-y-0"
|
||||
leave="transform transition ease-in duration-200"
|
||||
leave="transform transition ease-standard duration-base"
|
||||
leaveFrom="opacity-100 translate-y-0"
|
||||
leaveTo="opacity-0 -translate-y-4"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -142,11 +142,11 @@ export function SettingsTrigger({
|
|||
return (
|
||||
<Button
|
||||
onClick={onOpen}
|
||||
className={`inline-flex items-center py-1 px-2 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase hover:text-accent transition-transform transition-colors duration-200 ease-out hover:scale-[1.01] ${className}`}
|
||||
className={`inline-flex items-center py-1 px-2 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase hover:text-accent transition-transform transition-colors duration-base ease-standard ${className}`}
|
||||
aria-label="Settings"
|
||||
tabIndex={0}
|
||||
>
|
||||
<SettingsIcon className="w-4 h-4 transition-transform duration-200 ease-out hover:scale-[1.01] hover:rotate-45" />
|
||||
<SettingsIcon className="w-4 h-4 transition-transform duration-base ease-standard hover:rotate-45" />
|
||||
{triggerLabel && <span className="ml-2">{triggerLabel}</span>}
|
||||
</Button>
|
||||
);
|
||||
|
|
@ -519,10 +519,10 @@ export function SettingsModal({
|
|||
>
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enter="ease-standard duration-slow"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leave="ease-standard duration-base"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
|
|
@ -533,14 +533,14 @@ export function SettingsModal({
|
|||
<div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4">
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enter="ease-standard duration-slow"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leave="ease-standard duration-base"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<DialogPanel data-testid="settings-modal" className="relative w-full max-w-4xl transform rounded-xl bg-base text-left align-middle shadow-xl transition-all overflow-hidden border border-offbase">
|
||||
<DialogPanel data-testid="settings-modal" className="relative w-full max-w-4xl transform rounded-xl bg-base text-left align-middle shadow-xl transition overflow-hidden border border-offbase">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between px-5 py-3 border-b border-offbase">
|
||||
<div className="flex items-baseline gap-4">
|
||||
|
|
@ -666,7 +666,7 @@ export function SettingsModal({
|
|||
</ListboxButton>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
leave="transition ease-in duration-100"
|
||||
leave="transition ease-standard duration-fast"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
|
|
@ -782,7 +782,7 @@ export function SettingsModal({
|
|||
</ListboxButton>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
leave="transition ease-in duration-100"
|
||||
leave="transition ease-standard duration-fast"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
|
|
@ -908,7 +908,7 @@ export function SettingsModal({
|
|||
return (
|
||||
<button
|
||||
onClick={() => setTheme(systemTheme.id)}
|
||||
className={`flex items-center gap-2 rounded-lg px-2 py-1.5 w-full text-left transition-all duration-200 ease-in-out transform hover:scale-[1.02] border
|
||||
className={`flex items-center gap-2 rounded-lg px-2 py-1.5 w-full text-left transition duration-base ease-standard transform border
|
||||
${isActive
|
||||
? 'border-accent'
|
||||
: 'border-offbase hover:border-muted'
|
||||
|
|
@ -952,7 +952,7 @@ export function SettingsModal({
|
|||
setTheme('custom');
|
||||
setIsCustomExpanded(true);
|
||||
}}
|
||||
className={`flex items-center gap-2 rounded-lg px-2 py-1.5 flex-1 text-left transition-all duration-200 ease-in-out transform hover:scale-[1.02] border
|
||||
className={`flex items-center gap-2 rounded-lg px-2 py-1.5 flex-1 text-left transition duration-base ease-standard transform border
|
||||
${isActive
|
||||
? 'border-accent'
|
||||
: 'border-offbase hover:border-muted'
|
||||
|
|
@ -984,7 +984,7 @@ export function SettingsModal({
|
|||
style={{ color: colors.muted, backgroundColor: colors.base }}
|
||||
aria-label={isCustomExpanded ? 'Collapse color picker' : 'Expand color picker'}
|
||||
>
|
||||
<svg className={`w-3.5 h-3.5 transition-transform duration-200 ${isCustomExpanded ? 'rotate-180' : ''}`} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<svg className={`w-3.5 h-3.5 transition-transform duration-base ${isCustomExpanded ? 'rotate-180' : ''}`} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
|
|
@ -1052,7 +1052,7 @@ export function SettingsModal({
|
|||
<button
|
||||
key={t.id}
|
||||
onClick={() => setTheme(t.id)}
|
||||
className={`flex items-center gap-2 rounded-lg px-2 py-1.5 text-left transition-all duration-200 ease-in-out transform hover:scale-[1.02] border
|
||||
className={`flex items-center gap-2 rounded-lg px-2 py-1.5 text-left transition duration-base ease-standard transform border
|
||||
${isActive
|
||||
? 'border-accent'
|
||||
: 'border-offbase hover:border-muted'
|
||||
|
|
@ -1094,7 +1094,7 @@ export function SettingsModal({
|
|||
<button
|
||||
key={t.id}
|
||||
onClick={() => setTheme(t.id)}
|
||||
className={`flex items-center gap-2 rounded-lg px-2 py-1.5 text-left transition-all duration-200 ease-in-out transform hover:scale-[1.02] border
|
||||
className={`flex items-center gap-2 rounded-lg px-2 py-1.5 text-left transition duration-base ease-standard transform border
|
||||
${isActive
|
||||
? 'border-accent'
|
||||
: 'border-offbase hover:border-muted'
|
||||
|
|
@ -1247,7 +1247,7 @@ export function SettingsModal({
|
|||
<>
|
||||
<Button
|
||||
onClick={handleSignOut}
|
||||
className={buttonClass({ variant: 'outline', size: 'md', className: 'hover:scale-[1.04]' })}
|
||||
className={buttonClass({ variant: 'outline', size: 'md' })}
|
||||
>
|
||||
Disconnect account
|
||||
</Button>
|
||||
|
|
@ -1278,19 +1278,19 @@ export function SettingsModal({
|
|||
</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Link href="/signin">
|
||||
<Button className={buttonClass({ variant: 'outline', size: 'md', className: 'hover:scale-[1.04]' })}>
|
||||
<Button className={buttonClass({ variant: 'outline', size: 'md' })}>
|
||||
Connect
|
||||
</Button>
|
||||
</Link>
|
||||
{runtimeConfig.enableUserSignups && (
|
||||
<Link href="/signup">
|
||||
<Button className={buttonClass({ variant: 'primary', size: 'md', className: 'hover:scale-[1.04]' })}>
|
||||
<Button className={buttonClass({ variant: 'primary', size: 'md' })}>
|
||||
Create account
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
<Link href="/?redirect=false">
|
||||
<Button className={buttonClass({ variant: 'outline', size: 'md', className: 'hover:scale-[1.04]' })}>
|
||||
<Button className={buttonClass({ variant: 'outline', size: 'md' })}>
|
||||
Back to landing page
|
||||
</Button>
|
||||
</Link>
|
||||
|
|
@ -1439,7 +1439,7 @@ function SettingsChangelogPanel({
|
|||
<div className="flex items-center gap-3 px-4 py-3 border-b border-offbase bg-background">
|
||||
<Button
|
||||
onClick={onClose}
|
||||
className="inline-flex items-center justify-center rounded-md text-muted hover:text-accent hover:bg-base transition-all duration-200 ease-in-out transform hover:scale-[1.01]"
|
||||
className="inline-flex items-center justify-center rounded-md text-muted hover:text-accent hover:bg-base transition duration-base ease-standard transform"
|
||||
aria-label="Back to settings"
|
||||
title="Back"
|
||||
>
|
||||
|
|
@ -1472,7 +1472,7 @@ function SettingsChangelogPanel({
|
|||
href="https://github.com/richardr1126/openreader/releases"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="inline-flex text-xs font-medium text-accent hover:underline transition-all duration-200 ease-in-out transform hover:scale-[1.02]"
|
||||
className="inline-flex text-xs font-medium text-accent hover:underline transition duration-base ease-standard transform"
|
||||
>
|
||||
Open GitHub Releases
|
||||
</a>
|
||||
|
|
@ -1497,7 +1497,7 @@ function SettingsChangelogPanel({
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => setExpanded((prev) => ({ ...prev, [entry.tag_name]: !isExpanded }))}
|
||||
className="w-full text-left py-2 flex items-center gap-2 hover:bg-base transition-all duration-200 ease-in-out transform hover:scale-[1.01]"
|
||||
className="w-full text-left py-2 flex items-center gap-2 hover:bg-base transition duration-base ease-standard transform"
|
||||
>
|
||||
<ChevronRightIcon
|
||||
className={`w-3.5 h-3.5 shrink-0 text-muted transition-transform ${
|
||||
|
|
@ -1542,7 +1542,7 @@ function SettingsChangelogPanel({
|
|||
href={entry.html_url}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="inline-flex text-xs font-medium text-accent hover:underline transition-all duration-200 ease-in-out transform hover:scale-[1.02]"
|
||||
className="inline-flex text-xs font-medium text-accent hover:underline transition duration-base ease-standard transform"
|
||||
>
|
||||
View on GitHub
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -78,10 +78,10 @@ export default function ClaimDataModal({
|
|||
<Dialog as="div" className="relative z-[80]" onClose={onDismiss}>
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enter="ease-standard duration-slow"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leave="ease-standard duration-base"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
|
|
@ -92,14 +92,14 @@ export default function ClaimDataModal({
|
|||
<div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4">
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enter="ease-standard duration-slow"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leave="ease-standard duration-base"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<DialogPanel data-testid="claim-modal" className="w-full max-w-md transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition-all">
|
||||
<DialogPanel data-testid="claim-modal" className="w-full max-w-md transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition">
|
||||
<DialogTitle
|
||||
as="h3"
|
||||
className="text-lg font-semibold leading-6 text-foreground mb-4"
|
||||
|
|
@ -135,7 +135,7 @@ export default function ClaimDataModal({
|
|||
className="inline-flex justify-center rounded-lg bg-background px-3 py-1.5 text-sm
|
||||
font-medium text-foreground hover:bg-offbase focus:outline-none
|
||||
focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2
|
||||
transform transition-transform duration-200 ease-in-out hover:scale-[1.04] hover:text-accent
|
||||
transform transition-transform duration-base ease-standard hover:text-accent
|
||||
disabled:opacity-50"
|
||||
>
|
||||
Dismiss
|
||||
|
|
@ -148,7 +148,7 @@ export default function ClaimDataModal({
|
|||
className="inline-flex justify-center rounded-lg bg-accent px-3 py-1.5 text-sm
|
||||
font-medium text-background hover:bg-secondary-accent focus:outline-none
|
||||
focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2
|
||||
transform transition-transform duration-200 ease-in-out hover:scale-[1.04] hover:text-background
|
||||
transform transition-transform duration-base ease-standard hover:text-background
|
||||
disabled:opacity-50"
|
||||
>
|
||||
{isClaiming ? 'Claiming...' : 'Claim Data'}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export function RateLimitBanner({ className = '' }: RateLimitBannerProps) {
|
|||
href="/signup"
|
||||
className="inline-flex items-center px-2 py-1 text-xs font-medium rounded-md
|
||||
bg-accent text-background hover:bg-secondary-accent
|
||||
transform transition-transform duration-200 hover:scale-[1.04]"
|
||||
transform transition-transform duration-base"
|
||||
>
|
||||
Sign up for a higher limit
|
||||
</Link>
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ export function UserMenu({
|
|||
};
|
||||
|
||||
const rowClass =
|
||||
'w-full inline-flex items-center gap-2 px-2 py-1 rounded-md text-[12px] border border-transparent transition-all duration-200 ease-out text-left hover:scale-[1.01] hover:border-accent hover:bg-offbase hover:text-accent';
|
||||
'w-full inline-flex items-center gap-2 px-2 py-1 rounded-md text-[12px] border border-transparent transition duration-base ease-standard text-left hover:border-accent hover:bg-offbase hover:text-accent';
|
||||
|
||||
if (!session || session.user.isAnonymous) {
|
||||
if (variant === 'sidebar') {
|
||||
|
|
@ -55,13 +55,13 @@ export function UserMenu({
|
|||
return (
|
||||
<div className={`flex gap-2 ${className}`}>
|
||||
<Link href="/signin">
|
||||
<Button className="inline-flex items-center rounded-md bg-base border border-offbase px-2 py-1 text-xs font-medium text-foreground hover:bg-offbase focus:outline-none focus:ring-2 focus:ring-accent focus:ring-offset-2 transform transition-all duration-200 ease-in-out hover:scale-[1.01] hover:text-accent">
|
||||
<Button className="inline-flex items-center rounded-md bg-base border border-offbase px-2 py-1 text-xs font-medium text-foreground hover:bg-offbase focus:outline-none focus:ring-2 focus:ring-accent focus:ring-offset-2 transform transition duration-base ease-standard hover:text-accent">
|
||||
Connect
|
||||
</Button>
|
||||
</Link>
|
||||
{enableUserSignups && (
|
||||
<Link href="/signup">
|
||||
<Button className="inline-flex items-center rounded-md bg-accent px-2 py-1 text-xs font-medium text-background hover:bg-secondary-accent focus:outline-none focus:ring-2 focus:ring-accent focus:ring-offset-2 transform transition-all duration-200 ease-in-out hover:scale-[1.01]">
|
||||
<Button className="inline-flex items-center rounded-md bg-accent px-2 py-1 text-xs font-medium text-background hover:bg-secondary-accent focus:outline-none focus:ring-2 focus:ring-accent focus:ring-offset-2 transform transition duration-base ease-standard">
|
||||
Create account
|
||||
</Button>
|
||||
</Link>
|
||||
|
|
@ -97,7 +97,7 @@ export function UserMenu({
|
|||
|
||||
<Button
|
||||
onClick={handleDisconnectAccount}
|
||||
className="inline-flex items-center text-foreground text-xs hover:text-accent transform transition-all duration-200 ease-in-out hover:scale-[1.01]"
|
||||
className="inline-flex items-center text-foreground text-xs hover:text-accent transform transition duration-base ease-standard"
|
||||
title="Disconnect account"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
|
|
|
|||
|
|
@ -21,10 +21,10 @@ export function CreateFolderDialog({
|
|||
<Dialog as="div" className="relative z-50" onClose={onClose}>
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enter="ease-standard duration-slow"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leave="ease-standard duration-base"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
|
|
@ -35,14 +35,14 @@ export function CreateFolderDialog({
|
|||
<div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4">
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enter="ease-standard duration-slow"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leave="ease-standard duration-base"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<DialogPanel className="w-full max-w-md transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition-all">
|
||||
<DialogPanel className="w-full max-w-md transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition">
|
||||
<DialogTitle as="h3" className="text-lg font-semibold text-foreground">
|
||||
Create New Folder
|
||||
</DialogTitle>
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ function SidebarUploadLoader({
|
|||
strokeDasharray={`${circumference} ${circumference}`}
|
||||
strokeDashoffset={dashOffset}
|
||||
transform={`rotate(-90 ${size / 2} ${size / 2})`}
|
||||
style={{ transition: 'stroke-dashoffset 200ms ease-out' }}
|
||||
style={{ transition: 'stroke-dashoffset 200ms ease-standard' }}
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
|
@ -657,7 +657,7 @@ function DocumentListInner({ brand, appActions }: DocumentListInnerProps) {
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => setShowHint(false)}
|
||||
className="h-6 w-6 inline-flex items-center justify-center text-muted hover:text-accent hover:bg-base hover:scale-[1.01] rounded transition-all duration-200 ease-out"
|
||||
className="h-6 w-6 inline-flex items-center justify-center text-muted hover:text-accent hover:bg-base rounded transition duration-base ease-standard"
|
||||
aria-label="Dismiss hint"
|
||||
>
|
||||
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
|
|
|
|||
|
|
@ -261,7 +261,7 @@ export function DocumentPreview({ doc }: DocumentPreviewProps) {
|
|||
ref={imageRef}
|
||||
src={imagePreview}
|
||||
alt={`${doc.name} preview`}
|
||||
className={`absolute inset-0 h-full w-full object-cover transition-opacity duration-150 ${isImageReady ? 'opacity-100' : 'opacity-0'}`}
|
||||
className={`absolute inset-0 h-full w-full object-cover transition-opacity duration-fast ${isImageReady ? 'opacity-100' : 'opacity-0'}`}
|
||||
draggable={false}
|
||||
loading="lazy"
|
||||
onLoad={() => {
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ export function DocumentTile({
|
|||
data-doc-tile
|
||||
aria-selected={isSelected}
|
||||
className={
|
||||
'group relative flex flex-col rounded-md overflow-hidden border transition-all duration-200 ease-out hover:scale-[1.01] ' +
|
||||
'group relative flex flex-col rounded-md overflow-hidden border transition duration-base ease-standard ' +
|
||||
(isSelected
|
||||
? 'border-accent bg-offbase'
|
||||
: 'border-offbase bg-base hover:bg-offbase hover:border-accent') +
|
||||
|
|
@ -187,7 +187,7 @@ export function DocumentTile({
|
|||
{showDeleteButton && (
|
||||
<Button
|
||||
onClick={() => onDelete(doc)}
|
||||
className={`inline-flex items-center justify-center text-muted hover:text-accent hover:bg-base focus:outline-none transition-colors duration-200 ${TRASH_BTN_CLASSES[iconSize]}`}
|
||||
className={`inline-flex items-center justify-center text-muted hover:text-accent hover:bg-base focus:outline-none transition-colors duration-base ${TRASH_BTN_CLASSES[iconSize]}`}
|
||||
aria-label={`Delete ${doc.name}`}
|
||||
>
|
||||
<svg className={TRASH_ICON_CLASSES[iconSize]} fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ function GalleryThumb({
|
|||
onClick={onClick}
|
||||
aria-current={active ? 'true' : undefined}
|
||||
className={
|
||||
'group relative w-[98px] sm:w-[110px] shrink-0 cursor-pointer rounded-lg overflow-hidden border bg-base snap-start transition-all duration-200 ease-out ' +
|
||||
'group relative w-[98px] sm:w-[110px] shrink-0 cursor-pointer rounded-lg overflow-hidden border bg-base snap-start transition duration-base ease-standard ' +
|
||||
(active
|
||||
? 'border-accent shadow-[0_10px_24px_-18px_rgba(0,0,0,0.85)] -translate-y-0.5'
|
||||
: 'border-offbase hover:border-accent hover:-translate-y-0.5') +
|
||||
|
|
@ -106,20 +106,20 @@ function GalleryThumb({
|
|||
</div>
|
||||
<div
|
||||
className={
|
||||
'px-2 py-1.5 flex items-center gap-1.5 border-t transition-colors duration-200 ' +
|
||||
'px-2 py-1.5 flex items-center gap-1.5 border-t transition-colors duration-base ' +
|
||||
(active ? 'bg-offbase border-accent' : 'bg-base border-offbase')
|
||||
}
|
||||
>
|
||||
<KindIcon
|
||||
doc={doc}
|
||||
className={
|
||||
'w-3 h-3 shrink-0 transition-colors duration-200 ' +
|
||||
'w-3 h-3 shrink-0 transition-colors duration-base ' +
|
||||
(active ? 'text-accent' : 'text-muted')
|
||||
}
|
||||
/>
|
||||
<span
|
||||
className={
|
||||
'truncate text-[10px] leading-none transition-colors duration-200 ' +
|
||||
'truncate text-[10px] leading-none transition-colors duration-base ' +
|
||||
(active ? 'text-accent font-medium' : 'text-foreground')
|
||||
}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ function HeaderCell({
|
|||
onSortChange(field, nextDir);
|
||||
}}
|
||||
className={
|
||||
'flex items-center gap-1 px-2 py-1.5 text-[10px] uppercase tracking-wide font-semibold transition-colors duration-200 ease-out hover:text-accent ' +
|
||||
'flex items-center gap-1 px-2 py-1.5 text-[10px] uppercase tracking-wide font-semibold transition-colors duration-base ease-standard hover:text-accent ' +
|
||||
(active ? 'text-accent' : 'text-muted') +
|
||||
(align === 'right' ? ' justify-end' : '') +
|
||||
' ' +
|
||||
|
|
@ -131,7 +131,7 @@ function DocRow({
|
|||
data-doc-tile
|
||||
aria-selected={isSelected}
|
||||
className={
|
||||
'grid grid-cols-[minmax(0,1fr)_44px_72px_104px_28px] sm:grid-cols-[minmax(0,1fr)_56px_96px_140px_32px] items-center text-[12px] border-b border-offbase transition-colors duration-200 ease-out ' +
|
||||
'grid grid-cols-[minmax(0,1fr)_44px_72px_104px_28px] sm:grid-cols-[minmax(0,1fr)_56px_96px_140px_32px] items-center text-[12px] border-b border-offbase transition-colors duration-base ease-standard ' +
|
||||
(isSelected
|
||||
? 'bg-offbase text-accent'
|
||||
: 'text-foreground hover:bg-offbase') +
|
||||
|
|
@ -161,7 +161,7 @@ function DocRow({
|
|||
e.stopPropagation();
|
||||
onDeleteDoc(doc);
|
||||
}}
|
||||
className="h-7 w-7 flex items-center justify-center text-muted hover:text-accent hover:bg-offbase hover:scale-[1.02] rounded transition-all duration-200 ease-out"
|
||||
className="h-7 w-7 flex items-center justify-center text-muted hover:text-accent hover:bg-offbase rounded transition duration-base ease-standard"
|
||||
aria-label={`Delete ${doc.name}`}
|
||||
>
|
||||
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ function SidebarRow({
|
|||
type="button"
|
||||
onClick={onClick}
|
||||
className={
|
||||
'group w-full flex items-center gap-2 px-2 py-1 rounded-md text-[12px] border transform transition-all duration-200 ease-out text-left hover:scale-[1.01] ' +
|
||||
'group w-full flex items-center gap-2 px-2 py-1 rounded-md text-[12px] border transform transition duration-base ease-standard text-left ' +
|
||||
(active
|
||||
? 'border-accent bg-offbase text-accent'
|
||||
: 'border-transparent bg-transparent text-foreground hover:border-accent hover:text-accent') +
|
||||
|
|
@ -65,7 +65,7 @@ function SidebarRow({
|
|||
>
|
||||
<span
|
||||
className={
|
||||
'w-4 h-4 shrink-0 flex items-center justify-center transition-colors duration-200 ' +
|
||||
'w-4 h-4 shrink-0 flex items-center justify-center transition-colors duration-base ' +
|
||||
(active ? 'text-accent' : 'text-muted group-hover:text-accent')
|
||||
}
|
||||
>
|
||||
|
|
@ -74,7 +74,7 @@ function SidebarRow({
|
|||
<span className="truncate flex-1">{label}</span>
|
||||
{typeof count === 'number' && count > 0 && (
|
||||
<span
|
||||
className={`text-[10px] text-muted tabular-nums transition-transform duration-200 ease-out ${countClassName ?? ''}`}
|
||||
className={`text-[10px] text-muted tabular-nums transition-transform duration-base ease-standard ${countClassName ?? ''}`}
|
||||
>
|
||||
{count}
|
||||
</span>
|
||||
|
|
@ -272,7 +272,7 @@ export function FinderSidebar({
|
|||
rightSlot={(
|
||||
<Menu as="div" className="relative inline-flex items-center leading-none text-left shrink-0 normal-case tracking-normal font-normal">
|
||||
<MenuButton
|
||||
className="inline-flex items-center justify-center h-3.5 w-5 rounded-sm text-muted hover:text-accent transition-colors duration-200 ease-out focus:outline-none"
|
||||
className="inline-flex items-center justify-center h-3.5 w-5 rounded-sm text-muted hover:text-accent transition-colors duration-base ease-standard focus:outline-none"
|
||||
title="Folder actions"
|
||||
aria-label="Folder actions"
|
||||
>
|
||||
|
|
@ -280,10 +280,10 @@ export function FinderSidebar({
|
|||
</MenuButton>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
enter="transition ease-standard duration-fast"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-75"
|
||||
leave="transition ease-standard duration-fast"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
|
|
@ -370,7 +370,7 @@ export function FinderSidebar({
|
|||
onPointerMove={onResizeMove}
|
||||
onPointerUp={onResizeEnd}
|
||||
onPointerCancel={onResizeEnd}
|
||||
className="hidden md:block absolute top-0 right-0 h-full w-1 cursor-col-resize hover:bg-offbase active:bg-accent transition-colors duration-200 ease-out"
|
||||
className="hidden md:block absolute top-0 right-0 h-full w-1 cursor-col-resize hover:bg-offbase active:bg-accent transition-colors duration-base ease-standard"
|
||||
/>
|
||||
</aside>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ const ICON_SIZES: Array<{ value: IconSize; label: string }> = [
|
|||
|
||||
// Match SettingsModal / UserMenu trigger sizing exactly so all bar buttons share one rhythm.
|
||||
const TOOLBAR_BTN =
|
||||
'inline-flex items-center py-1 px-2 rounded-md border bg-base text-xs transition-all duration-200 ease-out hover:scale-[1.01]';
|
||||
'inline-flex items-center py-1 px-2 rounded-md border bg-base text-xs transition duration-base ease-standard';
|
||||
const TOOLBAR_BTN_INACTIVE =
|
||||
'border-offbase text-foreground hover:text-accent hover:border-accent hover:bg-offbase';
|
||||
const TOOLBAR_BTN_ACTIVE = 'border-accent bg-offbase text-accent';
|
||||
|
|
@ -64,7 +64,7 @@ const TOOLBAR_BTN_ACTIVE = 'border-accent bg-offbase text-accent';
|
|||
// matches the height of a standalone TOOLBAR_BTN.
|
||||
const PILL = 'inline-flex items-center rounded-md border border-offbase bg-base p-0.5 gap-0.5 shrink-0';
|
||||
const PILL_SEGMENT =
|
||||
'inline-flex items-center justify-center rounded-[5px] text-xs transition-colors duration-200 ease-out';
|
||||
'inline-flex items-center justify-center rounded-[5px] text-xs transition-colors duration-base ease-standard';
|
||||
const PILL_SEGMENT_INACTIVE = 'text-muted hover:bg-offbase hover:text-accent';
|
||||
const PILL_SEGMENT_ACTIVE = 'bg-offbase text-accent';
|
||||
|
||||
|
|
@ -133,7 +133,7 @@ export function FinderToolbar({
|
|||
</button>
|
||||
{isIconsToggle && viewMode === 'icons' && (
|
||||
<div
|
||||
className="absolute top-full left-1/2 z-30 -translate-x-1/2 pt-1 opacity-0 pointer-events-none transition-opacity duration-150 group-hover/icons:opacity-100 group-hover/icons:pointer-events-auto group-focus-within/icons:opacity-100 group-focus-within/icons:pointer-events-auto"
|
||||
className="absolute top-full left-1/2 z-30 -translate-x-1/2 pt-1 opacity-0 pointer-events-none transition-opacity duration-fast group-hover/icons:opacity-100 group-hover/icons:pointer-events-auto group-focus-within/icons:opacity-100 group-focus-within/icons:pointer-events-auto"
|
||||
>
|
||||
<div className={`${PILL} shadow-lg`}>
|
||||
{ICON_SIZES.map(({ value: sizeValue, label: sizeLabel }) => {
|
||||
|
|
@ -204,7 +204,7 @@ export function FinderToolbar({
|
|||
|
||||
<div className="flex-1 min-w-0" />
|
||||
|
||||
<div className="hidden sm:flex items-center gap-1.5 px-2 py-1 rounded-md bg-background border border-offbase hover:border-accent focus-within:ring-1 focus-within:ring-accent focus-within:border-accent transition-colors duration-200 ease-out w-[160px] md:w-[200px]">
|
||||
<div className="hidden sm:flex items-center gap-1.5 px-2 py-1 rounded-md bg-background border border-offbase hover:border-accent focus-within:ring-1 focus-within:ring-accent focus-within:border-accent transition-colors duration-base ease-standard w-[160px] md:w-[200px]">
|
||||
<SearchIcon className="w-3.5 h-3.5 text-muted shrink-0" />
|
||||
<input
|
||||
type="search"
|
||||
|
|
|
|||
|
|
@ -69,10 +69,10 @@ export function FinderWindow({
|
|||
>
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="transition-opacity duration-150"
|
||||
enter="transition-opacity duration-fast"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="transition-opacity duration-100"
|
||||
leave="transition-opacity duration-fast"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
|
|
@ -81,10 +81,10 @@ export function FinderWindow({
|
|||
<div className="fixed inset-y-0 left-0 flex max-w-full">
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="transition-transform duration-200 ease-out"
|
||||
enter="transition-transform duration-base ease-standard"
|
||||
enterFrom="-translate-x-full"
|
||||
enterTo="translate-x-0"
|
||||
leave="transition-transform duration-150 ease-in"
|
||||
leave="transition-transform duration-fast ease-standard"
|
||||
leaveFrom="translate-x-0"
|
||||
leaveTo="-translate-x-full"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -140,10 +140,10 @@ export function DexieMigrationModal({
|
|||
<Dialog as="div" className="relative z-[80]" onClose={() => (closeDisabled ? null : onComplete())}>
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enter="ease-standard duration-slow"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leave="ease-standard duration-base"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
|
|
@ -154,14 +154,14 @@ export function DexieMigrationModal({
|
|||
<div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4">
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enter="ease-standard duration-slow"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leave="ease-standard duration-base"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<DialogPanel data-testid="migration-modal" className="w-full max-w-md transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition-all">
|
||||
<DialogPanel data-testid="migration-modal" className="w-full max-w-md transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition">
|
||||
<DialogTitle as="h3" className="text-lg font-semibold leading-6 text-foreground mb-4">
|
||||
{title}
|
||||
</DialogTitle>
|
||||
|
|
@ -192,7 +192,7 @@ export function DexieMigrationModal({
|
|||
className="inline-flex justify-center rounded-lg bg-background px-3 py-1.5 text-sm
|
||||
font-medium text-foreground hover:bg-offbase focus:outline-none
|
||||
focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2
|
||||
transform transition-transform duration-200 ease-in-out hover:scale-[1.04] hover:text-accent
|
||||
transform transition-transform duration-base ease-standard hover:text-accent
|
||||
disabled:opacity-50"
|
||||
>
|
||||
Skip
|
||||
|
|
@ -203,7 +203,7 @@ export function DexieMigrationModal({
|
|||
className="inline-flex justify-center rounded-lg bg-accent px-3 py-1.5 text-sm
|
||||
font-medium text-background hover:bg-secondary-accent focus:outline-none
|
||||
focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2
|
||||
transform transition-transform duration-200 ease-in-out hover:scale-[1.04] hover:text-background
|
||||
transform transition-transform duration-base ease-standard hover:text-background
|
||||
disabled:opacity-50"
|
||||
>
|
||||
{isUploading ? 'Uploading…' : 'Upload'}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ export function DocumentHeaderMenu({
|
|||
{onOpenSegments && (
|
||||
<button
|
||||
onClick={onOpenSegments}
|
||||
className={`inline-flex items-center py-1 px-2 rounded-md border bg-base text-xs transition-all duration-200 ease-in-out hover:scale-[1.09] ${
|
||||
className={`inline-flex items-center py-1 px-2 rounded-md border bg-base text-xs transition duration-base ease-standard ${
|
||||
isSegmentsOpen
|
||||
? 'border-accent text-accent bg-offbase'
|
||||
: 'border-offbase text-foreground hover:bg-offbase hover:text-accent'
|
||||
|
|
@ -57,13 +57,13 @@ export function DocumentHeaderMenu({
|
|||
aria-label={isSegmentsOpen ? 'Hide segments sidebar' : 'Open segments sidebar'}
|
||||
title={isSegmentsOpen ? 'Hide Segments' : 'Segments'}
|
||||
>
|
||||
<ListIcon className="w-4 h-4 transform transition-transform duration-200 ease-in-out hover:scale-[1.09]" />
|
||||
<ListIcon className="w-4 h-4 transform transition-transform duration-base ease-standard" />
|
||||
</button>
|
||||
)}
|
||||
{showAudiobookExport && onOpenAudiobook && (
|
||||
<button
|
||||
onClick={onOpenAudiobook}
|
||||
className={`inline-flex items-center py-1 px-2 rounded-md border bg-base text-xs transition-all duration-200 ease-in-out hover:scale-[1.09] ${
|
||||
className={`inline-flex items-center py-1 px-2 rounded-md border bg-base text-xs transition duration-base ease-standard ${
|
||||
isAudiobookOpen
|
||||
? 'border-accent text-accent bg-offbase'
|
||||
: 'border-offbase text-foreground hover:bg-offbase hover:text-accent'
|
||||
|
|
@ -71,12 +71,12 @@ export function DocumentHeaderMenu({
|
|||
aria-label={isAudiobookOpen ? 'Hide audiobook export' : 'Open audiobook export'}
|
||||
title={isAudiobookOpen ? 'Hide Export Audiobook' : 'Export Audiobook'}
|
||||
>
|
||||
<DownloadIcon className="w-4 h-4 transform transition-transform duration-200 ease-in-out hover:scale-[1.09]" />
|
||||
<DownloadIcon className="w-4 h-4 transform transition-transform duration-base ease-standard" />
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={onOpenSettings}
|
||||
className={`inline-flex items-center py-1 px-2 rounded-md border bg-base text-xs transition-all duration-200 ease-in-out hover:scale-[1.09] ${
|
||||
className={`inline-flex items-center py-1 px-2 rounded-md border bg-base text-xs transition duration-base ease-standard ${
|
||||
isSettingsOpen
|
||||
? 'border-accent text-accent bg-offbase'
|
||||
: 'border-offbase text-foreground hover:bg-offbase hover:text-accent'
|
||||
|
|
@ -84,7 +84,7 @@ export function DocumentHeaderMenu({
|
|||
aria-label={isSettingsOpen ? 'Hide settings' : 'Open settings'}
|
||||
title={isSettingsOpen ? 'Hide Settings' : 'Settings'}
|
||||
>
|
||||
<FileSettingsIcon className="w-4 h-4 transform transition-transform duration-200 ease-in-out hover:scale-[1.09]" />
|
||||
<FileSettingsIcon className="w-4 h-4 transform transition-transform duration-base ease-standard" />
|
||||
</button>
|
||||
<UserMenu />
|
||||
</div>
|
||||
|
|
@ -95,17 +95,17 @@ export function DocumentHeaderMenu({
|
|||
<div className="sm:hidden flex items-center">
|
||||
<Menu as="div" className="relative inline-block text-left">
|
||||
<MenuButton
|
||||
className="inline-flex items-center justify-center py-1 px-2 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase transition-all duration-200 ease-in-out hover:scale-[1.09] hover:text-accent focus:outline-none focus-visible:ring-2 focus-visible:ring-accent"
|
||||
className="inline-flex items-center justify-center py-1 px-2 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase transition duration-base ease-standard hover:text-accent focus:outline-none focus-visible:ring-2 focus-visible:ring-accent"
|
||||
title="Menu"
|
||||
>
|
||||
<DotsVerticalIcon className="w-4 h-4 transform transition-transform duration-200 ease-in-out hover:scale-[1.09] hover:text-accent" />
|
||||
<DotsVerticalIcon className="w-4 h-4 transform transition-transform duration-base ease-standard hover:text-accent" />
|
||||
</MenuButton>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
enter="transition ease-standard duration-fast"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-75"
|
||||
leave="transition ease-standard duration-fast"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -118,10 +118,10 @@ export function DocumentSelectionModal({
|
|||
<Dialog as="div" className="relative z-[60]" onClose={onClose}>
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enter="ease-standard duration-slow"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leave="ease-standard duration-base"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
|
|
@ -132,14 +132,14 @@ export function DocumentSelectionModal({
|
|||
<div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4">
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enter="ease-standard duration-slow"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leave="ease-standard duration-base"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<DialogPanel className="w-full max-w-2xl transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition-all flex flex-col h-[80vh]">
|
||||
<DialogPanel className="w-full max-w-2xl transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition flex flex-col h-[80vh]">
|
||||
<DialogTitle
|
||||
as="h3"
|
||||
className="text-lg font-semibold leading-6 text-foreground mb-4 flex-shrink-0 flex justify-between items-center"
|
||||
|
|
|
|||
|
|
@ -155,9 +155,7 @@ export function DocumentUploader({
|
|||
noKeyboard: variant === 'overlay'
|
||||
});
|
||||
|
||||
const containerBase = `group w-full rounded transform transition-all duration-200 ease-in-out ${
|
||||
variant === 'compact' ? 'hover:scale-[1.01]' : ''
|
||||
} ${
|
||||
const containerBase = `group w-full rounded transition duration-base ease-standard ${
|
||||
isUploading || isConverting ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'
|
||||
} ${className}`;
|
||||
|
||||
|
|
@ -171,7 +169,7 @@ export function DocumentUploader({
|
|||
: `${
|
||||
isDragActive
|
||||
? 'border-2 border-dashed border-accent bg-base text-foreground'
|
||||
: 'border-2 border-dashed border-muted bg-transparent text-foreground hover:border-accent hover:bg-base hover:scale-[1.01]'
|
||||
: 'border-2 border-dashed border-muted bg-transparent text-foreground hover:border-accent hover:bg-base'
|
||||
}`;
|
||||
|
||||
const paddingClass = variant === 'compact' ? 'py-1 px-2 rounded-md' : 'py-5 px-3 rounded-lg';
|
||||
|
|
@ -219,7 +217,7 @@ export function DocumentUploader({
|
|||
<input {...getInputProps()} />
|
||||
{variant === 'compact' ? (
|
||||
<div className="flex items-center gap-2 text-left w-full min-w-0">
|
||||
<UploadIcon className="w-3.5 h-3.5 text-muted group-hover:text-accent shrink-0 transition-colors duration-200" />
|
||||
<UploadIcon className="w-3.5 h-3.5 text-muted group-hover:text-accent shrink-0 transition-colors duration-base" />
|
||||
{isUploading ? (
|
||||
<p className="text-[12px] font-medium truncate flex-1">Uploading…</p>
|
||||
) : isConverting ? (
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export function ZoomControl({
|
|||
className={buttonClass({
|
||||
variant: 'ghost',
|
||||
size: 'icon',
|
||||
className: 'h-6 w-6 text-sm leading-none hover:scale-[1.09]',
|
||||
className: 'h-6 w-6 text-sm leading-none',
|
||||
})}
|
||||
aria-label="Zoom out"
|
||||
>
|
||||
|
|
@ -36,7 +36,7 @@ export function ZoomControl({
|
|||
className={buttonClass({
|
||||
variant: 'ghost',
|
||||
size: 'icon',
|
||||
className: 'h-6 w-6 text-sm leading-none hover:scale-[1.09]',
|
||||
className: 'h-6 w-6 text-sm leading-none',
|
||||
})}
|
||||
aria-label="Zoom in"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ export const inputClass =
|
|||
'w-full rounded-md bg-background border border-offbase px-2.5 py-1.5 text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-accent';
|
||||
|
||||
export const listboxButtonClass =
|
||||
'relative w-full cursor-pointer rounded-md bg-background border border-offbase py-1.5 pl-2.5 pr-9 text-left text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-accent hover:bg-base transition-transform duration-200 ease-out hover:scale-[1.01]';
|
||||
'relative w-full cursor-pointer rounded-md bg-background border border-offbase py-1.5 pl-2.5 pr-9 text-left text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-accent hover:bg-base transition-colors duration-fast ease-standard';
|
||||
export const listboxOptionsClass =
|
||||
'z-50 w-[var(--button-width)] max-h-60 overflow-y-auto overscroll-contain rounded-md bg-background p-1 shadow-lg ring-1 ring-offbase focus:outline-none [--anchor-gap:0.25rem]';
|
||||
export const listboxOptionClass = (active: boolean) =>
|
||||
|
|
@ -48,10 +48,10 @@ export const listboxOptionClass = (active: boolean) =>
|
|||
export const segmentedGroupClass =
|
||||
'grid gap-1 rounded-full border border-offbase bg-background p-1';
|
||||
export const segmentedButtonClass = (active: boolean) =>
|
||||
`rounded-full px-2.5 py-1.5 text-xs font-medium transition-colors transition-transform duration-200 ease-out focus:outline-none focus-visible:ring-2 focus-visible:ring-accent ${
|
||||
`rounded-full px-2.5 py-1.5 text-xs font-medium transition-colors duration-fast ease-standard focus:outline-none focus-visible:ring-2 focus-visible:ring-accent ${
|
||||
active
|
||||
? 'bg-accent text-background hover:scale-[1.01]'
|
||||
: 'text-muted hover:bg-base hover:text-foreground hover:scale-[1.02]'
|
||||
? 'bg-accent text-background'
|
||||
: 'text-muted hover:bg-base hover:text-foreground'
|
||||
}`;
|
||||
|
||||
export function Section({
|
||||
|
|
@ -108,7 +108,7 @@ export function Card({
|
|||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<div className={`rounded-lg border border-offbase bg-background px-3 py-2 transition-transform duration-200 ease-out hover:scale-[1.005] ${className}`}>
|
||||
<div className={`rounded-lg border border-offbase bg-background px-3 py-2 transition-colors duration-fast ease-standard ${className}`}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
|
@ -159,13 +159,13 @@ export function Switch({
|
|||
aria-describedby={ariaDescribedBy}
|
||||
disabled={disabled}
|
||||
onClick={() => onChange(!checked)}
|
||||
className={`relative inline-flex shrink-0 cursor-pointer items-center rounded-full border border-offbase transition-colors duration-200 ease-out focus:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed ${s.track} ${
|
||||
className={`relative inline-flex shrink-0 cursor-pointer items-center rounded-full border border-offbase transition-colors duration-fast ease-standard focus:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed ${s.track} ${
|
||||
checked ? 'bg-accent' : 'bg-offbase'
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className={`pointer-events-none inline-block rounded-full bg-background shadow-sm ring-0 transition-transform duration-200 ease-out ${s.thumb} ${
|
||||
className={`pointer-events-none inline-block rounded-full bg-background shadow-sm ring-0 transition-transform duration-fast ease-standard ${s.thumb} ${
|
||||
checked ? s.on : s.off
|
||||
}`}
|
||||
/>
|
||||
|
|
@ -194,8 +194,8 @@ export function ToggleRow({
|
|||
const descId = useId();
|
||||
const rowClass =
|
||||
variant === 'flat'
|
||||
? 'px-0.5 pt-1 pb-2 border-b border-offbase last:border-b-0 transition-transform duration-200 ease-out hover:scale-[1.003]'
|
||||
: 'rounded-md border border-offbase bg-background px-2.5 py-1.5 transition-transform duration-200 ease-out hover:scale-[1.005]';
|
||||
? 'px-0.5 pt-1 pb-2 border-b border-offbase last:border-b-0 transition-colors duration-fast ease-standard'
|
||||
: 'rounded-md border border-offbase bg-background px-2.5 py-1.5 transition-colors duration-fast ease-standard';
|
||||
const handleTextToggle = () => {
|
||||
if (!disabled) onChange(!checked);
|
||||
};
|
||||
|
|
@ -243,7 +243,7 @@ export function CheckItem({
|
|||
<span
|
||||
id={labelId}
|
||||
onClick={handleTextToggle}
|
||||
className={`flex-1 min-w-0 truncate text-xs leading-4 text-foreground select-none transition-colors duration-200 ease-out group-hover:text-accent ${
|
||||
className={`flex-1 min-w-0 truncate text-xs leading-4 text-foreground select-none transition-colors duration-fast ease-standard group-hover:text-accent ${
|
||||
disabled ? '' : 'cursor-pointer'
|
||||
}`}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ export const Navigator = ({ currentPage, numPages, skipToLocation }: {
|
|||
<Button
|
||||
onClick={() => skipToLocation(currentPage - 1, true)}
|
||||
disabled={currentPage <= 1}
|
||||
className="relative p-2 rounded-full text-foreground hover:bg-offbase data-[hover]:bg-offbase data-[active]:bg-offbase transition-all duration-200 focus:outline-none disabled:opacity-50 transform ease-in-out hover:scale-[1.09] hover:text-accent"
|
||||
className="relative p-2 rounded-full text-foreground hover:bg-offbase data-[hover]:bg-offbase data-[active]:bg-offbase transition duration-base focus:outline-none disabled:opacity-50 transform ease-standard hover:text-accent"
|
||||
aria-label="Previous page"
|
||||
>
|
||||
<svg className="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||||
|
|
@ -67,7 +67,7 @@ export const Navigator = ({ currentPage, numPages, skipToLocation }: {
|
|||
{/* Page number popup */}
|
||||
<Popover className="relative mb-1">
|
||||
<PopoverButton
|
||||
className="bg-offbase px-2 py-0.5 rounded-full focus:outline-none cursor-pointer hover:bg-offbase transform transition-transform duration-200 ease-in-out hover:scale-[1.04] hover:text-accent"
|
||||
className="bg-offbase px-2 py-0.5 rounded-full focus:outline-none cursor-pointer hover:bg-offbase transform transition-transform duration-base ease-standard hover:text-accent"
|
||||
onClick={handlePopoverOpen}
|
||||
>
|
||||
<p className="text-xs whitespace-nowrap">
|
||||
|
|
@ -99,7 +99,7 @@ export const Navigator = ({ currentPage, numPages, skipToLocation }: {
|
|||
<Button
|
||||
onClick={() => skipToLocation(currentPage + 1, true)}
|
||||
disabled={currentPage >= (numPages || 1)}
|
||||
className="relative p-2 rounded-full text-foreground hover:bg-offbase data-[hover]:bg-offbase data-[active]:bg-offbase transition-all duration-200 focus:outline-none disabled:opacity-50 transform ease-in-out hover:scale-[1.09] hover:text-accent"
|
||||
className="relative p-2 rounded-full text-foreground hover:bg-offbase data-[hover]:bg-offbase data-[active]:bg-offbase transition duration-base focus:outline-none disabled:opacity-50 transform ease-standard hover:text-accent"
|
||||
aria-label="Next page"
|
||||
>
|
||||
<svg className="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ export function RateLimitPauseButton() {
|
|||
onClick={() => {
|
||||
if (isPlaying) togglePlay();
|
||||
}}
|
||||
className="relative p-1.5 rounded-md text-foreground hover:bg-offbase transition-all duration-200 focus:outline-none h-8 w-8 flex items-center justify-center transform ease-in-out hover:scale-[1.09] hover:text-accent"
|
||||
className="relative p-1.5 rounded-md text-foreground hover:bg-offbase transition duration-base focus:outline-none h-8 w-8 flex items-center justify-center transform ease-standard hover:text-accent"
|
||||
aria-label="Pause"
|
||||
>
|
||||
<PauseIcon className="w-5 h-5" />
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ export const SpeedControl = ({
|
|||
|
||||
return (
|
||||
<Popover className="relative">
|
||||
<PopoverButton className="flex items-center space-x-0.5 sm:space-x-1 bg-transparent text-foreground text-xs sm:text-sm focus:outline-none cursor-pointer hover:bg-offbase rounded pl-1.5 sm:pl-2 pr-0.5 sm:pr-1 py-0.5 sm:py-1 transform transition-transform duration-200 ease-in-out hover:scale-[1.04] hover:text-accent">
|
||||
<PopoverButton className="flex items-center space-x-0.5 sm:space-x-1 bg-transparent text-foreground text-xs sm:text-sm focus:outline-none cursor-pointer hover:bg-offbase rounded pl-1.5 sm:pl-2 pr-0.5 sm:pr-1 py-0.5 sm:py-1 transform transition-transform duration-base ease-standard hover:text-accent">
|
||||
<SpeedometerIcon className="h-3 w-3 sm:h-3.5 sm:w-3.5" />
|
||||
<span className="sm:hidden">{compactTriggerLabel}</span>
|
||||
<span className="hidden sm:inline">{triggerLabel}</span>
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ export default function TTSPlayer({ currentPage, numPages }: {
|
|||
{/* Playback Controls */}
|
||||
<Button
|
||||
onClick={skipBackward}
|
||||
className="relative p-1.5 rounded-md text-foreground hover:bg-offbase transition-all duration-200 focus:outline-none disabled:opacity-50 h-8 w-8 flex items-center justify-center transform ease-in-out hover:scale-[1.09] hover:text-accent"
|
||||
className="relative p-1.5 rounded-md text-foreground hover:bg-offbase transition duration-base focus:outline-none disabled:opacity-50 h-8 w-8 flex items-center justify-center transform ease-standard hover:text-accent"
|
||||
aria-label="Skip backward"
|
||||
disabled={isProcessing}
|
||||
>
|
||||
|
|
@ -60,7 +60,7 @@ export default function TTSPlayer({ currentPage, numPages }: {
|
|||
|
||||
<Button
|
||||
onClick={togglePlay}
|
||||
className="relative p-1.5 rounded-md text-foreground hover:bg-offbase transition-all duration-200 focus:outline-none h-8 w-8 flex items-center justify-center transform ease-in-out hover:scale-[1.09] hover:text-accent"
|
||||
className="relative p-1.5 rounded-md text-foreground hover:bg-offbase transition duration-base focus:outline-none h-8 w-8 flex items-center justify-center transform ease-standard hover:text-accent"
|
||||
aria-label={isPlaying ? 'Pause' : 'Play'}
|
||||
disabled={isProcessing && !isPlaying}
|
||||
>
|
||||
|
|
@ -69,7 +69,7 @@ export default function TTSPlayer({ currentPage, numPages }: {
|
|||
|
||||
<Button
|
||||
onClick={skipForward}
|
||||
className="relative p-1.5 rounded-md text-foreground hover:bg-offbase transition-all duration-200 focus:outline-none disabled:opacity-50 h-8 w-8 flex items-center justify-center transform ease-in-out hover:scale-[1.09] hover:text-accent"
|
||||
className="relative p-1.5 rounded-md text-foreground hover:bg-offbase transition duration-base focus:outline-none disabled:opacity-50 h-8 w-8 flex items-center justify-center transform ease-standard hover:text-accent"
|
||||
aria-label="Skip forward"
|
||||
disabled={isProcessing}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -34,8 +34,8 @@ export function VoicesControlBase({
|
|||
: 'bottom-full right-0 mb-1';
|
||||
|
||||
const buttonClass = variant === 'field'
|
||||
? 'relative cursor-pointer rounded-lg bg-base py-1.5 pl-3 pr-10 text-left text-foreground focus:outline-none focus:ring-2 focus:ring-accent transform transition-transform duration-200 ease-in-out hover:scale-[1.01] hover:text-accent w-full'
|
||||
: 'flex items-center space-x-0.5 sm:space-x-1 bg-transparent text-foreground text-xs sm:text-sm focus:outline-none cursor-pointer hover:bg-offbase rounded pl-1.5 sm:pl-2 pr-0.5 sm:pr-1 py-0.5 sm:py-1 transform transition-transform duration-200 ease-in-out hover:scale-[1.04] hover:text-accent';
|
||||
? 'relative cursor-pointer rounded-lg bg-base py-1.5 pl-3 pr-10 text-left text-foreground focus:outline-none focus:ring-2 focus:ring-accent transform transition-transform duration-base ease-standard hover:text-accent w-full'
|
||||
: 'flex items-center space-x-0.5 sm:space-x-1 bg-transparent text-foreground text-xs sm:text-sm focus:outline-none cursor-pointer hover:bg-offbase rounded pl-1.5 sm:pl-2 pr-0.5 sm:pr-1 py-0.5 sm:py-1 transform transition-transform duration-base ease-standard hover:text-accent';
|
||||
|
||||
const iconClass = variant === 'field'
|
||||
? 'h-3.5 w-3.5 shrink-0'
|
||||
|
|
|
|||
|
|
@ -41,10 +41,10 @@ export function ReaderSidebarShell({
|
|||
>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="transition-opacity ease-out duration-200"
|
||||
enter="transition-opacity ease-standard duration-base"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="transition-opacity ease-in duration-150"
|
||||
leave="transition-opacity ease-standard duration-fast"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
|
|
@ -58,10 +58,10 @@ export function ReaderSidebarShell({
|
|||
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-220"
|
||||
enter="transition ease-standard duration-base"
|
||||
enterFrom="translate-x-full"
|
||||
enterTo="translate-x-0"
|
||||
leave="transition ease-in duration-180"
|
||||
leave="transition ease-standard duration-base"
|
||||
leaveFrom="translate-x-0"
|
||||
leaveTo="translate-x-full"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -901,10 +901,10 @@ function SegmentMetadataPopover({ row }: { row: TTSSegmentRow }) {
|
|||
</PopoverButton>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-150"
|
||||
enter="transition ease-standard duration-fast"
|
||||
enterFrom="opacity-0 translate-y-1"
|
||||
enterTo="opacity-100 translate-y-0"
|
||||
leave="transition ease-in duration-100"
|
||||
leave="transition ease-standard duration-fast"
|
||||
leaveFrom="opacity-100 translate-y-0"
|
||||
leaveTo="opacity-0 translate-y-1"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
export const btnBase =
|
||||
'inline-flex items-center justify-center rounded-md text-sm font-medium focus:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 transition-colors transition-transform duration-200 ease-out disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100';
|
||||
export const btnPrimary = `${btnBase} bg-accent text-background hover:bg-secondary-accent hover:scale-[1.03]`;
|
||||
export const btnSecondary = `${btnBase} bg-base text-foreground border border-offbase hover:bg-offbase hover:scale-[1.03]`;
|
||||
export const btnOutline = `${btnBase} bg-background border border-offbase text-foreground hover:bg-base hover:text-accent hover:scale-[1.02]`;
|
||||
export const btnDanger = `${btnBase} bg-red-600 text-white border border-red-700 hover:bg-red-700 hover:scale-[1.02]`;
|
||||
'inline-flex items-center justify-center rounded-md text-sm font-medium focus:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 transition-colors duration-fast ease-standard disabled:opacity-50 disabled:cursor-not-allowed';
|
||||
export const btnPrimary = `${btnBase} bg-accent text-background hover:bg-secondary-accent`;
|
||||
export const btnSecondary = `${btnBase} bg-base text-foreground border border-offbase hover:bg-offbase`;
|
||||
export const btnOutline = `${btnBase} bg-background border border-offbase text-foreground hover:bg-base hover:text-accent`;
|
||||
export const btnDanger = `${btnBase} bg-danger text-background border border-danger hover:bg-danger`;
|
||||
export const btnGhost = `${btnBase} bg-transparent text-foreground hover:bg-base hover:text-accent`;
|
||||
|
||||
export type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'danger' | 'ghost';
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ export function EPUBViewer({ className = '', epubState }: EPUBViewerProps) {
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => setIsTocOpen(open => !open)}
|
||||
className="inline-flex items-center py-1 px-1 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase transition-all duration-200 ease-in-out transform hover:scale-[1.09] hover:text-accent"
|
||||
className="inline-flex items-center py-1 px-1 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase transition duration-base ease-standard transform hover:text-accent"
|
||||
aria-label={isTocOpen ? 'Hide chapters' : 'Show chapters'}
|
||||
>
|
||||
<DotsVerticalIcon className="w-4 h-4" />
|
||||
|
|
@ -171,7 +171,7 @@ export function EPUBViewer({ className = '', epubState }: EPUBViewerProps) {
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => handleLocationChanged('prev')}
|
||||
className="inline-flex items-center py-1 px-2 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase transition-all duration-200 ease-in-out transform hover:scale-[1.09] hover:text-accent"
|
||||
className="inline-flex items-center py-1 px-2 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase transition duration-base ease-standard transform hover:text-accent"
|
||||
aria-label="Previous section"
|
||||
>
|
||||
<ChevronLeftIcon className="w-4 h-4" />
|
||||
|
|
@ -185,7 +185,7 @@ export function EPUBViewer({ className = '', epubState }: EPUBViewerProps) {
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => handleLocationChanged('next')}
|
||||
className="inline-flex items-center py-1 px-2 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase transition-all duration-200 ease-in-out transform hover:scale-[1.09] hover:text-accent"
|
||||
className="inline-flex items-center py-1 px-2 rounded-md border border-offbase bg-base text-foreground text-xs hover:bg-offbase transition duration-base ease-standard transform hover:text-accent"
|
||||
aria-label="Next section"
|
||||
>
|
||||
<ChevronRightIcon className="w-4 h-4" />
|
||||
|
|
@ -205,7 +205,7 @@ export function EPUBViewer({ className = '', epubState }: EPUBViewerProps) {
|
|||
}}
|
||||
className="
|
||||
px-2 py-1 rounded-md font-medium text-foreground text-center bg-base
|
||||
hover:bg-offbase hover:text-accent transition-colors duration-150
|
||||
hover:bg-offbase hover:text-accent transition-colors duration-fast
|
||||
whitespace-nowrap
|
||||
flex-1 min-w-[140px]
|
||||
"
|
||||
|
|
|
|||
Loading…
Reference in a new issue