From 1996298f0c5a2e2e67e8ce6ce951c761f47bbf44 Mon Sep 17 00:00:00 2001 From: BiggerRain <15911122312@163.COM> Date: Wed, 29 Oct 2025 10:38:52 +0800 Subject: [PATCH] feat: add shadcn ui config (#954) * feat: add shadcn ui config * chore: tailwind config --- components.json | 22 +++++++ package.json | 3 + pnpm-lock.yaml | 56 ++++++++++++++++ src/components/ui/button.tsx | 57 +++++++++++++++++ src/lib/utils.ts | 6 ++ src/main.css | 62 ++++++++++++++++++ src/pages/main/index.tsx | 1 - tailwind.config.js | 120 +++++++++++++++++++++++++++-------- 8 files changed, 299 insertions(+), 28 deletions(-) create mode 100644 components.json create mode 100644 src/components/ui/button.tsx create mode 100644 src/lib/utils.ts diff --git a/components.json b/components.json new file mode 100644 index 00000000..8945f531 --- /dev/null +++ b/components.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "src/main.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "registries": {} +} diff --git a/package.json b/package.json index 49d28827..e2625202 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ }, "dependencies": { "@headlessui/react": "^2.2.2", + "@radix-ui/react-slot": "^1.2.3", "@tauri-apps/api": "^2.5.0", "@tauri-apps/plugin-autostart": "~2.2.0", "@tauri-apps/plugin-deep-link": "^2.2.1", @@ -35,6 +36,7 @@ "@wavesurfer/react": "^1.0.11", "ahooks": "^3.8.4", "axios": "^1.12.0", + "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "dayjs": "^1.11.13", "dotenv": "^16.5.0", @@ -59,6 +61,7 @@ "remark-gfm": "^4.0.1", "remark-math": "^6.0.0", "tailwind-merge": "^3.3.1", + "tailwindcss-animate": "^1.0.7", "tauri-plugin-fs-pro-api": "^2.4.0", "tauri-plugin-macos-permissions-api": "^2.3.0", "tauri-plugin-screenshots-api": "^2.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0b896d68..0cf3f128 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@headlessui/react': specifier: ^2.2.2 version: 2.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': + specifier: ^1.2.3 + version: 1.2.3(@types/react@18.3.26)(react@18.3.1) '@tauri-apps/api': specifier: ^2.5.0 version: 2.9.0 @@ -59,6 +62,9 @@ importers: axios: specifier: ^1.12.0 version: 1.12.0 + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 clsx: specifier: ^2.1.1 version: 2.1.1 @@ -131,6 +137,9 @@ importers: tailwind-merge: specifier: ^3.3.1 version: 3.3.1 + tailwindcss-animate: + specifier: ^1.0.7 + version: 1.0.7(tailwindcss@3.4.18) tauri-plugin-fs-pro-api: specifier: ^2.4.0 version: 2.4.0 @@ -1011,6 +1020,24 @@ packages: resolution: {integrity: sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==} engines: {node: '>=12'} + '@radix-ui/react-compose-refs@1.1.2': + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-slot@1.2.3': + resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@react-aria/focus@3.20.2': resolution: {integrity: sha512-Q3rouk/rzoF/3TuH6FzoAIKrl+kzZi9LHmr8S5EqLAOyP9TXIKG34x2j42dZsAhrw7TbF9gA8tBKwnCNH4ZV+Q==} peerDependencies: @@ -1694,6 +1721,9 @@ packages: resolution: {integrity: sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==} engines: {node: '>=8'} + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + cli-boxes@3.0.0: resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} engines: {node: '>=10'} @@ -3481,6 +3511,11 @@ packages: tailwind-merge@3.3.1: resolution: {integrity: sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==} + tailwindcss-animate@1.0.7: + resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders' + tailwindcss@3.4.18: resolution: {integrity: sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==} engines: {node: '>=14.0.0'} @@ -4512,6 +4547,19 @@ snapshots: '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 + '@radix-ui/react-compose-refs@1.1.2(@types/react@18.3.26)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.26 + + '@radix-ui/react-slot@1.2.3(@types/react@18.3.26)(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.26)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.26 + '@react-aria/focus@3.20.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@react-aria/interactions': 3.25.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -5185,6 +5233,10 @@ snapshots: ci-info@4.2.0: {} + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + cli-boxes@3.0.0: {} cli-cursor@5.0.0: @@ -7322,6 +7374,10 @@ snapshots: tailwind-merge@3.3.1: {} + tailwindcss-animate@1.0.7(tailwindcss@3.4.18): + dependencies: + tailwindcss: 3.4.18 + tailwindcss@3.4.18: dependencies: '@alloc/quick-lru': 5.2.0 diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx new file mode 100644 index 00000000..65d4fcd9 --- /dev/null +++ b/src/components/ui/button.tsx @@ -0,0 +1,57 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +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 transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring 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 shadow hover:bg-primary/90", + destructive: + "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", + outline: + "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", + secondary: + "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-9 px-4 py-2", + sm: "h-8 rounded-md px-3 text-xs", + lg: "h-10 rounded-md px-8", + icon: "h-9 w-9", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button" + return ( + + ) + } +) +Button.displayName = "Button" + +export { Button, buttonVariants } diff --git a/src/lib/utils.ts b/src/lib/utils.ts new file mode 100644 index 00000000..bd0c391d --- /dev/null +++ b/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} diff --git a/src/main.css b/src/main.css index 05b9c668..097bcf68 100644 --- a/src/main.css +++ b/src/main.css @@ -106,6 +106,59 @@ fill: currentColor; overflow: hidden; } + :root { + --background: 0 0% 100%; + --foreground: 0 0% 3.9%; + --card: 0 0% 100%; + --card-foreground: 0 0% 3.9%; + --popover: 0 0% 100%; + --popover-foreground: 0 0% 3.9%; + --primary: 0 0% 9%; + --primary-foreground: 0 0% 98%; + --secondary: 0 0% 96.1%; + --secondary-foreground: 0 0% 9%; + --muted: 0 0% 96.1%; + --muted-foreground: 0 0% 45.1%; + --accent: 0 0% 96.1%; + --accent-foreground: 0 0% 9%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; + --border: 0 0% 89.8%; + --input: 0 0% 89.8%; + --ring: 0 0% 3.9%; + --chart-1: 12 76% 61%; + --chart-2: 173 58% 39%; + --chart-3: 197 37% 24%; + --chart-4: 43 74% 66%; + --chart-5: 27 87% 67%; + --radius: 0.5rem; + } + .dark { + --background: 0 0% 3.9%; + --foreground: 0 0% 98%; + --card: 0 0% 3.9%; + --card-foreground: 0 0% 98%; + --popover: 0 0% 3.9%; + --popover-foreground: 0 0% 98%; + --primary: 0 0% 98%; + --primary-foreground: 0 0% 9%; + --secondary: 0 0% 14.9%; + --secondary-foreground: 0 0% 98%; + --muted: 0 0% 14.9%; + --muted-foreground: 0 0% 63.9%; + --accent: 0 0% 14.9%; + --accent-foreground: 0 0% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 0 0% 98%; + --border: 0 0% 14.9%; + --input: 0 0% 14.9%; + --ring: 0 0% 83.1%; + --chart-1: 220 70% 50%; + --chart-2: 160 60% 45%; + --chart-3: 30 80% 55%; + --chart-4: 280 65% 60%; + --chart-5: 340 75% 55%; + } } /* Component styles */ @@ -261,3 +314,12 @@ display: none; /* Chrome/Safari */ } } + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} diff --git a/src/pages/main/index.tsx b/src/pages/main/index.tsx index ac49d6c2..f76654bc 100644 --- a/src/pages/main/index.tsx +++ b/src/pages/main/index.tsx @@ -35,7 +35,6 @@ function MainApp() { <> - {synthesizeItem && } ); diff --git a/tailwind.config.js b/tailwind.config.js index 527bdcf7..21558a9f 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,4 +1,5 @@ -/** @type {import('tailwindcss').Config} */ +import animate from "tailwindcss-animate"; + export default { content: [ "./index.html", @@ -9,11 +10,11 @@ export default { theme: { extend: { backgroundColor: { - primary: "rgb(var(--color-primary) / )", - secondary: "rgb(var(--color-secondary) / )", - background: "rgb(var(--color-background) / )", - foreground: "rgb(var(--color-foreground) / )", - separator: "rgb(var(--color-separator) / )", + primary: 'rgb(var(--color-primary) / )', + secondary: 'rgb(var(--color-secondary) / )', + background: 'rgb(var(--color-background) / )', + foreground: 'rgb(var(--color-foreground) / )', + separator: 'rgb(var(--color-separator) / )' }, backgroundImage: { chat_bg_light: "url('./assets/chat_bg_light.png')", @@ -24,43 +25,108 @@ export default { inputbox_bg_dark: "url('./assets/inputbox_bg_dark.png')", }, textColor: { - primary: "rgb(var(--color-foreground) / )", + primary: 'rgb(var(--color-foreground) / )' }, animation: { - "fade-in": "fade-in 0.2s ease-in-out", - typing: "typing 1.5s ease-in-out infinite", - shake: "shake 0.5s ease-in-out", + 'fade-in': 'fade-in 0.2s ease-in-out', + typing: 'typing 1.5s ease-in-out infinite', + shake: 'shake 0.5s ease-in-out' }, keyframes: { - "fade-in": { - "0%": { opacity: "0" }, - "100%": { opacity: "1" }, + 'fade-in': { + '0%': { + opacity: '0' + }, + '100%': { + opacity: '1' + } }, typing: { - "0%": { opacity: "0.3" }, - "50%": { opacity: "1" }, - "100%": { opacity: "0.3" }, + '0%': { + opacity: '0.3' + }, + '50%': { + opacity: '1' + }, + '100%': { + opacity: '0.3' + } }, shake: { - "0%, 100%": { transform: "rotate(0deg)" }, - "25%": { transform: "rotate(-20deg)" }, - "75%": { transform: "rotate(20deg)" }, - }, + '0%, 100%': { + transform: 'rotate(0deg)' + }, + '25%': { + transform: 'rotate(-20deg)' + }, + '75%': { + transform: 'rotate(20deg)' + } + } }, boxShadow: { - "window-custom": "0px 16px 32px 0px rgba(0,0,0,0.3)", + 'window-custom': '0px 16px 32px 0px rgba(0,0,0,0.3)' }, zIndex: { - 100: "100", - 1000: "1000", - 2000: "2000", + '100': '100', + '1000': '1000', + '2000': '2000' }, screens: { - mobile: { max: "679px" }, + mobile: { + max: '679px' + } }, - }, + borderRadius: { + lg: 'var(--radius)', + md: 'calc(var(--radius) - 2px)', + sm: 'calc(var(--radius) - 4px)' + }, + colors: { + background: 'hsl(var(--background))', + foreground: 'hsl(var(--foreground))', + card: { + DEFAULT: 'hsl(var(--card))', + foreground: 'hsl(var(--card-foreground))' + }, + popover: { + DEFAULT: 'hsl(var(--popover))', + foreground: 'hsl(var(--popover-foreground))' + }, + primary: { + DEFAULT: 'hsl(var(--primary))', + foreground: 'hsl(var(--primary-foreground))' + }, + secondary: { + DEFAULT: 'hsl(var(--secondary))', + foreground: 'hsl(var(--secondary-foreground))' + }, + muted: { + DEFAULT: 'hsl(var(--muted))', + foreground: 'hsl(var(--muted-foreground))' + }, + accent: { + DEFAULT: 'hsl(var(--accent))', + foreground: 'hsl(var(--accent-foreground))' + }, + destructive: { + DEFAULT: 'hsl(var(--destructive))', + foreground: 'hsl(var(--destructive-foreground))' + }, + border: 'hsl(var(--border))', + input: 'hsl(var(--input))', + ring: 'hsl(var(--ring))', + chart: { + '1': 'hsl(var(--chart-1))', + '2': 'hsl(var(--chart-2))', + '3': 'hsl(var(--chart-3))', + '4': 'hsl(var(--chart-4))', + '5': 'hsl(var(--chart-5))' + } + } + } }, - plugins: [], + plugins: [animate], mode: "jit", darkMode: ["class", '[data-theme="dark"]'], safelist: ["bg-[green]", "bg-[red]", "bg-[yellow]"],