feat: Premium design overhaul — typography, atmosphere, animations, component polish
- Add Plus Jakarta Sans as display font for headings - Add subtle noise texture overlay + indigo radial gradient for depth - New keyframe animations: glow-pulse, fade-in-up, scale-in, slide-in-right - Card: interactive hover-lift + selected ring variants - Button: scale micro-interactions, destructive glow, transition-all - Header: logo upgrade with wordmark, animated nav indicator bar, glass search button, gradient shadow depth - StatusDot: glow halos per status variant (active/success/error/warning/urgent) - HealthDot: glow effects for connected/disconnected/reconnecting states - Card hover-lift and status glow CSS utilities
This commit is contained in:
@@ -4,6 +4,9 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Codewalk District</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@500;600;700;800&display=swap" rel="stylesheet">
|
||||
<script>
|
||||
(function() {
|
||||
var t = localStorage.getItem('cw-theme') || 'system';
|
||||
|
||||
@@ -1,32 +1,35 @@
|
||||
import { trpc } from "@/lib/trpc";
|
||||
import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";
|
||||
import { cn } from "@/lib/utils";
|
||||
import type { ConnectionState } from "@/hooks/useConnectionStatus";
|
||||
|
||||
export function HealthDot() {
|
||||
const health = trpc.healthCheck.useQuery(undefined, {
|
||||
refetchInterval: 30000,
|
||||
retry: 1,
|
||||
});
|
||||
interface HealthDotProps {
|
||||
connectionState: ConnectionState;
|
||||
}
|
||||
|
||||
const isHealthy = health.data && !health.isError;
|
||||
const isLoading = health.isLoading;
|
||||
const healthGlow: Record<ConnectionState, string> = {
|
||||
connected: "0 0 6px 1px hsl(var(--status-success-dot) / 0.4)",
|
||||
disconnected: "0 0 6px 1px hsl(var(--status-error-dot) / 0.5)",
|
||||
reconnecting: "0 0 6px 1px hsl(var(--status-neutral-dot) / 0.3)",
|
||||
};
|
||||
|
||||
export function HealthDot({ connectionState }: HealthDotProps) {
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span
|
||||
className={cn(
|
||||
"inline-block h-2 w-2 rounded-full",
|
||||
isLoading && "bg-status-neutral-dot animate-status-pulse",
|
||||
isHealthy && "bg-status-success-dot",
|
||||
!isHealthy && !isLoading && "bg-status-error-dot",
|
||||
connectionState === "reconnecting" && "bg-status-neutral-dot animate-status-pulse",
|
||||
connectionState === "connected" && "bg-status-success-dot",
|
||||
connectionState === "disconnected" && "bg-status-error-dot",
|
||||
)}
|
||||
style={{ boxShadow: healthGlow[connectionState] }}
|
||||
/>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
{isLoading
|
||||
? "Checking server..."
|
||||
: isHealthy
|
||||
{connectionState === "reconnecting"
|
||||
? "Reconnecting..."
|
||||
: connectionState === "connected"
|
||||
? "Server connected"
|
||||
: "Server disconnected"}
|
||||
</TooltipContent>
|
||||
|
||||
@@ -11,6 +11,15 @@ const dotColors: Record<StatusVariant, string> = {
|
||||
urgent: "bg-status-urgent-dot",
|
||||
};
|
||||
|
||||
const glowMap: Record<StatusVariant, string | undefined> = {
|
||||
active: "0 0 6px 1px hsl(var(--status-active-dot) / 0.5)",
|
||||
success: "0 0 6px 1px hsl(var(--status-success-dot) / 0.4)",
|
||||
warning: "0 0 6px 1px hsl(var(--status-warning-dot) / 0.4)",
|
||||
error: "0 0 6px 1px hsl(var(--status-error-dot) / 0.5)",
|
||||
neutral: undefined,
|
||||
urgent: "0 0 6px 1px hsl(var(--status-urgent-dot) / 0.4)",
|
||||
};
|
||||
|
||||
/** Maps raw entity status strings to semantic StatusVariant tokens. */
|
||||
export function mapEntityStatus(rawStatus: string): StatusVariant {
|
||||
switch (rawStatus) {
|
||||
@@ -84,6 +93,7 @@ export function StatusDot({
|
||||
|
||||
const variant = variantOverride ?? mapEntityStatus(status);
|
||||
const color = dotColors[variant];
|
||||
const glow = glowMap[variant];
|
||||
const displayLabel = label ?? status.replace(/_/g, " ").toLowerCase();
|
||||
|
||||
return (
|
||||
@@ -94,9 +104,10 @@ export function StatusDot({
|
||||
"inline-block shrink-0 rounded-full",
|
||||
sizeClasses[size],
|
||||
color,
|
||||
pulse && "animate-status-pulse",
|
||||
pulse && "animate-status-pulse transition-transform",
|
||||
className,
|
||||
)}
|
||||
style={glow ? { boxShadow: glow } : undefined}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,18 +5,18 @@ import { cva, type VariantProps } from "class-variance-authority"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-all duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
||||
default: "bg-primary text-primary-foreground hover:bg-primary/90 hover:shadow-md active:scale-[0.98]",
|
||||
destructive:
|
||||
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
||||
"bg-destructive text-destructive-foreground hover:bg-destructive/90 hover:shadow-[0_0_12px_hsl(var(--destructive)/0.3)]",
|
||||
outline:
|
||||
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground active:scale-[0.98]",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
|
||||
@@ -2,19 +2,25 @@ import * as React from "react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Card = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"rounded-lg border bg-card text-card-foreground shadow-sm",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
interactive?: boolean;
|
||||
selected?: boolean;
|
||||
}
|
||||
|
||||
const Card = React.forwardRef<HTMLDivElement, CardProps>(
|
||||
({ className, interactive, selected, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"rounded-lg border bg-card text-card-foreground shadow-sm",
|
||||
interactive && "transition-all duration-200 ease-out hover:shadow-md hover:-translate-y-0.5 hover:border-border/80 cursor-pointer",
|
||||
selected && "ring-1 ring-primary/30 border-primary/20",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
)
|
||||
Card.displayName = "Card"
|
||||
|
||||
const CardHeader = React.forwardRef<
|
||||
|
||||
@@ -150,6 +150,13 @@
|
||||
--ease-out: cubic-bezier(0, 0, 0.2, 1);
|
||||
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
|
||||
/* Spacing tokens */
|
||||
--space-section: 2rem;
|
||||
--space-section-lg: 3rem;
|
||||
|
||||
/* Background atmosphere */
|
||||
--bg-gradient: radial-gradient(ellipse 80% 60% at 50% -10%, hsl(239 84% 67% / 0.04), transparent 70%);
|
||||
|
||||
/* Z-index scale */
|
||||
--z-base: 0;
|
||||
--z-raised: 1;
|
||||
@@ -259,6 +266,9 @@
|
||||
--diff-remove-border: 0 84% 25%;
|
||||
--diff-hunk-bg: 239 40% 16%;
|
||||
|
||||
/* Background atmosphere — dark mode */
|
||||
--bg-gradient: radial-gradient(ellipse 80% 60% at 50% -10%, hsl(239 84% 67% / 0.06), transparent 70%);
|
||||
|
||||
/* Shadow tokens — dark mode (inset highlights + ambient glow) */
|
||||
--shadow-xs: none;
|
||||
--shadow-sm: inset 0 1px 0 hsl(0 0% 100% / 0.04);
|
||||
@@ -320,9 +330,28 @@ select:focus-visible {
|
||||
min-width: 320px;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
background-image: var(--bg-gradient);
|
||||
background-attachment: fixed;
|
||||
}
|
||||
}
|
||||
|
||||
/* Noise texture overlay */
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: -1;
|
||||
opacity: 0.015;
|
||||
pointer-events: none;
|
||||
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)'/%3E%3C/svg%3E");
|
||||
background-repeat: repeat;
|
||||
background-size: 256px 256px;
|
||||
}
|
||||
|
||||
.dark body::before {
|
||||
opacity: 0.03;
|
||||
}
|
||||
|
||||
/* Notion-style page link blocks inside the editor */
|
||||
.page-link-block {
|
||||
display: flex;
|
||||
@@ -426,3 +455,27 @@ select:focus-visible {
|
||||
.ProseMirror pre code::after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
.card-hover-lift {
|
||||
transition: transform var(--duration-normal) var(--ease-out),
|
||||
box-shadow var(--duration-normal) var(--ease-out);
|
||||
}
|
||||
.card-hover-lift:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.glow-active {
|
||||
box-shadow: 0 0 8px hsl(var(--status-active-dot) / 0.4);
|
||||
}
|
||||
.glow-success {
|
||||
box-shadow: 0 0 8px hsl(var(--status-success-dot) / 0.4);
|
||||
}
|
||||
.glow-error {
|
||||
box-shadow: 0 0 8px hsl(var(--status-error-dot) / 0.4);
|
||||
}
|
||||
.glow-warning {
|
||||
box-shadow: 0 0 8px hsl(var(--status-warning-dot) / 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { ThemeToggle } from '@/components/ThemeToggle'
|
||||
import { HealthDot } from '@/components/HealthDot'
|
||||
import { NavBadge } from '@/components/NavBadge'
|
||||
import { trpc } from '@/lib/trpc'
|
||||
import type { ConnectionState } from '@/hooks/useConnectionStatus'
|
||||
|
||||
const navItems = [
|
||||
{ label: 'Initiatives', to: '/initiatives', badgeKey: null },
|
||||
@@ -15,9 +16,10 @@ const navItems = [
|
||||
interface AppLayoutProps {
|
||||
children: React.ReactNode
|
||||
onOpenCommandPalette?: () => void
|
||||
connectionState: ConnectionState
|
||||
}
|
||||
|
||||
export function AppLayout({ children, onOpenCommandPalette }: AppLayoutProps) {
|
||||
export function AppLayout({ children, onOpenCommandPalette, connectionState }: AppLayoutProps) {
|
||||
const agents = trpc.listAgents.useQuery(undefined, {
|
||||
refetchInterval: 10000,
|
||||
})
|
||||
@@ -30,29 +32,39 @@ export function AppLayout({ children, onOpenCommandPalette }: AppLayoutProps) {
|
||||
return (
|
||||
<div className="flex h-screen flex-col bg-background">
|
||||
{/* Single-row 48px header */}
|
||||
<header className="z-sticky shrink-0 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
||||
<div className="flex h-12 items-center justify-between px-4">
|
||||
<header className="relative z-sticky shrink-0 border-b border-border/50 bg-background/95 shadow-[0_1px_0_0_hsl(var(--border)/0.5),0_1px_3px_-1px_hsl(var(--primary)/0.1)] backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
||||
<div className="flex h-12 items-center justify-between px-5">
|
||||
{/* Left: Logo + Nav */}
|
||||
<div className="flex items-center gap-6">
|
||||
<Link to="/initiatives" className="flex items-center gap-2 text-sm font-bold tracking-tight">
|
||||
<span className="inline-flex h-6 w-6 items-center justify-center rounded bg-primary text-[10px] font-bold text-primary-foreground">
|
||||
<Link to="/initiatives" className="flex items-center gap-2.5">
|
||||
<span className="inline-flex h-7 w-7 items-center justify-center rounded bg-primary text-[11px] font-bold text-primary-foreground shadow-sm ring-1 ring-primary/20">
|
||||
CW
|
||||
</span>
|
||||
<span className="hidden sm:inline">Codewalk District</span>
|
||||
<span className="hidden items-baseline gap-1 sm:inline-flex">
|
||||
<span className="font-display font-bold tracking-tight">Codewalk</span>
|
||||
<span className="font-display font-medium tracking-tight text-muted-foreground">District</span>
|
||||
</span>
|
||||
</Link>
|
||||
<nav className="flex items-center gap-0.5">
|
||||
<nav className="flex items-center gap-1">
|
||||
{navItems.map((item) => (
|
||||
<Link
|
||||
key={item.label}
|
||||
to={item.to}
|
||||
className="flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium text-muted-foreground transition-colors duration-fast hover:text-foreground"
|
||||
className="relative flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium text-muted-foreground transition-colors duration-fast hover:text-foreground"
|
||||
activeProps={{
|
||||
className: 'flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium text-foreground bg-accent',
|
||||
className: 'relative flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-semibold text-foreground',
|
||||
}}
|
||||
>
|
||||
{item.label}
|
||||
{item.badgeKey && (
|
||||
<NavBadge count={badgeCounts[item.badgeKey]} />
|
||||
{({ isActive }) => (
|
||||
<>
|
||||
{item.label}
|
||||
{item.badgeKey && (
|
||||
<NavBadge count={badgeCounts[item.badgeKey]} />
|
||||
)}
|
||||
{isActive && (
|
||||
<span className="absolute -bottom-[13px] left-1/2 h-0.5 w-4 -translate-x-1/2 rounded-full bg-primary" />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Link>
|
||||
))}
|
||||
@@ -60,11 +72,11 @@ export function AppLayout({ children, onOpenCommandPalette }: AppLayoutProps) {
|
||||
</div>
|
||||
|
||||
{/* Right: Cmd+K, Theme toggle, Health, Workspace */}
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
{onOpenCommandPalette && (
|
||||
<button
|
||||
onClick={onOpenCommandPalette}
|
||||
className="flex items-center gap-1.5 rounded-md border bg-muted/50 px-2.5 py-1 text-xs text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
|
||||
className="flex items-center gap-1.5 rounded-md border border-border/60 bg-background/80 px-2.5 py-1 text-xs text-muted-foreground backdrop-blur-sm transition-all duration-fast hover:border-border hover:bg-accent/80 hover:text-foreground"
|
||||
>
|
||||
<Search className="h-3 w-3" />
|
||||
<span className="hidden sm:inline">Search</span>
|
||||
@@ -73,8 +85,9 @@ export function AppLayout({ children, onOpenCommandPalette }: AppLayoutProps) {
|
||||
</kbd>
|
||||
</button>
|
||||
)}
|
||||
<div className="mx-1 h-4 w-px bg-border/50" />
|
||||
<ThemeToggle />
|
||||
<HealthDot />
|
||||
<HealthDot connectionState={connectionState} />
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -18,6 +18,7 @@ export default {
|
||||
fontFamily: {
|
||||
sans: ["Geist Sans", ...defaultTheme.fontFamily.sans],
|
||||
mono: ["Geist Mono", ...defaultTheme.fontFamily.mono],
|
||||
display: ['"Plus Jakarta Sans"', "var(--font-geist-sans)", "system-ui", "sans-serif"],
|
||||
},
|
||||
colors: {
|
||||
border: "hsl(var(--border))",
|
||||
@@ -168,12 +169,37 @@ export default {
|
||||
"0%": { backgroundPosition: "-200% 0" },
|
||||
"100%": { backgroundPosition: "200% 0" },
|
||||
},
|
||||
"glow-pulse": {
|
||||
"0%, 100%": { boxShadow: "0 0 4px var(--glow-color, hsl(239 84% 67% / 0.3))" },
|
||||
"50%": { boxShadow: "0 0 12px var(--glow-color, hsl(239 84% 67% / 0.5))" },
|
||||
},
|
||||
"fade-in-up": {
|
||||
"0%": { opacity: "0", transform: "translateY(8px)" },
|
||||
"100%": { opacity: "1", transform: "translateY(0)" },
|
||||
},
|
||||
"fade-in": {
|
||||
"0%": { opacity: "0" },
|
||||
"100%": { opacity: "1" },
|
||||
},
|
||||
"scale-in": {
|
||||
"0%": { opacity: "0", transform: "scale(0.95)" },
|
||||
"100%": { opacity: "1", transform: "scale(1)" },
|
||||
},
|
||||
"slide-in-right": {
|
||||
"0%": { opacity: "0", transform: "translateX(16px)" },
|
||||
"100%": { opacity: "1", transform: "translateX(0)" },
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
"accordion-down": "accordion-down 0.2s ease-out",
|
||||
"accordion-up": "accordion-up 0.2s ease-out",
|
||||
"status-pulse": "status-pulse 2s ease-in-out infinite",
|
||||
shimmer: "shimmer 1.5s infinite",
|
||||
"glow-pulse": "glow-pulse 2s ease-in-out infinite",
|
||||
"fade-in-up": "fade-in-up 0.4s var(--ease-out) forwards",
|
||||
"fade-in": "fade-in 0.3s var(--ease-out) forwards",
|
||||
"scale-in": "scale-in 0.3s var(--ease-out) forwards",
|
||||
"slide-in-right": "slide-in-right 0.3s var(--ease-out) forwards",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
71
package-lock.json
generated
71
package-lock.json
generated
@@ -25,6 +25,7 @@
|
||||
"execa": "^9.5.2",
|
||||
"gray-matter": "^4.0.3",
|
||||
"js-yaml": "^4.1.1",
|
||||
"motion": "^12.34.5",
|
||||
"nanoid": "^5.1.6",
|
||||
"pino": "^10.3.0",
|
||||
"simple-git": "^3.30.0",
|
||||
@@ -32,7 +33,7 @@
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"bin": {
|
||||
"cw": "dist/bin/cw.js"
|
||||
"cw": "apps/server/dist/bin/cw.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/better-sqlite3": "^7.6.13",
|
||||
@@ -6699,6 +6700,33 @@
|
||||
"url": "https://github.com/sponsors/rawify"
|
||||
}
|
||||
},
|
||||
"node_modules/framer-motion": {
|
||||
"version": "12.34.5",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.34.5.tgz",
|
||||
"integrity": "sha512-Z2dQ+o7BsfpJI3+u0SQUNCrN+ajCKJen1blC4rCHx1Ta2EOHs+xKJegLT2aaD9iSMbU3OoX+WabQXkloUbZmJQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-dom": "^12.34.5",
|
||||
"motion-utils": "^12.29.2",
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/is-prop-valid": "*",
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/is-prop-valid": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/fs-constants": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||
@@ -7409,6 +7437,47 @@
|
||||
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/motion": {
|
||||
"version": "12.34.5",
|
||||
"resolved": "https://registry.npmjs.org/motion/-/motion-12.34.5.tgz",
|
||||
"integrity": "sha512-N06NLJ9IeBHeielRqIvYvjPfXuRdyTxa+9++BgpGa+hY2D7TcMkI6QzV3jaRuv0aZRXgMa7cPy9YcBUBisPzAQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"framer-motion": "^12.34.5",
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/is-prop-valid": "*",
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/is-prop-valid": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/motion-dom": {
|
||||
"version": "12.34.5",
|
||||
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.34.5.tgz",
|
||||
"integrity": "sha512-k33CsnxO2K3gBRMUZT+vPmc4Utlb5menKdG0RyVNLtlqRaaJPRWlE9fXl8NTtfZ5z3G8TDvqSu0MENLqSTaHZA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-utils": "^12.29.2"
|
||||
}
|
||||
},
|
||||
"node_modules/motion-utils": {
|
||||
"version": "12.29.2",
|
||||
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.29.2.tgz",
|
||||
"integrity": "sha512-G3kc34H2cX2gI63RqU+cZq+zWRRPSsNIOjpdl9TN4AQwC4sgwYPl/Q/Obf/d53nOm569T0fYK+tcoSV50BWx8A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
"execa": "^9.5.2",
|
||||
"gray-matter": "^4.0.3",
|
||||
"js-yaml": "^4.1.1",
|
||||
"motion": "^12.34.5",
|
||||
"nanoid": "^5.1.6",
|
||||
"pino": "^10.3.0",
|
||||
"simple-git": "^3.30.0",
|
||||
|
||||
Reference in New Issue
Block a user