feat(layout): implement core layout structure and navigation sidebars
This commit is contained in:
parent
fb862b6e1b
commit
9d175303f5
13 changed files with 436 additions and 36 deletions
|
|
@ -1,16 +1,57 @@
|
|||
/**
|
||||
* @file app/(app)/layout.tsx
|
||||
* @description Layout component providing a full-screen flex container with overflow clipping for application sub-routes.
|
||||
* @description Main application layout component that manages responsive sidebars and drawer overlays.
|
||||
*/
|
||||
|
||||
"use client";
|
||||
|
||||
import { useSidebarStore } from "@/lib/store/useSidebarStore";
|
||||
import { ServerSidebar } from "@/components/sidebar/ServerSidebar";
|
||||
import { ChannelSidebar } from "@/components/sidebar/ChannelSidebar";
|
||||
import { MemberSidebar } from "@/components/sidebar/MemberSidebar";
|
||||
import { UserProfile } from "@/components/sidebar/UserProfile";
|
||||
import { MobileDrawer } from "@/components/ui/MobileDrawer";
|
||||
|
||||
/**
|
||||
|
||||
* App group layout wrapper component.
|
||||
*
|
||||
* @param {Object} props - The component props.
|
||||
* @param {React.ReactNode} props.children - The child elements to be rendered within the layout container.
|
||||
* @returns {JSX.Element} The rendered layout container wrapping the child components.
|
||||
*/
|
||||
|
||||
* Client component serving as the primary application layout, handling the server, channel, and member sidebars with mobile drawer support.
|
||||
*
|
||||
* @param {Object} props - The component props.
|
||||
* @param {React.ReactNode} props.children - The child layout or page content to render in the central main view.
|
||||
* @returns {JSX.Element} The application layout structure with responsive sidebars.
|
||||
*/
|
||||
export default function AppLayout({ children }: { children: React.ReactNode }) {
|
||||
return <div className="flex h-screen w-full overflow-hidden">{children}</div>;
|
||||
const { isNavOpen, isMembersOpen, closeAll } = useSidebarStore();
|
||||
|
||||
return (
|
||||
<div className="flex h-screen w-full overflow-hidden bg-background text-foreground">
|
||||
{/* Left Navigation: Server + Channels + User Profile */}
|
||||
<MobileDrawer
|
||||
isOpen={isNavOpen}
|
||||
onClose={closeAll}
|
||||
side="left"
|
||||
breakpoint="md"
|
||||
>
|
||||
<div className="flex flex-col h-full shrink-0">
|
||||
<div className="flex flex-1 min-h-0">
|
||||
<ServerSidebar />
|
||||
<ChannelSidebar />
|
||||
</div>
|
||||
<UserProfile />
|
||||
</div>
|
||||
</MobileDrawer>
|
||||
|
||||
{/* Center: Main Area */}
|
||||
<div className="flex-1 flex min-w-0">{children}</div>
|
||||
|
||||
{/* Right: List of Members */}
|
||||
<MobileDrawer
|
||||
isOpen={isMembersOpen}
|
||||
onClose={closeAll}
|
||||
side="right"
|
||||
breakpoint="xl"
|
||||
>
|
||||
<MemberSidebar />
|
||||
</MobileDrawer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,33 +1,28 @@
|
|||
/**
|
||||
* @file app/(app)/page.tsx
|
||||
* @description Welcome page component displayed as the initial landing screen of the Waveform application.
|
||||
* @description Main dashboard page displaying the primary chat view with header, message area, and input control.
|
||||
*/
|
||||
|
||||
import Image from "next/image";
|
||||
import { ChatHeader } from "@/components/chat/ChatHeader";
|
||||
import { ChatInput } from "@/components/chat/ChatInput";
|
||||
|
||||
/**
|
||||
* Renders the welcome page featuring the Waveform logo, main heading, and confirmation text.
|
||||
* Renders the main application dashboard containing the general chat interface.
|
||||
*
|
||||
* @returns {JSX.Element} The rendered welcome page component.
|
||||
* @returns {Promise<JSX.Element>} The rendered application dashboard page.
|
||||
*/
|
||||
export default function WelcomePage() {
|
||||
export default async function ApplicationDashboardPage() {
|
||||
return (
|
||||
<main className="flex-1 flex flex-col items-center justify-center p-6 text-center">
|
||||
<div className="mb-6 relative w-24 h-24">
|
||||
<Image
|
||||
src="/logo.png"
|
||||
alt="Waveform Logo"
|
||||
fill
|
||||
className="object-contain"
|
||||
priority
|
||||
/>
|
||||
<main className="flex-1 flex flex-col h-full min-w-0 bg-background">
|
||||
<ChatHeader />
|
||||
|
||||
<div className="flex-1 overflow-y-auto p-4 space-y-4">
|
||||
<div className="flex items-center justify-center h-full text-neutral-500 text-sm">
|
||||
This is the beginning of the #general channel.
|
||||
</div>
|
||||
</div>
|
||||
<h1 className="text-4xl font-extrabold tracking-tight text-accent mb-2">
|
||||
Waveform
|
||||
</h1>
|
||||
<p className="max-w-md">
|
||||
The basic framework has been successfully set up!
|
||||
</p>
|
||||
|
||||
<ChatInput />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
@import "tailwindcss";
|
||||
|
||||
@theme {
|
||||
--color-background: hsl(200 6% 10%);
|
||||
--color-foreground: hsl(210 5% 86%);
|
||||
--color-background: hsl(220 13% 13%);
|
||||
--color-surface: hsl(223 13% 10%);
|
||||
|
||||
--color-accent: hsl(235 86% 65%);
|
||||
--color-accent-hover: hsl(235 86% 58%);
|
||||
|
||||
--color-foreground: hsl(210 11% 93%);
|
||||
--color-muted: hsl(215 8% 55%);
|
||||
|
||||
--font-sans:
|
||||
system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica,
|
||||
|
|
|
|||
45
components/chat/ChatHeader.tsx
Normal file
45
components/chat/ChatHeader.tsx
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* @file components/chat/ChatHeader.tsx
|
||||
* @description Client component rendering the top header of the chat interface with sidebar toggles and current channel details.
|
||||
*/
|
||||
|
||||
"use client";
|
||||
|
||||
import { Menu, Hash, Users } from "lucide-react";
|
||||
import { useSidebarStore } from "@/lib/store/useSidebarStore";
|
||||
|
||||
/**
|
||||
* Renders the top navigation header for the chat view, providing toggle triggers for mobile navigation and member list sidebars.
|
||||
*
|
||||
* @returns {JSX.Element} The rendered chat header component.
|
||||
*/
|
||||
export function ChatHeader() {
|
||||
const { toggleNav, toggleMembers } = useSidebarStore();
|
||||
|
||||
return (
|
||||
<header className="h-12 border-b border-neutral-800 flex items-center justify-between px-4 bg-background shrink-0">
|
||||
<div className="flex items-center gap-3 font-semibold">
|
||||
<button
|
||||
onClick={toggleNav}
|
||||
className="md:hidden text-muted hover:text-foreground focus:outline-none cursor-pointer"
|
||||
aria-label="Toggle Navigation"
|
||||
>
|
||||
<Menu className="w-5 h-5" />
|
||||
</button>
|
||||
|
||||
<div className="flex items-center gap-1.5">
|
||||
<Hash className="w-5 h-5 text-muted" />
|
||||
<span className="text-foreground">general</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={toggleMembers}
|
||||
className="xl:hidden text-muted hover:text-foreground focus:outline-none cursor-pointer"
|
||||
aria-label="Toggle Members"
|
||||
>
|
||||
<Users className="w-5 h-5" />
|
||||
</button>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
23
components/chat/ChatInput.tsx
Normal file
23
components/chat/ChatInput.tsx
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* @file components/chat/ChatInput.tsx
|
||||
* @description Client component providing an input field for composing and sending chat messages.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Renders the input container component for sending messages in a chat channel.
|
||||
*
|
||||
* @returns {JSX.Element} The rendered chat input UI element.
|
||||
*/
|
||||
export function ChatInput() {
|
||||
return (
|
||||
<div className="p-3 md:p-4 bg-background shrink-0">
|
||||
<div className="bg-surface border border-surface rounded-lg p-2.5 flex items-center focus-within:ring-1 focus-within:ring-accent transition-all">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Nachricht an #allgemein"
|
||||
className="w-full bg-transparent outline-none text-foreground placeholder-muted text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
39
components/sidebar/ChannelSidebar.tsx
Normal file
39
components/sidebar/ChannelSidebar.tsx
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* @file components/sidebar/ChannelSidebar.tsx
|
||||
* @description Sidebar component for server navigation, rendering channel lists.
|
||||
*/
|
||||
|
||||
import { Hash } from "lucide-react";
|
||||
|
||||
/**
|
||||
* Navigation sidebar displaying server details and text channels.
|
||||
*
|
||||
* @returns {JSX.Element} The rendered channel sidebar element.
|
||||
*/
|
||||
export function ChannelSidebar() {
|
||||
return (
|
||||
<aside className="w-60 bg-surface flex flex-col border-r border-background shrink-0 h-full">
|
||||
<header className="h-12 border-b border-background flex items-center px-4 font-semibold text-foreground shrink-0">
|
||||
Waveform Community
|
||||
</header>
|
||||
|
||||
<div className="flex-1 overflow-y-auto p-3 space-y-4">
|
||||
<div>
|
||||
<h2 className="text-xs font-semibold text-muted uppercase tracking-wider mb-2 px-2">
|
||||
Text Channels
|
||||
</h2>
|
||||
<nav className="space-y-0.5">
|
||||
<button className="w-full text-left px-2 py-1.5 rounded text-muted hover:bg-background hover:text-foreground font-medium text-sm flex items-center gap-2 transition-colors cursor-pointer">
|
||||
<Hash className="w-4 h-4 text-muted shrink-0" />
|
||||
<span className="truncate">general</span>
|
||||
</button>
|
||||
<button className="w-full text-left px-2 py-1.5 rounded text-muted hover:bg-background hover:text-foreground font-medium text-sm flex items-center gap-2 transition-colors cursor-pointer">
|
||||
<Hash className="w-4 h-4 text-muted shrink-0" />
|
||||
<span className="truncate">dev-talk</span>
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
25
components/sidebar/MemberSidebar.tsx
Normal file
25
components/sidebar/MemberSidebar.tsx
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/**
|
||||
* @file components/sidebar/MemberSidebar.tsx
|
||||
* @description Sidebar component displaying the list of active server/chat members and their online status.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Renders the member sidebar showing online users and their profile avatars.
|
||||
*
|
||||
* @returns {JSX.Element} The rendered member sidebar navigation container.
|
||||
*/
|
||||
export function MemberSidebar() {
|
||||
return (
|
||||
<aside className="w-60 bg-[hsl(200_6%_8%)] border-l border-neutral-800 p-4 shrink-0 h-full overflow-y-auto">
|
||||
<h2 className="text-xs font-semibold text-neutral-400 uppercase tracking-wider mb-3">
|
||||
Online — 1
|
||||
</h2>
|
||||
<div className="flex items-center gap-3 py-1.5">
|
||||
<div className="w-8 h-8 rounded-full bg-accent flex items-center justify-center font-bold text-xs text-white">
|
||||
U
|
||||
</div>
|
||||
<span className="text-sm font-medium text-foreground">User</span>
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
25
components/sidebar/ServerSidebar.tsx
Normal file
25
components/sidebar/ServerSidebar.tsx
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/**
|
||||
* @file components/sidebar/ServerSidebar.tsx
|
||||
* @description Sidebar navigation component for switching between servers and direct messages.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Navigation bar for switching between servers and direct messages.
|
||||
*
|
||||
* @returns {JSX.Element} The rendered server sidebar component.
|
||||
*/
|
||||
export function ServerSidebar() {
|
||||
return (
|
||||
<aside className="w-18 bg-surface flex flex-col items-center py-3 gap-3 border-r border-background shrink-0 h-full justify-between">
|
||||
<div className="flex flex-col items-center gap-3 w-full">
|
||||
<div className="w-12 h-12 rounded-2xl bg-accent flex items-center justify-center font-bold text-white cursor-pointer hover:rounded-xl transition-all shadow-md">
|
||||
W
|
||||
</div>
|
||||
<div className="w-8 h-0.5 bg-background/80 rounded-full" />
|
||||
<div className="w-12 h-12 rounded-3xl bg-background flex items-center justify-center text-muted hover:bg-accent hover:text-white hover:rounded-xl transition-all cursor-pointer">
|
||||
+
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
45
components/sidebar/UserProfile.tsx
Normal file
45
components/sidebar/UserProfile.tsx
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* @file components/sidebar/UserProfile.tsx
|
||||
* @description User profile component rendered at the bottom of the sidebar, displaying user avatar, status indicator, username, and settings action trigger.
|
||||
*/
|
||||
|
||||
import { Settings } from "lucide-react";
|
||||
|
||||
/**
|
||||
* Renders the user profile card with user information, online status, and quick access settings.
|
||||
*
|
||||
* @returns {JSX.Element} The rendered user profile footer element.
|
||||
*/
|
||||
export function UserProfile() {
|
||||
return (
|
||||
<div className="p-2 w-full bg-surface shrink-0">
|
||||
<footer className="h-14 bg-background/80 hover:bg-background/90 border border-background/50 rounded-xl flex items-center justify-between px-3 gap-2 shadow-lg backdrop-blur-md transition-all duration-200">
|
||||
<div className="flex items-center gap-2.5 min-w-0 flex-1 cursor-pointer group">
|
||||
<div className="relative shrink-0">
|
||||
<div className="w-8 h-8 rounded-full bg-accent flex items-center justify-center text-white font-bold text-xs shadow-sm transition-transform group-hover:scale-105">
|
||||
U
|
||||
</div>
|
||||
{/* Online Status Indicator */}
|
||||
<span className="absolute bottom-0 right-0 w-2.5 h-2.5 rounded-full bg-emerald-500 ring-2 ring-background" />
|
||||
</div>
|
||||
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-semibold text-foreground truncate leading-tight group-hover:text-accent transition-colors">
|
||||
Username
|
||||
</p>
|
||||
<p className="text-xs text-muted truncate leading-tight font-medium">
|
||||
Online
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
className="p-1.5 text-muted hover:text-foreground focus:outline-none cursor-pointer"
|
||||
aria-label="User Settings"
|
||||
>
|
||||
<Settings className="w-4 h-4" />
|
||||
</button>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
66
components/ui/MobileDrawer.tsx
Normal file
66
components/ui/MobileDrawer.tsx
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/**
|
||||
* @file components/ui/MobileDrawer.tsx
|
||||
* @description Slide-out drawer component for mobile views with configurable side placement, responsive breakpoint behavior, and backdrop overlay.
|
||||
*/
|
||||
|
||||
"use client";
|
||||
|
||||
import { clsx } from "clsx";
|
||||
|
||||
interface MobileDrawerProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
side: "left" | "right";
|
||||
breakpoint: "md" | "xl";
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a responsive drawer container that slides in from either side on mobile viewports and transitions to a static layout at specified breakpoints.
|
||||
*
|
||||
* @param {MobileDrawerProps} props - The component props.
|
||||
* @param {boolean} props.isOpen - Controls the visibility state of the drawer overlay on mobile viewports.
|
||||
* @param {() => void} props.onClose - Callback function triggered when clicking the backdrop overlay to close the drawer.
|
||||
* @param {"left" | "right"} props.side - The screen edge from which the drawer slides out.
|
||||
* @param {"md" | "xl"} props.breakpoint - Tailwind responsive breakpoint at which the drawer becomes static and hides the mobile backdrop.
|
||||
* @param {React.ReactNode} props.children - Content rendered within the drawer body.
|
||||
* @returns {JSX.Element} The drawer element alongside its conditional backdrop overlay.
|
||||
*/
|
||||
export function MobileDrawer({
|
||||
isOpen,
|
||||
onClose,
|
||||
side,
|
||||
breakpoint,
|
||||
children,
|
||||
}: MobileDrawerProps) {
|
||||
const isLeft = side === "left";
|
||||
|
||||
const translateHidden = isLeft ? "-translate-x-full" : "translate-x-full";
|
||||
const breakpointStatic =
|
||||
breakpoint === "md"
|
||||
? "md:static md:translate-x-0"
|
||||
: "xl:static xl:translate-x-0";
|
||||
const breakpointHidden = breakpoint === "md" ? "md:hidden" : "xl:hidden";
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={clsx(
|
||||
"fixed inset-y-0 z-40 flex transform transition-transform duration-200 ease-in-out h-full",
|
||||
isLeft ? "left-0" : "right-0",
|
||||
breakpointStatic,
|
||||
isOpen ? "translate-x-0" : translateHidden,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
{isOpen && (
|
||||
<div
|
||||
className={clsx("fixed inset-0 bg-black/60 z-30", breakpointHidden)}
|
||||
onClick={onClose}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
39
lib/store/useSidebarStore.ts
Normal file
39
lib/store/useSidebarStore.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* @file lib/store/useSidebarStore.ts
|
||||
* @description Condition state management store for controlling the visibility and mutual exclusion of navigation and members sidebars.
|
||||
*/
|
||||
|
||||
import { create } from "zustand";
|
||||
|
||||
/**
|
||||
* Interface representing the state and actions of the sidebar store.
|
||||
*
|
||||
* @interface SidebarState
|
||||
* @property {boolean} isNavOpen - Indicates whether the navigation sidebar is open.
|
||||
* @property {boolean} isMembersOpen - Indicates whether the members list sidebar is open.
|
||||
* @property {() => void} toggleNav - Toggles the navigation sidebar while automatically closing the members sidebar.
|
||||
* @property {() => void} toggleMembers - Toggles the members sidebar while automatically closing the navigation sidebar.
|
||||
* @property {() => void} closeAll - Closes both the navigation and members sidebars simultaneously.
|
||||
*/
|
||||
interface SidebarState {
|
||||
isNavOpen: boolean;
|
||||
isMembersOpen: boolean;
|
||||
toggleNav: () => void;
|
||||
toggleMembers: () => void;
|
||||
closeAll: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom Condition hook for managing global sidebar visibility states.
|
||||
*
|
||||
* @returns {SidebarState} The current sidebar state and action handlers.
|
||||
*/
|
||||
export const useSidebarStore = create<SidebarState>((set) => ({
|
||||
isNavOpen: false,
|
||||
isMembersOpen: false,
|
||||
toggleNav: () =>
|
||||
set((state) => ({ isNavOpen: !state.isNavOpen, isMembersOpen: false })),
|
||||
toggleMembers: () =>
|
||||
set((state) => ({ isMembersOpen: !state.isMembersOpen, isNavOpen: false })),
|
||||
closeAll: () => set({ isNavOpen: false, isMembersOpen: false }),
|
||||
}));
|
||||
56
package-lock.json
generated
56
package-lock.json
generated
|
|
@ -8,9 +8,12 @@
|
|||
"name": "waveform",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^1.35.0",
|
||||
"next": "16.3.3",
|
||||
"react": "^19.2.8",
|
||||
"react-dom": "^19.2.8"
|
||||
"react-dom": "^19.2.8",
|
||||
"zustand": "^5.0.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.3.3",
|
||||
|
|
@ -1722,7 +1725,7 @@
|
|||
"version": "19.2.18",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz",
|
||||
"integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"csstype": "^3.2.2"
|
||||
|
|
@ -2853,6 +2856,15 @@
|
|||
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/clsx": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
|
|
@ -2906,7 +2918,7 @@
|
|||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/damerau-levenshtein": {
|
||||
|
|
@ -5156,6 +5168,15 @@
|
|||
"yallist": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/lucide-react": {
|
||||
"version": "1.35.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.35.0.tgz",
|
||||
"integrity": "sha512-yXCCWxGFYT6bLIPYC4SY6fPQPRs/d797rRIue+J9XP2Td6vQvD53gaQRBCnIVT1kTQRHtAtxlfOQNWAuIF8ELg==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.21",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
||||
|
|
@ -6891,6 +6912,35 @@
|
|||
"peerDependencies": {
|
||||
"zod": "^3.25.0 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/zustand": {
|
||||
"version": "5.0.15",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.15.tgz",
|
||||
"integrity": "sha512-MpSEjRiBkA9crSYeOUH32rJC7SVqAbm0Fqcqge/bUi2PPoLcBWKOsG+C8mevmpr8TwXHBVkChbbJiyvkE+i/3A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.20.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": ">=18.0.0",
|
||||
"immer": ">=9.0.6",
|
||||
"react": ">=18.0.0",
|
||||
"use-sync-external-store": ">=1.2.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"immer": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"use-sync-external-store": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,9 +9,12 @@
|
|||
"lint": "eslint"
|
||||
},
|
||||
"dependencies": {
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^1.35.0",
|
||||
"next": "16.3.3",
|
||||
"react": "^19.2.8",
|
||||
"react-dom": "^19.2.8"
|
||||
"react-dom": "^19.2.8",
|
||||
"zustand": "^5.0.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.3.3",
|
||||
|
|
|
|||
Loading…
Reference in a new issue