-
+
diff --git a/apps/web/components/chat-loader.tsx b/apps/web/components/chat-loader.tsx
new file mode 100644
index 00000000..788ec88d
--- /dev/null
+++ b/apps/web/components/chat-loader.tsx
@@ -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 (
+
) : isPending ? (
+
+
+ {label && (
+
+ {label}
+
+ )}
+
+ );
+}
+
diff --git a/apps/web/components/new/memories-grid.tsx b/apps/web/components/new/memories-grid.tsx
index 74116e4f..19b11feb 100644
--- a/apps/web/components/new/memories-grid.tsx
+++ b/apps/web/components/new/memories-grid.tsx
@@ -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() {
-
) : documents.length === 0 && !isPending ? (
-
- Loading memory list...
-
+
@@ -186,10 +183,7 @@ export function MemoriesGrid() {
{isLoadingMore && (
diff --git a/apps/web/components/superloader.tsx b/apps/web/components/superloader.tsx
new file mode 100644
index 00000000..55998d1a
--- /dev/null
+++ b/apps/web/components/superloader.tsx
@@ -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 (
+
-
)}
-
- Loading more memories...
-
+
+
+ {/* Right path */}
+
+
+ {/* Left path */}
+
+
+
+
+ {label}
+
+
+ );
+}
+
diff --git a/apps/web/package.json b/apps/web/package.json
index 3590aba4..dc4a5772 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -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",
diff --git a/packages/ui/pages/login.tsx b/packages/ui/pages/login.tsx
index 8b9eeff1..971fb746 100644
--- a/packages/ui/pages/login.tsx
+++ b/packages/ui/pages/login.tsx
@@ -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() {