Commit 4918e579 authored by Administrator's avatar Administrator

Update 13 files via Son of Anton

parent b02ad490
{ {
"name": "son-of-anton-frontend", "name": "son-of-anton-frontend",
"version": "1.0.0", "version": "2.0.0",
"private": true, "private": true,
"type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vite build", "build": "vite build",
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"lucide-react": "^0.469.0",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-markdown": "^9.0.1",
"react-router-dom": "^7.1.1", "react-router-dom": "^7.1.1",
"react-markdown": "^9.0.1",
"react-syntax-highlighter": "^15.6.1", "react-syntax-highlighter": "^15.6.1",
"remark-gfm": "^4.0.0" "remark-gfm": "^4.0.0",
"lucide-react": "^0.469.0",
"@radix-ui/react-dialog": "^1.1.4",
"@radix-ui/react-dropdown-menu": "^2.1.4",
"@radix-ui/react-popover": "^1.1.4",
"@radix-ui/react-select": "^2.1.4",
"@radix-ui/react-slider": "^1.2.2",
"@radix-ui/react-switch": "^1.1.2",
"@radix-ui/react-tabs": "^1.1.2",
"@radix-ui/react-tooltip": "^1.1.6",
"@radix-ui/react-accordion": "^1.2.2",
"@radix-ui/react-avatar": "^1.1.2",
"@radix-ui/react-checkbox": "^1.1.3",
"@radix-ui/react-context-menu": "^2.2.4",
"@radix-ui/react-scroll-area": "^1.2.2",
"@radix-ui/react-separator": "^1.1.1",
"@radix-ui/react-toggle": "^1.1.1",
"@radix-ui/react-toggle-group": "^1.1.1",
"@radix-ui/react-progress": "^1.1.1",
"@radix-ui/react-alert-dialog": "^1.1.4",
"framer-motion": "^11.18.0",
"sonner": "^1.7.1",
"cmdk": "^1.0.4",
"vaul": "^1.1.2",
"recharts": "^2.15.0",
"react-hot-toast": "^2.5.2",
"input-otp": "^1.4.2",
"react-resizable-panels": "^2.1.7",
"embla-carousel-react": "^8.5.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"tailwind-merge": "^2.6.0",
"date-fns": "^4.1.0",
"react-day-picker": "^9.5.0",
"zustand": "^5.0.3",
"usehooks-ts": "^3.1.0"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "^18.3.18", "@types/react": "^18.3.18",
...@@ -23,6 +58,10 @@ ...@@ -23,6 +58,10 @@
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"postcss": "^8.4.49", "postcss": "^8.4.49",
"tailwindcss": "^3.4.17", "tailwindcss": "^3.4.17",
"vite": "^6.0.7" "vite": "^6.0.7",
"@tailwindcss/typography": "^0.5.16",
"@tailwindcss/forms": "^0.5.10",
"@tailwindcss/container-queries": "^0.1.1",
"tailwindcss-animate": "^1.0.7"
} }
} }
\ No newline at end of file
import React from "react";
import { cva } from "class-variance-authority";
import { cn } from "../../lib/utils";
const badgeVariants = cva(
"inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
{
variants: {
variant: {
default: "border-transparent bg-primary text-primary-foreground",
secondary: "border-transparent bg-secondary text-secondary-foreground",
destructive: "border-transparent bg-destructive text-destructive-foreground",
outline: "text-foreground",
success: "border-transparent bg-green-500/15 text-green-400 border-green-500/20",
warning: "border-transparent bg-yellow-500/15 text-yellow-400 border-yellow-500/20",
info: "border-transparent bg-blue-500/15 text-blue-400 border-blue-500/20",
accent: "border-transparent bg-primary/15 text-primary border-primary/20",
},
},
defaultVariants: {
variant: "default",
},
}
);
function Badge({ className, variant, ...props }) {
return <div className={cn(badgeVariants({ variant }), className)} {...props} />;
}
export { Badge, badgeVariants };
\ No newline at end of file
import React from "react";
import { cva } from "class-variance-authority";
import { cn } from "../../lib/utils";
import { Loader2 } from "lucide-react";
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg text-sm font-medium transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 active:scale-[0.98]",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90 shadow-sm",
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90 shadow-sm",
outline: "border border-border bg-transparent hover:bg-white/5 hover:text-foreground",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-white/5 hover:text-foreground",
link: "text-primary underline-offset-4 hover:underline",
glow: "bg-primary text-primary-foreground hover:bg-primary/90 shadow-glow hover:shadow-glow-lg",
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-11 rounded-lg px-8 text-base",
xl: "h-12 rounded-xl px-10 text-base",
icon: "h-9 w-9",
"icon-sm": "h-8 w-8",
"icon-lg": "h-11 w-11",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
);
const Button = React.forwardRef(
({ className, variant, size, loading, children, disabled, ...props }, ref) => {
return (
<button
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
disabled={disabled || loading}
{...props}
>
{loading && <Loader2 className="animate-spin" />}
{children}
</button>
);
}
);
Button.displayName = "Button";
export { Button, buttonVariants };
\ No newline at end of file
import React from "react";
import { cn } from "../../lib/utils";
const Card = React.forwardRef(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn(
"rounded-xl border border-border bg-card text-card-foreground shadow-sm transition-shadow hover:shadow-card-hover",
className
)}
{...props}
/>
));
Card.displayName = "Card";
const CardHeader = React.forwardRef(({ className, ...props }, ref) => (
<div ref={ref} className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />
));
CardHeader.displayName = "CardHeader";
const CardTitle = React.forwardRef(({ className, ...props }, ref) => (
<h3 ref={ref} className={cn("font-semibold leading-none tracking-tight text-white", className)} {...props} />
));
CardTitle.displayName = "CardTitle";
const CardDescription = React.forwardRef(({ className, ...props }, ref) => (
<p ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />
));
CardDescription.displayName = "CardDescription";
const CardContent = React.forwardRef(({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
));
CardContent.displayName = "CardContent";
const CardFooter = React.forwardRef(({ className, ...props }, ref) => (
<div ref={ref} className={cn("flex items-center p-6 pt-0", className)} {...props} />
));
CardFooter.displayName = "CardFooter";
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };
\ No newline at end of file
import React from "react";
import { cn } from "../../lib/utils";
const Input = React.forwardRef(({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
"flex h-9 w-full rounded-lg border border-border bg-transparent px-3 py-1 text-sm shadow-sm transition-colors",
"file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground",
"placeholder:text-muted-foreground",
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background",
"disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props}
/>
);
});
Input.displayName = "Input";
export { Input };
\ No newline at end of file
import { motion, AnimatePresence } from "framer-motion";
/**
* Pre-configured motion variants for common animations.
* Use these instead of raw CSS animations for buttery-smooth UX.
*/
export const fadeIn = {
initial: { opacity: 0 },
animate: { opacity: 1 },
exit: { opacity: 0 },
transition: { duration: 0.2 },
};
export const fadeInUp = {
initial: { opacity: 0, y: 12 },
animate: { opacity: 1, y: 0 },
exit: { opacity: 0, y: 12 },
transition: { duration: 0.25, ease: "easeOut" },
};
export const fadeInDown = {
initial: { opacity: 0, y: -12 },
animate: { opacity: 1, y: 0 },
exit: { opacity: 0, y: -12 },
transition: { duration: 0.25, ease: "easeOut" },
};
export const scaleIn = {
initial: { opacity: 0, scale: 0.95 },
animate: { opacity: 1, scale: 1 },
exit: { opacity: 0, scale: 0.95 },
transition: { duration: 0.2, ease: "easeOut" },
};
export const slideInRight = {
initial: { opacity: 0, x: 20 },
animate: { opacity: 1, x: 0 },
exit: { opacity: 0, x: 20 },
transition: { duration: 0.25, ease: "easeOut" },
};
export const slideInLeft = {
initial: { opacity: 0, x: -20 },
animate: { opacity: 1, x: 0 },
exit: { opacity: 0, x: -20 },
transition: { duration: 0.25, ease: "easeOut" },
};
export const staggerContainer = {
animate: {
transition: {
staggerChildren: 0.05,
delayChildren: 0.1,
},
},
};
export const staggerItem = {
initial: { opacity: 0, y: 10 },
animate: { opacity: 1, y: 0 },
transition: { duration: 0.2 },
};
/**
* MotionDiv — a pre-configured motion.div with fadeInUp by default.
* Pass any variant prop to override.
*/
export function MotionDiv({ children, className, variants = fadeInUp, ...props }) {
return (
<motion.div className={className} {...variants} {...props}>
{children}
</motion.div>
);
}
/**
* MotionList — staggered list animation
*/
export function MotionList({ children, className, ...props }) {
return (
<motion.div
className={className}
variants={staggerContainer}
initial="initial"
animate="animate"
{...props}
>
{children}
</motion.div>
);
}
export function MotionItem({ children, className, ...props }) {
return (
<motion.div className={className} variants={staggerItem} {...props}>
{children}
</motion.div>
);
}
export { motion, AnimatePresence };
\ No newline at end of file
import * as React from "react";
import * as SeparatorPrimitive from "@radix-ui/react-separator";
import { cn } from "../../lib/utils";
const Separator = React.forwardRef(
({ className, orientation = "horizontal", decorative = true, ...props }, ref) => (
<SeparatorPrimitive.Root
ref={ref}
decorative={decorative}
orientation={orientation}
className={cn(
"shrink-0 bg-border",
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
className
)}
{...props}
/>
)
);
Separator.displayName = SeparatorPrimitive.Root.displayName;
export { Separator };
\ No newline at end of file
import { cn } from "../../lib/utils";
function Skeleton({ className, ...props }) {
return (
<div
className={cn(
"animate-pulse rounded-md bg-white/[0.06]",
className
)}
{...props}
/>
);
}
export { Skeleton };
\ No newline at end of file
import { Toaster as SonnerToaster } from "sonner";
/**
* Drop this into App.jsx: <Toaster />
* Then use: toast("Hello"), toast.success("Done"), toast.error("Shit")
*/
export function Toaster() {
return (
<SonnerToaster
position="bottom-right"
toastOptions={{
style: {
background: "hsl(230, 18%, 8%)",
border: "1px solid hsl(230, 14%, 16%)",
color: "hsl(220, 15%, 85%)",
fontSize: "13px",
},
classNames: {
success: "!border-green-500/30",
error: "!border-red-500/30",
warning: "!border-yellow-500/30",
info: "!border-blue-500/30",
},
}}
theme="dark"
richColors
closeButton
/>
);
}
\ No newline at end of file
import * as React from "react";
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
import { cn } from "../../lib/utils";
const TooltipProvider = TooltipPrimitive.Provider;
const Tooltip = TooltipPrimitive.Root;
const TooltipTrigger = TooltipPrimitive.Trigger;
const TooltipContent = React.forwardRef(
({ className, sideOffset = 4, ...props }, ref) => (
<TooltipPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 overflow-hidden rounded-md bg-popover border border-border px-3 py-1.5 text-xs text-popover-foreground shadow-md",
"animate-in fade-in-0 zoom-in-95",
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2",
"data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
/>
)
);
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
\ No newline at end of file
...@@ -3,283 +3,296 @@ ...@@ -3,283 +3,296 @@
@tailwind utilities; @tailwind utilities;
/* ═══════════════════════════════════════════════════ /* ═══════════════════════════════════════════════════
ROOT VARIABLES & BASE DESIGN TOKENS — HSL values for complete theming
═══════════════════════════════════════════════════ */ ═══════════════════════════════════════════════════ */
:root { @layer base {
--sat: env(safe-area-inset-top, 0px); :root {
--sar: env(safe-area-inset-right, 0px); /* ── Anton Brand Tokens ── */
--sab: env(safe-area-inset-bottom, 0px); --anton-bg: 230 20% 4%;
--sal: env(safe-area-inset-left, 0px); --anton-surface: 230 18% 7%;
--header-h: 3.25rem; --anton-card: 230 16% 10%;
color-scheme: dark; --anton-border: 230 14% 16%;
} --anton-text: 220 15% 85%;
--anton-muted: 220 10% 45%;
--anton-accent: 0 75% 58%;
--anton-success: 142 70% 45%;
--anton-danger: 0 85% 60%;
--anton-warning: 38 92% 50%;
--anton-info: 217 91% 60%;
/* ── shadcn-compatible Semantic Tokens ── */
--background: 230 20% 4%;
--foreground: 220 15% 85%;
--card: 230 16% 10%;
--card-foreground: 220 15% 85%;
--popover: 230 18% 8%;
--popover-foreground: 220 15% 85%;
--primary: 0 75% 58%;
--primary-foreground: 0 0% 100%;
--secondary: 230 16% 14%;
--secondary-foreground: 220 15% 85%;
--muted: 230 14% 16%;
--muted-foreground: 220 10% 45%;
--accent: 230 16% 14%;
--accent-foreground: 220 15% 85%;
--destructive: 0 85% 60%;
--destructive-foreground: 0 0% 100%;
--border: 230 14% 16%;
--input: 230 14% 16%;
--ring: 0 75% 58%;
--radius: 0.75rem;
}
/* ═══════════════════════════════════════════════════ * {
GLOBAL RESETS FOR MOBILE @apply border-border;
═══════════════════════════════════════════════════ */ }
*, *::before, *::after { body {
-webkit-tap-highlight-color: transparent; @apply bg-background text-foreground antialiased;
-webkit-touch-callout: none; font-feature-settings: "rlig" 1, "calt" 1;
}
} }
html { /* ═══════════════════════════════════════════════════
overflow: hidden; SCROLLBAR — Dark themed, thin, sexy
height: 100%; ═══════════════════════════════════════════════════ */
height: 100dvh;
}
body { @layer base {
overflow: hidden; * {
height: 100%; scrollbar-width: thin;
height: 100dvh; scrollbar-color: hsl(var(--anton-border)) transparent;
overscroll-behavior: none; }
-webkit-overflow-scrolling: touch;
font-family: 'Inter', system-ui, -apple-system, sans-serif;
position: fixed;
width: 100%;
top: 0;
left: 0;
}
#root { ::-webkit-scrollbar {
height: 100%; @apply w-1.5 h-1.5;
height: 100dvh; }
overflow: hidden; ::-webkit-scrollbar-track {
display: flex; @apply bg-transparent;
flex-direction: column; }
::-webkit-scrollbar-thumb {
@apply bg-white/10 rounded-full;
}
::-webkit-scrollbar-thumb:hover {
@apply bg-white/20;
}
} }
/* ═══════════════════════════════════════════════════ /* ═══════════════════════════════════════════════════
SAFE AREA UTILITIES COMPONENT UTILITIES
═══════════════════════════════════════════════════ */ ═══════════════════════════════════════════════════ */
.safe-top { padding-top: var(--sat); } @layer utilities {
.safe-bottom { padding-bottom: max(var(--sab), 8px); } /* Glass morphism */
.safe-left { padding-left: var(--sal); } .glass {
.safe-right { padding-right: var(--sar); } @apply bg-white/[0.03] backdrop-blur-xl border border-white/[0.06];
}
.glass-heavy {
@apply bg-white/[0.06] backdrop-blur-2xl border border-white/[0.08];
}
/* ═══════════════════════════════════════════════════ /* Gradient text */
SCROLLBAR .text-gradient {
═══════════════════════════════════════════════════ */ @apply bg-clip-text text-transparent bg-gradient-to-r;
}
.text-gradient-brand {
@apply text-gradient from-red-400 via-orange-400 to-red-500;
}
.text-gradient-cool {
@apply text-gradient from-blue-400 via-purple-400 to-pink-400;
}
::-webkit-scrollbar { width: 4px; height: 4px; } /* Glow effects */
::-webkit-scrollbar-track { background: transparent; } .glow-sm {
::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.08); border-radius: 4px; } box-shadow: 0 0 10px rgba(229, 62, 62, 0.1);
::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.15); } }
.glow-md {
box-shadow: 0 0 20px rgba(229, 62, 62, 0.15);
}
.glow-lg {
box-shadow: 0 0 40px rgba(229, 62, 62, 0.2);
}
/* ═══════════════════════════════════════════════════ /* Noise texture overlay */
ANIMATIONS .noise {
═══════════════════════════════════════════════════ */ position: relative;
}
.noise::after {
content: "";
position: absolute;
inset: 0;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)' opacity='0.02'/%3E%3C/svg%3E");
pointer-events: none;
z-index: 1;
}
@keyframes fadeIn { /* Focus ring */
from { opacity: 0; transform: translateY(6px); } .focus-ring {
to { opacity: 1; transform: translateY(0); } @apply focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background;
} }
@keyframes slideInLeft { /* Shimmer loading */
from { transform: translateX(-100%); } .shimmer {
to { transform: translateX(0); } @apply relative overflow-hidden;
} background: linear-gradient(
90deg,
transparent 0%,
rgba(255, 255, 255, 0.04) 50%,
transparent 100%
);
background-size: 200% 100%;
@apply animate-shimmer;
}
@keyframes slideOutLeft { /* Hide scrollbar but keep scroll */
from { transform: translateX(0); } .no-scrollbar::-webkit-scrollbar {
to { transform: translateX(-100%); } display: none;
} }
.no-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
@keyframes fadeOverlayIn { /* Dot grid background */
from { opacity: 0; } .bg-dot-grid {
to { opacity: 1; } background-image: radial-gradient(
} circle,
rgba(255, 255, 255, 0.05) 1px,
transparent 1px
);
background-size: 24px 24px;
}
@keyframes fadeOverlayOut { /* Line grid background */
from { opacity: 1; } .bg-line-grid {
to { opacity: 0; } background-image:
linear-gradient(rgba(255, 255, 255, 0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(255, 255, 255, 0.03) 1px, transparent 1px);
background-size: 40px 40px;
}
} }
.animate-fade-in { animation: fadeIn 0.2s ease-out both; }
.animate-slide-in { animation: slideInLeft 0.25s cubic-bezier(0.16, 1, 0.3, 1) both; }
.animate-slide-out { animation: slideOutLeft 0.2s ease-in both; }
.animate-overlay-in { animation: fadeOverlayIn 0.2s ease-out both; }
.animate-overlay-out { animation: fadeOverlayOut 0.15s ease-in both; }
/* ═══════════════════════════════════════════════════ /* ═══════════════════════════════════════════════════
MOBILE INPUT FIXES TYPOGRAPHY — Prose overrides for chat
═══════════════════════════════════════════════════ */ ═══════════════════════════════════════════════════ */
textarea, input, select { @layer components {
font-size: 16px !important; /* Prevents iOS zoom on focus */ .prose-anton {
} @apply prose prose-anton prose-sm max-w-none;
@media (min-width: 640px) {
textarea, input, select {
font-size: 14px !important;
} }
}
textarea { .prose-anton pre {
-webkit-appearance: none; @apply bg-[hsl(230,16%,10%)] border border-[hsl(230,14%,16%)] rounded-xl my-4;
appearance: none; }
}
select {
-webkit-appearance: none;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='%23666' viewBox='0 0 16 16'%3E%3Cpath d='M8 11L3 6h10z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 10px center;
padding-right: 28px;
}
/* ═══════════════════════════════════════════════════ .prose-anton table {
TOUCH-FRIENDLY RANGE SLIDER @apply w-full text-xs border-collapse;
═══════════════════════════════════════════════════ */ }
input[type="range"] { .prose-anton th {
-webkit-appearance: none; @apply bg-white/5 px-3 py-2 text-left font-semibold text-white border-b border-[hsl(230,14%,16%)];
appearance: none; }
width: 100%;
height: 6px;
border-radius: 3px;
background: rgba(255,255,255,0.08);
outline: none;
cursor: pointer;
}
input[type="range"]::-webkit-slider-thumb { .prose-anton td {
-webkit-appearance: none; @apply px-3 py-2 border-b border-[hsl(230,14%,16%)];
appearance: none; }
width: 22px;
height: 22px;
border-radius: 50%;
background: #e53e3e;
border: 2px solid #1a1a2e;
cursor: pointer;
box-shadow: 0 0 8px rgba(229,62,62,0.3);
}
input[type="range"]::-moz-range-thumb { .prose-anton tr:hover td {
width: 22px; @apply bg-white/[0.02];
height: 22px; }
border-radius: 50%;
background: #e53e3e;
border: 2px solid #1a1a2e;
cursor: pointer;
} }
/* ═══════════════════════════════════════════════════ /* ═══════════════════════════════════════════════════
MARKDOWN PROSE RADIX UI OVERRIDES — Dark theme compatible
═══════════════════════════════════════════════════ */ ═══════════════════════════════════════════════════ */
.prose-anton { @layer components {
color: #e2e2ea; /* Dialog */
line-height: 1.65; [data-radix-dialog-overlay] {
word-break: break-word; @apply fixed inset-0 z-50 bg-black/80 backdrop-blur-sm;
overflow-wrap: anywhere; animation: fade-in 0.15s ease-out;
} }
.prose-anton h1, .prose-anton h2, .prose-anton h3,
.prose-anton h4, .prose-anton h5, .prose-anton h6 {
color: #fff;
font-weight: 600;
margin-top: 1.2em;
margin-bottom: 0.5em;
}
.prose-anton h1 { font-size: 1.4em; }
.prose-anton h2 { font-size: 1.2em; }
.prose-anton h3 { font-size: 1.05em; }
.prose-anton p { margin-bottom: 0.75em; }
.prose-anton ul, .prose-anton ol { [data-radix-dialog-content] {
padding-left: 1.4em; @apply fixed left-1/2 top-1/2 z-50 -translate-x-1/2 -translate-y-1/2;
margin-bottom: 0.75em; @apply bg-[hsl(var(--popover))] border border-[hsl(var(--border))];
} @apply rounded-xl shadow-elevated p-6;
animation: scale-in 0.2s ease-out;
}
.prose-anton li { margin-bottom: 0.25em; } /* Dropdown */
.prose-anton li::marker { color: #555; } [data-radix-dropdown-menu-content] {
@apply bg-[hsl(var(--popover))] border border-[hsl(var(--border))];
.prose-anton code:not(pre code) { @apply rounded-lg shadow-elevated p-1 min-w-[8rem];
background: rgba(255,255,255,0.06); animation: scale-in 0.15s ease-out;
border: 1px solid rgba(255,255,255,0.08); }
border-radius: 4px;
padding: 0.15em 0.35em;
font-family: 'JetBrains Mono', monospace;
font-size: 0.85em;
color: #ff6b6b;
word-break: break-all;
}
.prose-anton a { [data-radix-dropdown-menu-item] {
color: #e53e3e; @apply relative flex cursor-pointer select-none items-center rounded-md px-2 py-1.5 text-sm;
text-decoration: underline; @apply text-[hsl(var(--foreground))] outline-none;
text-underline-offset: 2px; @apply transition-colors hover:bg-white/5 focus:bg-white/5;
} }
.prose-anton blockquote { /* Tooltip */
border-left: 3px solid #e53e3e; [data-radix-tooltip-content] {
padding: 0.5em 1em; @apply bg-[hsl(var(--popover))] border border-[hsl(var(--border))];
margin: 0.75em 0; @apply rounded-md px-3 py-1.5 text-xs shadow-lg;
background: rgba(229,62,62,0.04); animation: fade-in 0.1s ease-out;
border-radius: 0 6px 6px 0; }
color: #aaa;
}
.prose-anton hr { /* Select */
border: none; [data-radix-select-content] {
border-top: 1px solid rgba(255,255,255,0.08); @apply bg-[hsl(var(--popover))] border border-[hsl(var(--border))];
margin: 1.5em 0; @apply rounded-lg shadow-elevated overflow-hidden;
} animation: scale-in 0.15s ease-out;
}
.prose-anton table { [data-radix-select-item] {
width: 100%; @apply relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm;
border-collapse: collapse; @apply outline-none transition-colors hover:bg-white/5 focus:bg-white/5;
font-size: 0.85em; }
margin: 0.75em 0;
display: block;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
} }
.prose-anton th, .prose-anton td { /* ═══════════════════════════════════════════════════
border: 1px solid rgba(255,255,255,0.08); RANGE INPUT — Styled for dark theme
padding: 0.4em 0.7em; ═══════════════════════════════════════════════════ */
text-align: left;
white-space: nowrap;
}
.prose-anton th { @layer base {
background: rgba(255,255,255,0.04); input[type="range"] {
font-weight: 600; @apply w-full h-1.5 bg-white/10 rounded-full appearance-none cursor-pointer;
}
input[type="range"]::-webkit-slider-thumb {
@apply w-4 h-4 bg-[hsl(var(--primary))] rounded-full appearance-none cursor-pointer;
@apply shadow-md hover:scale-110 transition-transform;
}
input[type="range"]::-moz-range-thumb {
@apply w-4 h-4 bg-[hsl(var(--primary))] rounded-full border-0 cursor-pointer;
@apply shadow-md hover:scale-110 transition-transform;
}
input[type="range"]:focus {
@apply outline-none;
}
input[type="range"]:focus::-webkit-slider-thumb {
@apply ring-2 ring-[hsl(var(--ring))] ring-offset-2 ring-offset-[hsl(var(--background))];
}
} }
.prose-anton strong { color: #fff; font-weight: 600; }
.prose-anton em { font-style: italic; }
/* ═══════════════════════════════════════════════════ /* ═══════════════════════════════════════════════════
THINKING PULSE SELECTION
═══════════════════════════════════════════════════ */ ═══════════════════════════════════════════════════ */
@keyframes thinkPulse { ::selection {
0%, 100% { opacity: 1; } @apply bg-red-500/30 text-white;
50% { opacity: 0.5; }
} }
.thinking-pulse { animation: thinkPulse 1.5s ease-in-out infinite; }
/* ═══════════════════════════════════════════════════ /* ═══════════════════════════════════════════════════
MOBILE-SPECIFIC OVERRIDES THINKING PULSE (for AI reasoning indicator)
═══════════════════════════════════════════════════ */ ═══════════════════════════════════════════════════ */
@media (max-width: 639px) { @keyframes thinking-pulse-kf {
.prose-anton { font-size: 0.9rem; line-height: 1.6; } 0%, 100% { opacity: 1; }
.prose-anton h1 { font-size: 1.25em; } 50% { opacity: 0.5; }
.prose-anton h2 { font-size: 1.15em; }
} }
.thinking-pulse {
/* Prevent body scroll when modal/drawer is open */ animation: thinking-pulse-kf 1.5s ease-in-out infinite;
body.drawer-open {
touch-action: none;
} }
\ No newline at end of file
import { clsx } from "clsx";
import { twMerge } from "tailwind-merge";
/**
* Merge Tailwind classes intelligently — the foundation of
* every good component library. Handles conflicts like a boss.
*
* Usage: cn("px-4 py-2", isActive && "bg-primary", className)
*/
export function cn(...inputs) {
return twMerge(clsx(inputs));
}
/**
* Format a number with locale-aware separators
*/
export function formatNumber(n) {
if (n == null) return "0";
return Number(n).toLocaleString();
}
/**
* Truncate text with ellipsis
*/
export function truncate(str, maxLength = 50) {
if (!str || str.length <= maxLength) return str || "";
return str.slice(0, maxLength) + "…";
}
/**
* Generate initials from a name
*/
export function getInitials(name, maxChars = 2) {
if (!name) return "?";
return name
.split(/[\s_-]+/)
.map((w) => w[0])
.filter(Boolean)
.slice(0, maxChars)
.join("")
.toUpperCase();
}
\ No newline at end of file
/** @type {import('tailwindcss').Config} */ /** @type {import('tailwindcss').Config} */
export default { export default {
darkMode: "class",
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
theme: { theme: {
container: {
center: true,
padding: "2rem",
screens: { "2xl": "1400px" },
},
extend: { extend: {
colors: { colors: {
"anton-bg": "#09090f", // ── Core brand ──
"anton-surface": "#0f0f1a", anton: {
"anton-card": "#16162a", bg: "hsl(var(--anton-bg) / <alpha-value>)",
"anton-border": "#1e1e3a", surface: "hsl(var(--anton-surface) / <alpha-value>)",
"anton-text": "#e2e2ea", card: "hsl(var(--anton-card) / <alpha-value>)",
"anton-muted": "#6b6b8a", border: "hsl(var(--anton-border) / <alpha-value>)",
"anton-accent": "#e53e3e", text: "hsl(var(--anton-text) / <alpha-value>)",
"anton-success": "#48bb78", muted: "hsl(var(--anton-muted) / <alpha-value>)",
"anton-danger": "#e53e3e", accent: "hsl(var(--anton-accent) / <alpha-value>)",
success: "hsl(var(--anton-success) / <alpha-value>)",
danger: "hsl(var(--anton-danger) / <alpha-value>)",
warning: "hsl(var(--anton-warning) / <alpha-value>)",
info: "hsl(var(--anton-info) / <alpha-value>)",
},
// ── shadcn-compatible semantic tokens ──
border: "hsl(var(--border) / <alpha-value>)",
input: "hsl(var(--input) / <alpha-value>)",
ring: "hsl(var(--ring) / <alpha-value>)",
background: "hsl(var(--background) / <alpha-value>)",
foreground: "hsl(var(--foreground) / <alpha-value>)",
primary: {
DEFAULT: "hsl(var(--primary) / <alpha-value>)",
foreground: "hsl(var(--primary-foreground) / <alpha-value>)",
},
secondary: {
DEFAULT: "hsl(var(--secondary) / <alpha-value>)",
foreground: "hsl(var(--secondary-foreground) / <alpha-value>)",
},
destructive: {
DEFAULT: "hsl(var(--destructive) / <alpha-value>)",
foreground: "hsl(var(--destructive-foreground) / <alpha-value>)",
},
muted: {
DEFAULT: "hsl(var(--muted) / <alpha-value>)",
foreground: "hsl(var(--muted-foreground) / <alpha-value>)",
},
accent: {
DEFAULT: "hsl(var(--accent) / <alpha-value>)",
foreground: "hsl(var(--accent-foreground) / <alpha-value>)",
},
popover: {
DEFAULT: "hsl(var(--popover) / <alpha-value>)",
foreground: "hsl(var(--popover-foreground) / <alpha-value>)",
},
card: {
DEFAULT: "hsl(var(--card) / <alpha-value>)",
foreground: "hsl(var(--card-foreground) / <alpha-value>)",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
}, },
fontFamily: { fontFamily: {
sans: ["Inter", "system-ui", "-apple-system", "sans-serif"], sans: ["Inter", "system-ui", "-apple-system", "sans-serif"],
mono: ["JetBrains Mono", "Fira Code", "monospace"], mono: ["JetBrains Mono", "Fira Code", "monospace"],
display: ["Inter", "system-ui", "sans-serif"],
},
fontSize: {
"2xs": ["0.625rem", { lineHeight: "0.875rem" }],
},
spacing: {
"4.5": "1.125rem",
"5.5": "1.375rem",
"13": "3.25rem",
"15": "3.75rem",
"17": "4.25rem",
"18": "4.5rem",
"112": "28rem",
"128": "32rem",
"144": "36rem",
},
backdropBlur: {
xs: "2px",
},
boxShadow: {
glow: "0 0 20px rgba(229, 62, 62, 0.15)",
"glow-lg": "0 0 40px rgba(229, 62, 62, 0.2)",
"inner-glow": "inset 0 0 20px rgba(229, 62, 62, 0.05)",
"card-hover": "0 8px 30px rgba(0, 0, 0, 0.3), 0 0 1px rgba(255, 255, 255, 0.05)",
"elevated": "0 20px 60px rgba(0, 0, 0, 0.4)",
},
keyframes: {
"fade-in": {
from: { opacity: "0", transform: "translateY(8px)" },
to: { opacity: "1", transform: "translateY(0)" },
},
"fade-out": {
from: { opacity: "1", transform: "translateY(0)" },
to: { opacity: "0", transform: "translateY(8px)" },
},
"slide-in-from-right": {
from: { transform: "translateX(100%)" },
to: { transform: "translateX(0)" },
},
"slide-in-from-left": {
from: { transform: "translateX(-100%)" },
to: { transform: "translateX(0)" },
},
"slide-in-from-top": {
from: { transform: "translateY(-100%)" },
to: { transform: "translateY(0)" },
},
"slide-in-from-bottom": {
from: { transform: "translateY(100%)" },
to: { transform: "translateY(0)" },
},
"scale-in": {
from: { opacity: "0", transform: "scale(0.95)" },
to: { opacity: "1", transform: "scale(1)" },
},
"spin-slow": {
from: { transform: "rotate(0deg)" },
to: { transform: "rotate(360deg)" },
},
shimmer: {
"0%": { backgroundPosition: "-200% 0" },
"100%": { backgroundPosition: "200% 0" },
},
"pulse-glow": {
"0%, 100%": { boxShadow: "0 0 5px rgba(229, 62, 62, 0.2)" },
"50%": { boxShadow: "0 0 20px rgba(229, 62, 62, 0.4)" },
},
"accordion-down": {
from: { height: "0" },
to: { height: "var(--radix-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: "0" },
},
float: {
"0%, 100%": { transform: "translateY(0px)" },
"50%": { transform: "translateY(-6px)" },
},
"border-beam": {
"100%": { offsetDistance: "100%" },
},
},
animation: {
"fade-in": "fade-in 0.3s ease-out",
"fade-out": "fade-out 0.3s ease-out",
"slide-in-right": "slide-in-from-right 0.3s ease-out",
"slide-in-left": "slide-in-from-left 0.3s ease-out",
"slide-in-top": "slide-in-from-top 0.3s ease-out",
"slide-in-bottom": "slide-in-from-bottom 0.3s ease-out",
"scale-in": "scale-in 0.2s ease-out",
"spin-slow": "spin-slow 3s linear infinite",
shimmer: "shimmer 2s linear infinite",
"pulse-glow": "pulse-glow 2s ease-in-out infinite",
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
float: "float 3s ease-in-out infinite",
"border-beam": "border-beam calc(var(--duration)*1s) infinite linear",
}, },
screens: { typography: {
"xs": "400px", anton: {
css: {
"--tw-prose-body": "hsl(var(--anton-text))",
"--tw-prose-headings": "#ffffff",
"--tw-prose-links": "hsl(var(--anton-accent))",
"--tw-prose-bold": "#ffffff",
"--tw-prose-code": "hsl(var(--anton-accent))",
"--tw-prose-pre-bg": "hsl(var(--anton-card))",
"--tw-prose-pre-code": "hsl(var(--anton-text))",
"--tw-prose-quotes": "hsl(var(--anton-muted))",
"--tw-prose-quote-borders": "hsl(var(--anton-accent))",
"--tw-prose-hr": "hsl(var(--anton-border))",
"--tw-prose-th-borders": "hsl(var(--anton-border))",
"--tw-prose-td-borders": "hsl(var(--anton-border))",
maxWidth: "none",
a: { textDecoration: "none", fontWeight: "500" },
"a:hover": { color: "hsl(var(--anton-accent))" },
code: {
backgroundColor: "rgba(255,255,255,0.06)",
padding: "0.15em 0.35em",
borderRadius: "0.25em",
fontWeight: "400",
},
"code::before": { content: '""' },
"code::after": { content: '""' },
pre: {
backgroundColor: "hsl(var(--anton-card))",
border: "1px solid hsl(var(--anton-border))",
borderRadius: "0.75rem",
},
table: { fontSize: "0.875rem" },
th: { fontWeight: "600", color: "#ffffff" },
img: { borderRadius: "0.75rem" },
hr: { borderColor: "hsl(var(--anton-border))" },
blockquote: {
borderLeftColor: "hsl(var(--anton-accent))",
fontStyle: "normal",
},
},
},
}, },
}, },
}, },
plugins: [], plugins: [
require("@tailwindcss/typography"),
require("@tailwindcss/forms")({ strategy: "class" }),
require("@tailwindcss/container-queries"),
require("tailwindcss-animate"),
],
}; };
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment