add loaders, sign button to match

This commit is contained in:
Mahesh Sanikommmu 2025-11-10 13:00:42 -08:00
parent 14bd1dc39b
commit 96f4e3d7f0
6 changed files with 338 additions and 17 deletions

View file

@ -9,10 +9,10 @@ export default function NewPage() {
return (
<div className="min-h-screen bg-black">
<Header />
<main className="relative min-h-screen">
<main className="relative">
<div className="relative z-10">
<div className="flex flex-row h-[calc(100vh-90px)] relative">
<div className="flex-1 flex flex-col justify-start p-6">
<div className="flex-1 flex flex-col justify-start p-6 pr-0">
<MemoriesGrid />
</div>

View file

@ -0,0 +1,227 @@
'use client';
import {
motion,
useMotionValue,
useTransform,
animate,
useReducedMotion,
} from 'motion/react';
import { useEffect, useMemo } from 'react';
import * as flubber from 'flubber';
type ChatLoaderProps = {
size?: number;
colorClassName?: string;
label?: string;
className?: string;
};
const LEFT_PATHS = [
'M12.6984 9.02793V3.52344H10.6523V9.49591C10.6523 10.1302 10.9028 10.7395 11.3479 11.1883L16.5188 16.4032L17.9655 14.9441L14.1463 11.0926H19.0324V9.02914L12.6984 9.02793Z', // 0
'M12.6984 9.02793V3.52344H10.6523V9.49591C10.6523 10.1302 10.9028 10.7395 11.3479 11.1883L16.5188 16.4032L17.9655 14.9441L14.1463 11.0926H14.149L12.699 9.02914L12.6984 9.02793Z', // 1
'M12.6985 9.02793V3.52344H10.6524V9.49591C10.6524 10.1302 10.6516 10.7381 10.6532 11.0926L10.6524 16.4075H12.6985L12.6991 11.0926V9.02914L12.6985 9.02793Z', // 2
'M14.5653 7.14453V7.1485H10.6528V8.0394C10.6528 9.25237 10.6512 10.4147 10.6542 11.0925L10.6528 11.0887H14.5653L14.5664 11.0925V7.14684L14.5653 7.14453Z', // 3
'M19.0304 8.51562V8.51963H15.0776V9.41971C15.0776 10.6452 15.076 11.8194 15.0791 12.5043L15.0776 12.5004H19.0304L19.0315 12.5043V8.51796L19.0304 8.51562Z', // 4
'M19.0304 8.51562V8.51963H15.0776V9.41971C15.0776 10.6452 15.076 11.8194 15.0791 12.5043L15.0776 12.5004H19.0304L19.0315 12.5043V8.51796L19.0304 8.51562Z', // 5
'M19.0304 8.51562V8.51963H15.0776V9.41971C15.0776 10.6452 15.076 11.8194 15.0791 12.5043L15.0776 12.5004H19.0304L19.0315 12.5043V8.51796L19.0304 8.51562Z', // 6
'M19.0304 6.51562V6.51963H15.0776V7.41971C15.0776 8.64517 15.076 9.81944 15.0791 10.5043L15.0776 10.5004H19.0304L19.0315 10.5043V6.51796L19.0304 6.51562Z', // 7
'M19.0304 8.51562V8.51963H15.0776V9.41971C15.0776 10.6452 15.076 11.8194 15.0791 12.5043L15.0776 12.5004H19.0304L19.0315 12.5043V8.51796L19.0304 8.51562Z', // 8
'M19.0304 8.51562V8.51963H15.0776V9.41971C15.0776 10.6452 15.076 11.8194 15.0791 12.5043L15.0776 12.5004H19.0304L19.0315 12.5043V8.51796L19.0304 8.51562Z', // 9
'M19.0304 6.51562V6.51963H15.0776V7.41971C15.0776 8.64517 15.076 9.81944 15.0791 10.5043L15.0776 10.5004H19.0304L19.0315 10.5043V6.51796L19.0304 6.51562Z', // 10
'M19.0304 8.51562V8.51963H15.0776V9.41971C15.0776 10.6452 15.076 11.8194 15.0791 12.5043L15.0776 12.5004H19.0304L19.0315 12.5043V8.51796L19.0304 8.51562Z', // 11
'M19.0304 8.51562V8.51963H15.0776V9.41971C15.0776 10.6452 15.076 11.8194 15.0791 12.5043L15.0776 12.5004H19.0304L19.0315 12.5043V8.51796L19.0304 8.51562Z', // 12
'M19.0304 6.51562V6.51963H15.0776V7.41971C15.0776 8.64517 15.076 9.81944 15.0791 10.5043L15.0776 10.5004H19.0304L19.0315 10.5043V6.51796L19.0304 6.51562Z', // 13
'M19.0304 8.51562V8.51963H15.0776V9.41971C15.0776 10.6452 15.076 11.8194 15.0791 12.5043L15.0776 12.5004H19.0304L19.0315 12.5043V8.51796L19.0304 8.51562Z', // 14
'M14.5653 7.14453V7.1485H10.6528V8.0394C10.6528 9.25237 10.6512 10.4147 10.6542 11.0925L10.6528 11.0887H14.5653L14.5664 11.0925V7.14684L14.5653 7.14453Z', // 15
'M12.6985 9.02793V3.52344H10.6524V9.49591C10.6524 10.1302 10.6516 10.7381 10.6532 11.0926L10.6524 16.4075H12.6985L12.6991 11.0926V9.02914L12.6985 9.02793Z', // 16
'M12.6984 9.02793V3.52344H10.6523V9.49591C10.6523 10.1302 10.9028 10.7395 11.3479 11.1883L16.5188 16.4032L17.9655 14.9441L14.1463 11.0926H14.149L12.699 9.02914L12.6984 9.02793Z', // 17
'M12.6984 9.02793V3.52344H10.6523V9.49591C10.6523 10.1302 10.9028 10.7395 11.3479 11.1883L16.5188 16.4032L17.9655 14.9441L14.1463 11.0926H19.0324V9.02914L12.6984 9.02793Z', // 18
];
const MIDDLE_PATHS = [
'M6.60156 9.46875L6.60171 11.5326L6.60231 11.5286H8.64841V11.0646C8.64841 10.4302 8.64841 10.1053 8.64841 9.46911L6.60156 9.46875Z', // 0
'M6.60156 9.46875L6.60171 11.5326L6.60231 11.5286H8.64841V11.0646C8.64841 10.4302 8.64841 10.1053 8.64841 9.46911L6.60156 9.46875Z', // 1
'M6.60156 9.46875L6.60171 11.5326L6.60231 11.5286H8.64841V11.0646C8.64841 10.4302 8.64841 10.1053 8.64841 9.46911L6.60156 9.46875Z', // 2
'M6.60156 9.46875L6.60171 11.5326L6.60231 11.5286H8.64841V11.0646C8.64841 10.4302 8.64841 10.1053 8.64841 9.46911L6.60156 9.46875Z', // 3
'M1.96875 8.50391L1.96904 12.4909L1.9702 12.4833H5.92302V11.5868C5.92302 10.3613 5.92302 9.73358 5.92302 8.50459L1.96875 8.50391Z', // 4
'M1.96875 6.50391L1.96904 10.4909L1.9702 10.4833H5.92302V9.58685C5.92302 8.3613 5.92302 7.73358 5.92302 6.50459L1.96875 6.50391Z', // 5
'M1.96875 8.50391L1.96904 12.4909L1.9702 12.4833H5.92302V11.5868C5.92302 10.3613 5.92302 9.73358 5.92302 8.50459L1.96875 8.50391Z', // 6
'M1.96875 8.50391L1.96904 12.4909L1.9702 12.4833H5.92302V11.5868C5.92302 10.3613 5.92302 9.73358 5.92302 8.50459L1.96875 8.50391Z', // 7
'M1.96875 6.50391L1.96904 10.4909L1.9702 10.4833H5.92302V9.58685C5.92302 8.3613 5.92302 7.73358 5.92302 6.50459L1.96875 6.50391Z', // 8
'M1.96875 8.50391L1.96904 12.4909L1.9702 12.4833H5.92302V11.5868C5.92302 10.3613 5.92302 9.73358 5.92302 8.50459L1.96875 8.50391Z', // 9
'M1.96875 8.50391L1.96904 12.4909L1.9702 12.4833H5.92302V11.5868C5.92302 10.3613 5.92302 9.73358 5.92302 8.50459L1.96875 8.50391Z', // 10
'M1.96875 6.50391L1.96904 10.4909L1.9702 10.4833H5.92302V9.58685C5.92302 8.3613 5.92302 7.73358 5.92302 6.50459L1.96875 6.50391Z', // 11
'M1.96875 8.50391L1.96904 12.4909L1.9702 12.4833H5.92302V11.5868C5.92302 10.3613 5.92302 9.73358 5.92302 8.50459L1.96875 8.50391Z', // 12
'M1.96875 8.50391L1.96904 12.4909L1.9702 12.4833H5.92302V11.5868C5.92302 10.3613 5.92302 9.73358 5.92302 8.50459L1.96875 8.50391Z', // 13
'M1.96875 8.50391L1.96904 12.4909L1.9702 12.4833H5.92302V11.5868C5.92302 10.3613 5.92302 9.73358 5.92302 8.50459L1.96875 8.50391Z', // 14
'M1.96875 8.50391L1.96904 12.4909L1.9702 12.4833H5.92302V11.5868C5.92302 10.3613 5.92302 9.73358 5.92302 8.50459L1.96875 8.50391Z', // 15
'M6.60156 9.46875L6.60171 11.5326L6.60231 11.5286H8.64841V11.0646C8.64841 10.4302 8.64841 10.1053 8.64841 9.46911L6.60156 9.46875Z', // 16
'M6.60156 9.46875L6.60171 11.5326L6.60231 11.5286H8.64841V11.0646C8.64841 10.4302 8.64841 10.1053 8.64841 9.46911L6.60156 9.46875Z', // 17
'M6.60156 9.46875L6.60171 11.5326L6.60231 11.5286H8.64841V11.0646C8.64841 10.4302 8.64841 10.1053 8.64841 9.46911L6.60156 9.46875Z', // 18
];
const RIGHT_PATHS = [
'M3.03472 6.05861L6.8539 9.91021H1.96777V11.9737H8.3006V17.4781H10.3467V11.5057C10.3467 10.8713 10.0963 10.2621 9.65119 9.81327L4.48145 4.59961L3.03472 6.05861Z', // 0
'M3.03516 6.05861L6.85434 9.91021H6.85044L8.30044 11.9737L8.30104 17.4781H10.3471V11.5057C10.3471 10.8713 10.0967 10.2621 9.65162 9.81327L4.48188 4.59961L3.03516 6.05861Z', // 1
'M8.30024 4.58789L8.2998 9.91036L8.30039 11.9738L8.30099 17.4783H10.3471V11.5058C10.3471 10.8714 10.3471 10.5465 10.3471 9.91036V4.58789H8.30024Z', // 2
'M6.42383 9.9082L6.42412 13.8633L6.42527 13.8557H10.3464V12.9664C10.3464 11.7507 10.3464 11.128 10.3464 9.90888L6.42383 9.9082Z', // 3
'M8.52051 8.50391L8.5208 12.4909L8.52196 12.4833H12.4748V11.5868C12.4748 10.3613 12.4748 9.73358 12.4748 8.50459L8.52051 8.50391Z', // 4
'M8.52051 8.50391L8.5208 12.4909L8.52196 12.4833H12.4748V11.5868C12.4748 10.3613 12.4748 9.73358 12.4748 8.50459L8.52051 8.50391Z', // 5
'M8.52051 6.50391L8.5208 10.4909L8.52196 10.4833H12.4748V9.58685C12.4748 8.3613 12.4748 7.73358 12.4748 6.50459L8.52051 6.50391Z', // 6
'M8.52051 8.50391L8.5208 12.4909L8.52196 12.4833H12.4748V11.5868C12.4748 10.3613 12.4748 9.73358 12.4748 8.50459L8.52051 8.50391Z', // 7
'M8.52051 8.50391L8.5208 12.4909L8.52196 12.4833H12.4748V11.5868C12.4748 10.3613 12.4748 9.73358 12.4748 8.50459L8.52051 8.50391Z', // 8
'M8.52051 6.50391L8.5208 10.4909L8.52196 10.4833H12.4748V9.58685C12.4748 8.3613 12.4748 7.73358 12.4748 6.50459L8.52051 6.50391Z', // 9
'M8.52051 8.50391L8.5208 12.4909L8.52196 12.4833H12.4748V11.5868C12.4748 10.3613 12.4748 9.73358 12.4748 8.50459L8.52051 8.50391Z', // 10
'M8.52051 8.50391L8.5208 12.4909L8.52196 12.4833H12.4748V11.5868C12.4748 10.3613 12.4748 9.73358 12.4748 8.50459L8.52051 8.50391Z', // 11
'M8.52051 6.50391L8.5208 10.4909L8.52196 10.4833H12.4748V9.58685C12.4748 8.3613 12.4748 7.73358 12.4748 6.50459L8.52051 6.50391Z', // 12
'M8.52051 8.50391L8.5208 12.4909L8.52196 12.4833H12.4748V11.5868C12.4748 10.3613 12.4748 9.73358 12.4748 8.50459L8.52051 8.50391Z', // 13
'M8.52051 8.50391L8.5208 12.4909L8.52196 12.4833H12.4748V11.5868C12.4748 10.3613 12.4748 9.73358 12.4748 8.50459L8.52051 8.50391Z', // 14
'M6.42383 9.9082L6.42412 13.8633L6.42527 13.8557H10.3464V12.9664C10.3464 11.7507 10.3464 11.128 10.3464 9.90888L6.42383 9.9082Z', // 15
'M8.30024 4.58789L8.2998 9.91036L8.30039 11.9738L8.30099 17.4783H10.3471V11.5058C10.3471 10.8714 10.3471 10.5465 10.3471 9.91036V4.58789H8.30024Z', // 16
'M3.03516 6.05861L6.85434 9.91021H6.85044L8.30044 11.9737L8.30104 17.4781H10.3471V11.5057C10.3471 10.8713 10.0967 10.2621 9.65162 9.81327L4.48188 4.59961L3.03516 6.05861Z', // 17
'M3.03472 6.05861L6.8539 9.91021H1.96777V11.9737H8.3006V17.4781H10.3467V11.5057C10.3467 10.8713 10.0963 10.2621 9.65119 9.81327L4.48145 4.59961L3.03472 6.05861Z', // 18
];
export function ChatLoader({
size = 80,
colorClassName = 'text-white',
label = '',
className = '',
}: ChatLoaderProps) {
const prefersReducedMotion = useReducedMotion();
const t = useMotionValue(0);
const loopDuration = 3.6; // full cycle
const makeMultiInterp = (paths: string[]) => {
if (!paths || paths.length === 0) {
return (_t: number) => '';
}
if (paths.length === 1) {
const only = paths[0];
return (_t: number) => only;
}
const options: { maxSegmentLength?: number } = { maxSegmentLength: 0.5 };
const interpolateFn = (flubber as any).interpolate as (
from: string,
to: string,
options?: { maxSegmentLength?: number }
) => (t: number) => string;
const segmentInterpolators: Array<(t: number) => string> = [];
for (let i = 0; i < paths.length - 1; i++) {
segmentInterpolators.push(interpolateFn(paths[i], paths[i + 1], options));
}
const segmentCount = segmentInterpolators.length;
return (t: number) => {
if (t <= 0) return paths[0] || '';
if (t >= 1) return paths[paths.length - 1] || '';
const scaled = t * segmentCount;
const segIndex = Math.min(Math.floor(scaled), segmentCount - 1);
const localT = scaled - segIndex;
return segmentInterpolators[segIndex](localT);
};
};
const leftInterp = useMemo(
() => (LEFT_PATHS.length ? makeMultiInterp(LEFT_PATHS) : null),
[]
);
const middleInterp = useMemo(
() => (MIDDLE_PATHS.length ? makeMultiInterp(MIDDLE_PATHS) : null),
[]
);
const rightInterp = useMemo(
() => (RIGHT_PATHS.length ? makeMultiInterp(RIGHT_PATHS) : null),
[]
);
// Turn scalar t into d strings
const leftD = useTransform(t, v =>
leftInterp ? leftInterp(v) : LEFT_PATHS[0] || ''
);
const middleD = useTransform(t, v =>
middleInterp ? middleInterp(v) : MIDDLE_PATHS[0] || ''
);
const rightD = useTransform(t, v =>
rightInterp ? rightInterp(v) : RIGHT_PATHS[0] || ''
);
const middleOpacity = useTransform(t, v => {
if (v < 0.2) return 0;
if (v < 0.3) return (v - 0.2) / 0.1; // fade in
if (v < 0.8) return 1;
if (v < 0.9) return 1 - (v - 0.8) / 0.1; // fade out
return 0;
});
useEffect(() => {
if (prefersReducedMotion) {
t.set(0);
return;
}
const controls = animate(t, [0, 1], {
duration: loopDuration,
ease: 'linear',
repeat: Infinity,
repeatType: 'loop',
repeatDelay: 0.4, // ⬅️ wait 2 seconds at the end before restarting
});
return () => controls.stop();
}, [t, prefersReducedMotion, loopDuration]);
return (
<div
role='status'
aria-label={label}
className={`inline-flex flex-col items-center gap-2 ${className}`}
style={{ width: size }}
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 36 20'
width={size}
height={(size * 20) / 36}
className={colorClassName}
>
{leftInterp && <motion.path d={leftD as any} fill='currentColor' />}
{rightInterp && <motion.path d={rightD as any} fill='currentColor' />}
{middleInterp && (
<motion.path
d={middleD as any}
fill='currentColor'
style={{ opacity: middleOpacity as any }}
/>
)}
</svg>
{label && (
<span
className='text-xs font-medium text-slate-400'
style={{ fontSize: size * 0.18 }}
>
{label}
</span>
)}
</div>
);
}

View file

@ -7,8 +7,8 @@ import { useInfiniteQuery } from "@tanstack/react-query"
import { useCallback, memo, useMemo } from "react"
import type { z } from "zod"
import { Masonry, useInfiniteLoader } from "masonic"
import { Sparkles } from "lucide-react"
import { dmSansClassName } from "@/utils/fonts"
import { SuperLoader } from "@/components/superloader"
import { cn } from "@lib/utils"
import { Button } from "@ui/components/button"
import { useProject } from "@/stores"
@ -158,10 +158,7 @@ export function MemoriesGrid() {
</div>
) : isPending ? (
<div className="h-full flex items-center justify-center p-4">
<div className="flex items-center gap-2">
<Sparkles className="w-4 h-4 animate-spin text-blue-400" />
<span>Loading memory list...</span>
</div>
<SuperLoader />
</div>
) : documents.length === 0 && !isPending ? (
<div className="h-full flex items-center justify-center p-4">
@ -186,10 +183,7 @@ export function MemoriesGrid() {
{isLoadingMore && (
<div className="py-8 flex items-center justify-center">
<div className="flex items-center gap-2">
<Sparkles className="w-4 h-4 animate-spin text-blue-400" />
<span>Loading more memories...</span>
</div>
<SuperLoader />
</div>
)}
</div>

View file

@ -0,0 +1,98 @@
'use client';
import { motion, useReducedMotion, Variants } from 'motion/react';
type NovaPathLoaderProps = {
size?: number; // px
colorClassName?: string;
label?: string;
className?: string;
};
// full SVG path data from nova-2d-anim.svg
const PATH_RIGHT =
'M3.03472 6.05861L6.8539 9.91021H1.96777V11.9737H8.3006V17.4781H10.3467V11.5057C10.3467 10.8713 10.0963 10.2621 9.65119 9.81327L4.48145 4.59961L3.03472 6.05861Z';
const PATH_LEFT =
'M12.6994 9.02793V3.52344H10.6533V9.49591C10.6533 10.1302 10.9037 10.7395 11.3488 11.1883L16.5197 16.4032L17.9665 14.9441L14.1473 11.0926H19.0334V9.02914L12.6994 9.02793Z';
// animation for stroke draw
const strokeVariants: Variants = {
hidden: { pathLength: 0, opacity: 0.2 },
visible: (i: number) => ({
pathLength: [0, 1],
opacity: [0.2, 1],
transition: {
duration: 0.9,
repeat: Infinity,
repeatType: 'reverse',
ease: 'easeInOut',
delay: i * 0.18,
},
}),
static: { pathLength: 1, opacity: 0.7 },
};
export function SuperLoader({
size = 42,
colorClassName = 'text-sky-400',
label = 'Loading...',
className = '',
}: NovaPathLoaderProps) {
const prefersReducedMotion = useReducedMotion();
const animateVariant = prefersReducedMotion ? 'static' : 'visible';
return (
<div
role='status'
aria-label={label}
className={`inline-flex flex-col items-center gap-2 ${className}`}
style={{ width: size + 10 }}
>
<motion.svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 21 21'
width={size}
height={size}
className={`shrink-0 ${colorClassName}`}
>
{/* Right path */}
<motion.path
d={PATH_RIGHT}
fill='none'
stroke='currentColor'
strokeWidth={1.4}
strokeLinecap='round'
strokeLinejoin='round'
initial='hidden'
animate={animateVariant}
variants={strokeVariants}
custom={0}
/>
{/* Left path */}
<motion.path
d={PATH_LEFT}
fill='none'
stroke='currentColor'
strokeWidth={1.4}
strokeLinecap='round'
strokeLinejoin='round'
initial='hidden'
animate={animateVariant}
variants={strokeVariants}
custom={1}
/>
</motion.svg>
<span
className='text-xs font-medium text-slate-500'
style={{ fontSize: size * 0.25 }}
>
{label}
</span>
</div>
);
}

View file

@ -58,6 +58,7 @@
"dotenv": "^16.6.0",
"embla-carousel-autoplay": "^8.6.0",
"embla-carousel-react": "^8.6.0",
"flubber": "^0.4.2",
"framer-motion": "^12.23.12",
"idb-keyval": "^6.2.2",
"is-hotkey": "^0.2.0",

View file

@ -16,6 +16,7 @@ import { useState, useEffect } from "react";
import { motion } from "framer-motion";
import { dmSansClassName } from "../../../apps/web/utils/fonts"
import { cn } from "@lib/utils";
import { Logo } from "@ui/assets/Logo";
function AnimatedGradientBackground() {
return (
@ -419,17 +420,17 @@ export function LoginPage() {
<div className="relative">
<Button
className="w-full h-11"
className='flex justify-center items-center w-full h-[44px] relative gap-3 p-2 rounded-xl'
style={{
borderRadius: "12px",
background:
"linear-gradient(180deg, #0FF0D2 0%, #5349dd 1%, #1E0FF0 100%)",
boxShadow:
"1px 1px 1px 1px #1A88FF inset, 0 2px 10px 0 rgba(5, 1, 0, 0.20)",
background:
'linear-gradient(182.37deg, #0ff0d2 -91.53%, #5bd3fb -67.8%, #1e0ff0 95.17%)',
boxShadow:
'1px 1px 2px 0px #1A88FF inset, 0 2px 10px 0 rgba(5, 1, 0, 0.20)',
}}
disabled={isLoading}
type="submit"
>
<Logo className="size-4" />
{isLoadingEmail
? "Sending login link..."
: "Log in with Supermemory"}