From 3916ed876330e92ffbdcc6ddf8b41a05510bdb98 Mon Sep 17 00:00:00 2001 From: siyue Date: Sun, 10 Nov 2024 16:52:26 +0800 Subject: [PATCH] feat:baseinfo add icon --- apps/fronted/package.json | 2 + apps/fronted/src/app/page.tsx | 458 +++++++++++++----- .../src/components/editor/IconSelector.tsx | 113 +++++ .../components/editor/basic/BasicPanel.tsx | 251 ++++++---- apps/fronted/src/components/ui/command.tsx | 155 ++++++ apps/fronted/src/components/ui/dialog.tsx | 122 +++++ apps/fronted/src/store/useResumeStore.ts | 1 + apps/fronted/src/types/resume.ts | 2 + pnpm-lock.yaml | 293 +++++++++++ 9 files changed, 1196 insertions(+), 201 deletions(-) create mode 100644 apps/fronted/src/components/editor/IconSelector.tsx create mode 100644 apps/fronted/src/components/ui/command.tsx create mode 100644 apps/fronted/src/components/ui/dialog.tsx diff --git a/apps/fronted/package.json b/apps/fronted/package.json index 6224d41..92976de 100644 --- a/apps/fronted/package.json +++ b/apps/fronted/package.json @@ -11,6 +11,7 @@ "dependencies": { "@radix-ui/react-accordion": "^1.2.1", "@radix-ui/react-alert-dialog": "^1.1.2", + "@radix-ui/react-dialog": "^1.1.2", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-popover": "^1.1.1", "@radix-ui/react-select": "^2.1.2", @@ -31,6 +32,7 @@ "@tiptap/starter-kit": "^2.4.0", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", + "cmdk": "1.0.0", "date-fns": "^3.6.0", "dayjs": "^1.11.12", "framer-motion": "^11.11.10", diff --git a/apps/fronted/src/app/page.tsx b/apps/fronted/src/app/page.tsx index 010b287..4dca685 100644 --- a/apps/fronted/src/app/page.tsx +++ b/apps/fronted/src/app/page.tsx @@ -1,11 +1,12 @@ "use client"; + import React, { useState, useEffect } from "react"; import Image from "next/image"; import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; +import { motion } from "framer-motion"; import { ArrowRight, - FileText, Sun, Moon, Layout, @@ -15,51 +16,246 @@ import { Grid, Layers, Palette, - Settings + Settings, + Zap, + Shield, + Globe, + Cloud } from "lucide-react"; import ScrollToTop from "../components/ScrollToTop"; -import Logo from "@/assets/images/logo@2x.svg"; import EditButton from "@/components/EditButton"; -const features = [ - { - icon: , - title: "自由布局", - description: "灵活的模块拖拽,随心所欲地调整简历版面布局" - }, - { - icon: , - title: "样式定制", - description: "字体、颜色、间距等细节完全可调,打造独特简历风格" - }, - { - icon: , - title: "模块定制", - description: "自定义模块内容与顺序,突出个人优势特点" - }, - { - icon: , - title: "主题定制", - description: "提供多套配色方案,也可以自定义专属配色" - }, - { - icon: , - title: "内容模板", - description: "丰富的内容模板,帮助你更好地展示经历" - }, - { - icon: , - title: "一键调整", - description: "快速调整字号、间距等,适配不同场景需求" - } -]; +import { cn } from "@/lib/utils"; -const stats = [ - { number: "95%", label: "定制程度", icon: Layout }, - { number: "100+", label: "组件模块", icon: Grid }, - { number: "50+", label: "预设主题", icon: Palette }, - { number: "24H", label: "客服响应", icon: Share2 } -]; +const TypewriterText = ({ text }) => { + const [displayText, setDisplayText] = useState(""); + const [currentIndex, setCurrentIndex] = useState(0); + useEffect(() => { + if (currentIndex < text.length) { + const timeout = setTimeout( + () => { + setDisplayText((prev) => prev + text[currentIndex]); + setCurrentIndex((c) => c + 1); + }, + 50 + Math.random() * 50 + ); + return () => clearTimeout(timeout); + } + }, [currentIndex, text]); + + return ( + + {displayText} + + + ); +}; + +const FeatureGrid = ({ isDark }) => { + const brandFeature = { + title: "Magic Resume", + subtitle: "AI驱动的智能简历系统", + tagline: "让简历制作变得简单", + color: "from-blue-500 via-indigo-500 to-purple-500" + }; + + const features = [ + { + title: "智能排版", + description: "AI辅助排版,告别繁琐调整", + highlight: "3倍效率", + icon: Layout, + color: "from-blue-600 to-blue-400" + }, + { + title: "专业模板", + description: "精选行业模板,一键应用", + highlight: "100+ 模板", + icon: Layers, + color: "from-violet-600 to-violet-400" + }, + { + title: "数据安全", + description: "本地存储,安全可靠", + highlight: "隐私优先", + icon: Shield, + color: "from-emerald-600 to-emerald-400" + }, + { + title: "多端同步", + description: "随时随地,无缝编辑", + highlight: "云端同步", + icon: Cloud, + color: "from-orange-600 to-orange-400" + } + ]; + + // 品牌核心卡片组件 + const BrandCard = () => ( + +
+ {/* 渐变背景 */} +
+
+
+ + {/* 光效装饰 */} +
+
+
+ + {/* 内容 */} +
+ +

+ {brandFeature.title} +

+

+ {brandFeature.subtitle} +

+

+ {brandFeature.tagline} +

+
+
+
+ + ); + + // 功能卡片组件 + const FeatureCard = ({ feature, index }) => ( + +
+
+ {/* 背景装饰 */} +
+
+
+ + {/* 内容 */} +
+ {/* 图标 */} +
+ +
+ + {/* 文本 */} +

+ {feature.title} +

+

+ {feature.description} +

+ + {/* 高亮标签 */} +
+ + {feature.highlight} + +
+
+
+
+ + ); + + return ( +
+
+ {/* 品牌核心展示 */} + + + {/* 功能特性 */} + {features.map((feature, index) => ( + + ))} +
+
+ ); +}; + +// 主页面组件 export default function LandingPage() { const [isDark, setIsDark] = useState(true); @@ -90,31 +286,6 @@ export default function LandingPage() { } }; - const TypewriterText = ({ text }: { text: string }) => { - const [displayText, setDisplayText] = useState(""); - const [currentIndex, setCurrentIndex] = useState(0); - - useEffect(() => { - if (currentIndex < text.length) { - const timeout = setTimeout( - () => { - setDisplayText((prev) => prev + text[currentIndex]); - setCurrentIndex((c) => c + 1); - }, - 50 + Math.random() * 50 - ); // 添加随机延迟使打字效果更自然 - return () => clearTimeout(timeout); - } - }, [currentIndex, text]); - - return ( - - {displayText} - - - ); - }; - return (

- +

+ +

开启你的个性化简历之旅

+ +
+ + {/* Footer */} + + + {/* 移动端菜单按钮 */} +
+ + + +
); } diff --git a/apps/fronted/src/components/editor/IconSelector.tsx b/apps/fronted/src/components/editor/IconSelector.tsx new file mode 100644 index 0000000..234a2e7 --- /dev/null +++ b/apps/fronted/src/components/editor/IconSelector.tsx @@ -0,0 +1,113 @@ +import React from "react"; +import { User } from "lucide-react"; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem +} from "@/components/ui/command"; +import { + Popover, + PopoverContent, + PopoverTrigger +} from "@/components/ui/popover"; +import { cn } from "@/lib/utils"; + +// 定义一个简单的图标列表作为示例 +const ICONS = [ + { name: "User", component: User } + // 其他图标将在选择后动态导入 +]; + +const IconSelector = ({ value, onChange, theme = "light" }) => { + const [open, setOpen] = React.useState(false); + const [icons, setIcons] = React.useState(ICONS); + + // 在组件挂载时加载图标 + React.useEffect(() => { + const loadIcons = async () => { + const lucide = await import("lucide-react"); + // console.log(lucide, "lucide"); + // const iconList = Object.entries(lucide) + // .filter( + // ([name]) => name[0] === name[0].toUpperCase() // 只选择以大写字母开头的导出(图标组件) + // ) + // ?.map(([name, component]) => ({ + // name, + // component + // })); + // console.log(iconList, "iconList"); + // setIcons(iconList); + }; + + loadIcons(); + }, []); + + // 获取当前选中的图标组件 + const getCurrentIcon = React.useCallback(() => { + if (!value) return User; + const icon = icons.find((i) => i.name === value); + return icon ? icon.component : User; + }, [value, icons]); + + const IconComponent = getCurrentIcon(); + + return ( + + + + + + + + 未找到图标 + + {icons?.map(({ name, component: Icon }) => ( + { + onChange(name); + setOpen(false); + }} + className={cn( + "flex items-center gap-2 cursor-pointer py-2", + theme === "dark" + ? "hover:bg-neutral-800" + : "hover:bg-gray-100" + )} + > + + {name} + + ))} + + + + + ); +}; + +export default IconSelector; diff --git a/apps/fronted/src/components/editor/basic/BasicPanel.tsx b/apps/fronted/src/components/editor/basic/BasicPanel.tsx index f645d17..00b216d 100644 --- a/apps/fronted/src/components/editor/basic/BasicPanel.tsx +++ b/apps/fronted/src/components/editor/basic/BasicPanel.tsx @@ -1,16 +1,96 @@ -"use client"; import React, { useState } from "react"; -import { PlusCircle, X } from "lucide-react"; +import { + PlusCircle, + X, + User, + Briefcase, + Calendar, + Mail, + Phone, + MapPin, + FileText, + Smartphone, + Globe, + Github +} from "lucide-react"; import { Button } from "@/components/ui/button"; import Field from "../Field"; import { useResumeStore } from "@/store/useResumeStore"; import { Input } from "@/components/ui/input"; -import { platform } from "os"; import { cn } from "@/lib/utils"; +import { + Popover, + PopoverContent, + PopoverTrigger +} from "@/components/ui/popover"; + +// 预定义图标选项 +const iconOptions = [ + { label: "用户", value: "User", icon: User }, + { label: "工作", value: "Briefcase", icon: Briefcase }, + { label: "日期", value: "Calendar", icon: Calendar }, + { label: "邮箱", value: "Mail", icon: Mail }, + { label: "电话", value: "Phone", icon: Phone }, + { label: "地址", value: "MapPin", icon: MapPin }, + { label: "简介", value: "FileText", icon: FileText }, + { label: "手机", value: "Smartphone", icon: Smartphone }, + { label: "网站", value: "Globe", icon: Globe }, + { label: "Github", value: "Github", icon: Github } +]; + +const IconSelector = ({ value, onChange, theme }) => { + const [open, setOpen] = React.useState(false); + const selectedIcon = + iconOptions.find((i) => i.value === value) || iconOptions[0]; + + const handleSelect = (iconValue) => { + onChange(iconValue); + setOpen(false); + }; + + return ( + + + + + + {iconOptions.map(({ value: iconValue, icon: Icon, label }) => ( + + ))} + + + ); +}; const BasicPanel = () => { const { basic, updateBasicInfo, theme } = useResumeStore(); - const [fields, setFields] = useState(basic.customFields); + const [fields, setFields] = useState(basic?.customFields || []); const [newLabel, setNewLabel] = useState(""); const [newValue, setNewValue] = useState(""); @@ -19,102 +99,111 @@ const BasicPanel = () => { id: crypto.randomUUID(), label: newLabel, value: newValue, + icon: "User", required: false, placeholder: "" }; - setFields([...fields, newField]); + const updatedFields = [...fields, newField]; + setFields(updatedFields); updateBasicInfo({ ...basic, - customFields: [...fields, newField] + customFields: updatedFields }); + setNewLabel(""); + setNewValue(""); }; - const updateField = (id: string, updates: { value: string }) => { - setFields( - fields.map((field) => - field.id === id ? { ...field, ...updates } : field - ) + const updateField = (id: string, updates: any) => { + const updatedFields = fields.map((field) => + field.id === id ? { ...field, ...updates } : field ); - updateBasicInfo({ customFields: fields }); + setFields(updatedFields); + updateBasicInfo({ + ...basic, + customFields: updatedFields + }); }; - const deleteField = (id) => { - setFields(fields.filter((field) => field.id !== id)); + const updateIcon = (key: string, iconName: string) => { updateBasicInfo({ - customFields: fields.filter((field) => field.id !== id) + ...basic, + icons: { + ...(basic?.icons || {}), + [key]: iconName + } }); }; + const deleteField = (id: string) => { + const updatedFields = fields.filter((field) => field.id !== id); + setFields(updatedFields); + updateBasicInfo({ + ...basic, + customFields: updatedFields + }); + }; + + const renderField = (key: string, label: string, options = {}) => { + const { + required = false, + type = "text", + isCustom = false, + field = null + } = options; + + const selectedIcon = basic?.icons?.[key] || "User"; + + return ( +
+
+ updateIcon(key, value)} + theme={theme} + /> + {label} +
+ + + isCustom + ? updateField(field.id, { value }) + : updateBasicInfo({ [key]: value }) + } + placeholder={`请输入${label}`} + type={type} + /> +
+ ); + }; + return (
- updateBasicInfo({ name: value })} - placeholder="请输入你的姓名" - required - /> - updateBasicInfo({ title: value })} - placeholder="期望职位" - required - /> - updateBasicInfo({ birthDate: value })} - type="date" - /> + {renderField("name", "姓名", { required: true })} + {renderField("title", "职位", { required: true })} + {renderField("birthDate", "出生日期", { type: "date" })}
- updateBasicInfo({ email: value })} - placeholder="your@email.com" - required - /> - updateBasicInfo({ phone: value })} - placeholder="手机号码" - required - /> + {renderField("email", "电子邮箱", { required: true })} + {renderField("phone", "电话", { required: true })}
- updateBasicInfo({ location: value })} - placeholder="城市" - required - /> - updateBasicInfo({ summary: value })} - type="textarea" - placeholder="简单介绍一下自己..." - /> + {renderField("location", "所在地", { required: true })} + {renderField("summary", "个人简介", { type: "textarea" })} {/* 自定义字段 */} - {fields.map((field) => ( -
- updateField(field.id, { value })} - placeholder={field.placeholder} - /> - -
- ))} + {Array.isArray(fields) && + fields.map((field) => ( +
+ {renderField(field.id, field.label, { isCustom: true, field })} + +
+ ))} {/* 新字段输入区 */}
@@ -145,8 +234,8 @@ const BasicPanel = () => { disabled={!newLabel} className={cn( theme === "dark" - ? "bg-indigo-600 hover:bg-indigo-700 text-white " - : "bg-black hover:bg-neutral-800 text-white " + ? "bg-indigo-600 hover:bg-indigo-700 text-white" + : "bg-black hover:bg-neutral-800 text-white" )} > diff --git a/apps/fronted/src/components/ui/command.tsx b/apps/fronted/src/components/ui/command.tsx new file mode 100644 index 0000000..ace9b53 --- /dev/null +++ b/apps/fronted/src/components/ui/command.tsx @@ -0,0 +1,155 @@ +"use client" + +import * as React from "react" +import { type DialogProps } from "@radix-ui/react-dialog" +import { Command as CommandPrimitive } from "cmdk" +import { Search } from "lucide-react" + +import { cn } from "@/lib/utils" +import { Dialog, DialogContent } from "@/components/ui/dialog" + +const Command = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Command.displayName = CommandPrimitive.displayName + +interface CommandDialogProps extends DialogProps {} + +const CommandDialog = ({ children, ...props }: CommandDialogProps) => { + return ( + + + + {children} + + + + ) +} + +const CommandInput = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( +
+ + +
+)) + +CommandInput.displayName = CommandPrimitive.Input.displayName + +const CommandList = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) + +CommandList.displayName = CommandPrimitive.List.displayName + +const CommandEmpty = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>((props, ref) => ( + +)) + +CommandEmpty.displayName = CommandPrimitive.Empty.displayName + +const CommandGroup = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) + +CommandGroup.displayName = CommandPrimitive.Group.displayName + +const CommandSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +CommandSeparator.displayName = CommandPrimitive.Separator.displayName + +const CommandItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) + +CommandItem.displayName = CommandPrimitive.Item.displayName + +const CommandShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ) +} +CommandShortcut.displayName = "CommandShortcut" + +export { + Command, + CommandDialog, + CommandInput, + CommandList, + CommandEmpty, + CommandGroup, + CommandItem, + CommandShortcut, + CommandSeparator, +} diff --git a/apps/fronted/src/components/ui/dialog.tsx b/apps/fronted/src/components/ui/dialog.tsx new file mode 100644 index 0000000..01ff19c --- /dev/null +++ b/apps/fronted/src/components/ui/dialog.tsx @@ -0,0 +1,122 @@ +"use client" + +import * as React from "react" +import * as DialogPrimitive from "@radix-ui/react-dialog" +import { X } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Dialog = DialogPrimitive.Root + +const DialogTrigger = DialogPrimitive.Trigger + +const DialogPortal = DialogPrimitive.Portal + +const DialogClose = DialogPrimitive.Close + +const DialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogOverlay.displayName = DialogPrimitive.Overlay.displayName + +const DialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + {children} + + + Close + + + +)) +DialogContent.displayName = DialogPrimitive.Content.displayName + +const DialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +DialogHeader.displayName = "DialogHeader" + +const DialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +DialogFooter.displayName = "DialogFooter" + +const DialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogTitle.displayName = DialogPrimitive.Title.displayName + +const DialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogDescription.displayName = DialogPrimitive.Description.displayName + +export { + Dialog, + DialogPortal, + DialogOverlay, + DialogClose, + DialogTrigger, + DialogContent, + DialogHeader, + DialogFooter, + DialogTitle, + DialogDescription, +} diff --git a/apps/fronted/src/store/useResumeStore.ts b/apps/fronted/src/store/useResumeStore.ts index 9bed541..109b019 100644 --- a/apps/fronted/src/store/useResumeStore.ts +++ b/apps/fronted/src/store/useResumeStore.ts @@ -66,6 +66,7 @@ const initialState = { location: "北京市", summary: "5年前端开发经验...", birthDate: "1995-01-01", + icons: {}, customFields: [] }, education: [ diff --git a/apps/fronted/src/types/resume.ts b/apps/fronted/src/types/resume.ts index 3715b7c..7e6e9e9 100644 --- a/apps/fronted/src/types/resume.ts +++ b/apps/fronted/src/types/resume.ts @@ -6,10 +6,12 @@ export interface BasicInfo { phone: string; location: string; summary: string; + icons: Record; customFields: Array<{ id: string; label: string; value: string; + icon: string; required: boolean; placeholder: string; }>; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7d9a41d..5053e08 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -98,6 +98,9 @@ importers: '@radix-ui/react-alert-dialog': specifier: ^1.1.2 version: 1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-dialog': + specifier: ^1.1.2 + version: 1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1) '@radix-ui/react-label': specifier: ^2.1.0 version: 2.1.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1) @@ -158,6 +161,9 @@ importers: clsx: specifier: ^2.1.1 version: 2.1.1 + cmdk: + specifier: 1.0.0 + version: 1.0.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1) date-fns: specifier: ^3.6.0 version: 3.6.0 @@ -1444,6 +1450,12 @@ packages: resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==} dev: false + /@radix-ui/primitive@1.0.1: + resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} + dependencies: + '@babel/runtime': 7.24.5 + dev: false + /@radix-ui/primitive@1.1.0: resolution: {integrity: sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==} dev: false @@ -1598,6 +1610,20 @@ packages: react: 18.3.1 dev: false + /@radix-ui/react-context@1.0.1(@types/react@18.3.2)(react@18.3.1): + resolution: {integrity: sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.24.5 + '@types/react': 18.3.2 + react: 18.3.1 + dev: false + /@radix-ui/react-context@1.1.0(@types/react@18.3.2)(react@18.3.1): resolution: {integrity: sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==} peerDependencies: @@ -1624,6 +1650,40 @@ packages: react: 18.3.1 dev: false + /@radix-ui/react-dialog@1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.24.5 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-context': 1.0.1(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-id': 1.0.1(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-slot': 1.0.2(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.3.2)(react@18.3.1) + '@types/react': 18.3.2 + '@types/react-dom': 18.3.0 + aria-hidden: 1.2.4 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-remove-scroll: 2.5.5(@types/react@18.3.2)(react@18.3.1) + dev: false + /@radix-ui/react-dialog@1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-Yj4dZtqa2o+kG61fzB0H2qUvmwBA2oyQroGLyNtBj1beo1khoQ3q1a2AO8rrQYjd8256CO9+N8L9tvsS+bnIyA==} peerDependencies: @@ -1670,6 +1730,31 @@ packages: react: 18.3.1 dev: false + /@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.24.5 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.3.2)(react@18.3.1) + '@types/react': 18.3.2 + '@types/react-dom': 18.3.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + dev: false + /@radix-ui/react-dismissable-layer@1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-QSxg29lfr/xcev6kSz7MAlmDnzbP1eI/Dwn3Tp1ip0KT5CUELsxkekFEMVBEoykI3oV39hKT4TKZzBNMbcTZYQ==} peerDependencies: @@ -1694,6 +1779,20 @@ packages: react-dom: 18.3.1(react@18.3.1) dev: false + /@radix-ui/react-focus-guards@1.0.1(@types/react@18.3.2)(react@18.3.1): + resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.24.5 + '@types/react': 18.3.2 + react: 18.3.1 + dev: false + /@radix-ui/react-focus-guards@1.1.1(@types/react@18.3.2)(react@18.3.1): resolution: {integrity: sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==} peerDependencies: @@ -1707,6 +1806,29 @@ packages: react: 18.3.1 dev: false + /@radix-ui/react-focus-scope@1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.24.5 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.2)(react@18.3.1) + '@types/react': 18.3.2 + '@types/react-dom': 18.3.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + dev: false + /@radix-ui/react-focus-scope@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==} peerDependencies: @@ -1729,6 +1851,21 @@ packages: react-dom: 18.3.1(react@18.3.1) dev: false + /@radix-ui/react-id@1.0.1(@types/react@18.3.2)(react@18.3.1): + resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.24.5 + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.2)(react@18.3.1) + '@types/react': 18.3.2 + react: 18.3.1 + dev: false + /@radix-ui/react-id@1.1.0(@types/react@18.3.2)(react@18.3.1): resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==} peerDependencies: @@ -1826,6 +1963,27 @@ packages: react-dom: 18.3.1(react@18.3.1) dev: false + /@radix-ui/react-portal@1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.24.5 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1) + '@types/react': 18.3.2 + '@types/react-dom': 18.3.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + dev: false + /@radix-ui/react-portal@1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-WeDYLGPxJb/5EGBoedyJbT0MpoULmwnIPMJMSldkuiMsBAv7N1cRdsTWZWht9vpPOiN3qyiGAtbK2is47/uMFg==} peerDependencies: @@ -1847,6 +2005,28 @@ packages: react-dom: 18.3.1(react@18.3.1) dev: false + /@radix-ui/react-presence@1.0.1(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.24.5 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.2)(react@18.3.1) + '@types/react': 18.3.2 + '@types/react-dom': 18.3.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + dev: false + /@radix-ui/react-presence@1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==} peerDependencies: @@ -1868,6 +2048,27 @@ packages: react-dom: 18.3.1(react@18.3.1) dev: false + /@radix-ui/react-primitive@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.24.5 + '@radix-ui/react-slot': 1.0.2(@types/react@18.3.2)(react@18.3.1) + '@types/react': 18.3.2 + '@types/react-dom': 18.3.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + dev: false + /@radix-ui/react-primitive@2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==} peerDependencies: @@ -2119,6 +2320,20 @@ packages: react-dom: 18.3.1(react@18.3.1) dev: false + /@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.3.2)(react@18.3.1): + resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.24.5 + '@types/react': 18.3.2 + react: 18.3.1 + dev: false + /@radix-ui/react-use-callback-ref@1.1.0(@types/react@18.3.2)(react@18.3.1): resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==} peerDependencies: @@ -2132,6 +2347,21 @@ packages: react: 18.3.1 dev: false + /@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.3.2)(react@18.3.1): + resolution: {integrity: sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.24.5 + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.2)(react@18.3.1) + '@types/react': 18.3.2 + react: 18.3.1 + dev: false + /@radix-ui/react-use-controllable-state@1.1.0(@types/react@18.3.2)(react@18.3.1): resolution: {integrity: sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==} peerDependencies: @@ -2146,6 +2376,21 @@ packages: react: 18.3.1 dev: false + /@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.3.2)(react@18.3.1): + resolution: {integrity: sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.24.5 + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.2)(react@18.3.1) + '@types/react': 18.3.2 + react: 18.3.1 + dev: false + /@radix-ui/react-use-escape-keydown@1.1.0(@types/react@18.3.2)(react@18.3.1): resolution: {integrity: sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==} peerDependencies: @@ -2160,6 +2405,20 @@ packages: react: 18.3.1 dev: false + /@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.3.2)(react@18.3.1): + resolution: {integrity: sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.24.5 + '@types/react': 18.3.2 + react: 18.3.1 + dev: false + /@radix-ui/react-use-layout-effect@1.1.0(@types/react@18.3.2)(react@18.3.1): resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==} peerDependencies: @@ -3740,6 +3999,21 @@ packages: engines: {node: '>=6'} dev: false + /cmdk@1.0.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-gDzVf0a09TvoJ5jnuPvygTB77+XdOSwEmJ88L6XPFPlv7T3RxbP9jgenfylrAMD0+Le1aO0nVjQUzl2g+vjz5Q==} + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + dependencies: + '@radix-ui/react-dialog': 1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + dev: false + /co@4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} @@ -7191,6 +7465,25 @@ packages: tslib: 2.6.2 dev: false + /react-remove-scroll@2.5.5(@types/react@18.3.2)(react@18.3.1): + resolution: {integrity: sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.3.2 + react: 18.3.1 + react-remove-scroll-bar: 2.3.6(@types/react@18.3.2)(react@18.3.1) + react-style-singleton: 2.2.1(@types/react@18.3.2)(react@18.3.1) + tslib: 2.6.2 + use-callback-ref: 1.3.2(@types/react@18.3.2)(react@18.3.1) + use-sidecar: 1.1.2(@types/react@18.3.2)(react@18.3.1) + dev: false + /react-remove-scroll@2.6.0(@types/react@18.3.2)(react@18.3.1): resolution: {integrity: sha512-I2U4JVEsQenxDAKaVa3VZ/JeJZe0/2DxPWL8Tj8yLKctQJQiZM52pn/GWFpSp8dftjM3pSAHVJZscAnC/y+ySQ==} engines: {node: '>=10'}