diff --git a/src/elements/components/carousel/comparison.tsx b/src/elements/components/carousel/comparison.tsx index 523f3626..91318210 100644 --- a/src/elements/components/carousel/comparison.tsx +++ b/src/elements/components/carousel/comparison.tsx @@ -19,6 +19,7 @@ export const ImageComparison = ({ image }: ImageComparisonProps) => { scale: 1, }); const prevTransformStateRef = useRef(transformState); + const imagesLoadedRef = useRef({ lr: false, sr: false }); useEffect(() => { const prevTransformState = prevTransformStateRef.current; @@ -38,11 +39,66 @@ export const ImageComparison = ({ image }: ImageComparisonProps) => { }, [transformState]); useEffect(() => { - lrRef.current?.centerView(1, 0); - srRef.current?.centerView(1, 0); + // Reset transform state when image changes + setTransformState({ + positionX: 0, + positionY: 0, + scale: 1, + }); + // Reset image load tracking + imagesLoadedRef.current = { lr: false, sr: false }; setHandlePosition(50); }, [image]); + // Track image loading and center images once loaded + useEffect(() => { + // Center images function + const centerImages = () => { + if (lrRef.current && srRef.current) { + // Use requestAnimationFrame to ensure DOM is updated + requestAnimationFrame(() => { + lrRef.current?.centerView(1, 0); + srRef.current?.centerView(1, 0); + }); + } + }; + + const lrImg = new Image(); + const srImg = new Image(); + let loadedCount = 0; + + const checkAndCenter = () => { + loadedCount++; + if (loadedCount === 2) { + // Both images loaded, center them + setTimeout(() => { + centerImages(); + }, 50); // Small delay to ensure DOM is ready + } + }; + + lrImg.onload = () => { + imagesLoadedRef.current.lr = true; + checkAndCenter(); + }; + srImg.onload = () => { + imagesLoadedRef.current.sr = true; + checkAndCenter(); + }; + + lrImg.src = image.LR; + srImg.src = image.SR; + + // Fallback: center after a timeout even if images don't load + const fallbackTimer = setTimeout(() => { + centerImages(); + }, 500); + + return () => { + clearTimeout(fallbackTimer); + }; + }, [image]); + return ( { } + key={`${image.LR}-${image.SR}`} position={handlePosition} onPositionChange={setHandlePosition} /> diff --git a/src/elements/components/image-carousel.module.scss b/src/elements/components/image-carousel.module.scss index 5289ff45..a1dbfdbf 100644 --- a/src/elements/components/image-carousel.module.scss +++ b/src/elements/components/image-carousel.module.scss @@ -1,7 +1,18 @@ .imageWrapper { + height: 350px; + + @media screen and (min-width: 768px) { + height: 450px; + } + + @media screen and (min-width: 1024px) { + height: 500px; + } + @media screen and (max-width: 1023px) { border-radius: 0; margin: -1rem 0 0 -1rem; width: calc(100% + 2rem); + height: 300px; } } diff --git a/src/elements/components/image-carousel.tsx b/src/elements/components/image-carousel.tsx index 91f683fb..bee8a0c6 100644 --- a/src/elements/components/image-carousel.tsx +++ b/src/elements/components/image-carousel.tsx @@ -47,7 +47,7 @@ export const ImageCarousel = ({ images, readonly, indexKey, onChange }: ImageCar return (
{selectedImage ? ( diff --git a/src/elements/components/model-card.module.scss b/src/elements/components/model-card.module.scss index c35e635d..c19189a7 100644 --- a/src/elements/components/model-card.module.scss +++ b/src/elements/components/model-card.module.scss @@ -9,15 +9,28 @@ border-radius: 0.5rem; border-width: 1px; border-style: solid; + transition: transform 0.3s ease, box-shadow 0.3s ease; + z-index: 1; --inner-radius: calc(0.5rem - 1px); + &:hover { + transform: scale(1.02); + z-index: 10; + } + &.overflowHidden { --inner-radius: 0; overflow: hidden; } + // Ensure popovers can escape the card's bounds when open + &:has([data-headlessui-state='open']) { + overflow: visible; + z-index: 100; + } + .inner { position: relative; display: flex; @@ -60,6 +73,9 @@ .details { padding: 0.75rem; padding-top: 0; + overflow: visible; + position: relative; + z-index: 10; &.paired { position: absolute; @@ -68,7 +84,7 @@ right: 0; backdrop-filter: blur(2rem) saturate(2); border-radius: 0 0 var(--inner-radius) var(--inner-radius); - overflow: hidden; + overflow: visible; @include themed using($t) { background: linear-gradient(transparent, t($t, white, $fade-900)) diff --git a/src/elements/components/model-card.tsx b/src/elements/components/model-card.tsx index 7b59d0d8..9f8538db 100644 --- a/src/elements/components/model-card.tsx +++ b/src/elements/components/model-card.tsx @@ -257,7 +257,7 @@ const useMakeLazyCard = (lazy: boolean, card: JSX.Element) => { className={joinClasses( style.modelCard, !editMode && style.overflowHidden, - 'border-gray-300 bg-white shadow-lg hover:shadow-xl dark:border-gray-700 dark:bg-fade-900' + 'border-gray-300 bg-white shadow-lg hover:shadow-2xl dark:border-gray-700 dark:bg-fade-900' )} > {card} @@ -270,7 +270,7 @@ const useMakeLazyCard = (lazy: boolean, card: JSX.Element) => { } > diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 81e39a49..95c38654 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,3 +1,5 @@ +import { Inter } from 'next/font/google'; +import { useEffect } from 'react'; import '../styles/globals.scss'; import 'react-tooltip/dist/react-tooltip.css'; import { DevicePixelRatioProvider } from '../lib/hooks/use-device-pixel-ratio'; @@ -5,14 +7,27 @@ import { TooltipProvider } from '../lib/hooks/use-tooltip'; import { WebApiProvider } from '../lib/hooks/use-web-api'; import type { AppProps } from 'next/app'; +const inter = Inter({ + subsets: ['latin'], + variable: '--font-inter', + display: 'swap', +}); + export default function App({ Component, pageProps }: AppProps) { + useEffect(() => { + // Ensure the font variable is available on the root element + document.documentElement.style.setProperty('--font-inter', inter.style.fontFamily); + }, []); + return ( - - - - - - - +
+ + + + + + + +
); } diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 63ab9ec5..85bdc462 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -133,8 +133,12 @@ export default function Page({ modelData: staticModelData }: Props) {

-

- The best place to find AI Upscaling models +

+ The best place to find + + AI Upscaling + + models

diff --git a/src/pages/models/[id].tsx b/src/pages/models/[id].tsx index 8fe2b5cc..3155e916 100644 --- a/src/pages/models/[id].tsx +++ b/src/pages/models/[id].tsx @@ -334,26 +334,33 @@ function isTrue(value: T | null | undefined | false | '' | 0): value is T { } function MetadataTable({ rows }: { rows: (false | null | undefined | readonly [string, ReactNode])[] }) { + const filteredRows = rows.filter(isTrue); return ( - - - {rows.filter(isTrue).map((row, i) => { - const [label, value] = row; - const extraPadding = i === 0 ? 'pt-3' : i === rows.length - 1 ? 'pb-3' : ''; - return ( - - + + + ); + })} + +
+ + + {filteredRows.map((row, i) => { + const [label, value] = row; + const extraPadding = i === 0 ? 'pt-3' : i === filteredRows.length - 1 ? 'pb-3' : ''; + const isLastRow = i === filteredRows.length - 1; + return ( + - {label} - - - - ); - })} - -
{value}
+
+ {label} + {value}
+

); } export default function Page({ @@ -437,16 +444,20 @@ export default function Page({ )} - {/* Two columns */} -
- {/* Left column */} + {/* Full-width preview at top (YouTube-style) */} +
+ updateModelProperty('images', images)} + /> +
+ + {/* Two columns: Description and Sidebar */} +
+ {/* Left column: Description */}
- updateModelProperty('images', images)} - />
{editMode && ( @@ -564,7 +575,7 @@ export default function Page({
- {/* Right column */} + {/* Right column: Sidebar */}
{/* Download Button */}
diff --git a/src/styles/globals.scss b/src/styles/globals.scss index 4c021e3c..398e6ca1 100644 --- a/src/styles/globals.scss +++ b/src/styles/globals.scss @@ -50,10 +50,10 @@ :root { --max-page-width: 1400px; - --fonts-heading: -apple-system, blinkmacsystemfont, 'Segoe UI', helvetica, arial, sans-serif, 'Apple Color Emoji', - 'Segoe UI Emoji', 'Segoe UI Symbol'; - --fonts-body: -apple-system, blinkmacsystemfont, 'Segoe UI', helvetica, arial, sans-serif, 'Apple Color Emoji', - 'Segoe UI Emoji', 'Segoe UI Symbol'; + --fonts-heading: var(--font-inter), -apple-system, blinkmacsystemfont, 'Segoe UI', helvetica, arial, sans-serif, + 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; + --fonts-body: var(--font-inter), -apple-system, blinkmacsystemfont, 'Segoe UI', helvetica, arial, sans-serif, + 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; --fonts-mono: sfmono-regular, menlo, monaco, consolas, 'Liberation Mono', 'Courier New', monospace; overflow-y: scroll; diff --git a/tailwind.config.js b/tailwind.config.js index 91f5140d..50a18ff1 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -3,6 +3,17 @@ module.exports = { content: ['./src/**/*.{js,ts,jsx,tsx}'], theme: { extend: { + fontFamily: { + sans: [ + 'var(--font-inter)', + '-apple-system', + 'BlinkMacSystemFont', + 'Segoe UI', + 'Helvetica', + 'Arial', + 'sans-serif', + ], + }, colors: { 'fade-50': '#f9f8ff', 'fade-100': '#f3f2fd',