diff --git a/apps/style.css b/apps/style.css deleted file mode 100644 index c364dc4..0000000 --- a/apps/style.css +++ /dev/null @@ -1,260 +0,0 @@ -/* src/app.css or put it where you have your main .css file */ -@import 'tailwindcss'; - -@plugin 'tailwindcss-animate'; -@plugin '@kobalte/tailwindcss'; - -@custom-variant dark (.dark &,[data-kb-theme="dark"] &); - -@theme { - --color-border: var(--border); - --color-input: var(--input); - --color-ring: var(--ring); - --color-background: var(--background); - --color-foreground: var(--foreground); - - --color-primary: var(--primary); - --color-primary-foreground: var(--primary-foreground); - - --color-secondary: var(--secondary); - --color-secondary-foreground: var(--secondary-foreground); - - --color-destructive: var(--destructive); - --color-destructive-foreground: var(--destructive-foreground); - - --color-info: var(--info); - --color-info-foreground: var(--info-foreground); - - --color-success: var(--success); - --color-success-foreground: var(--success-foreground); - - --color-warning: var(--warning); - --color-warning-foreground: var(--warning-foreground); - - --color-error: var(--error); - --color-error-foreground: var(--error-foreground); - - --color-muted: var(--muted); - --color-muted-foreground: var(--muted-foreground); - - --color-accent: var(--accent); - --color-accent-foreground: var(--accent-foreground); - - --color-popover: var(--popover); - --color-popover-foreground: var(--popover-foreground); - - --color-card: var(--card); - --color-card-foreground: var(--card-foreground); - - --radius: 0.5rem; - --radius-xl: calc(var(--radius) + 4px); - --radius-lg: var(--radius); - --radius-md: calc(var(--radius) - 2px); - --radius-sm: calc(var(--radius) - 4px); - - @keyframes accordion-down { - from { - height: 0; - } - to { - height: var(--kb-accordion-content-height); - } - } - - @keyframes accordion-up { - from { - height: var(--kb-accordion-content-height); - } - to { - height: 0; - } - } - - @keyframes content-show { - from { - opacity: 0; - transform: scale(0.96); - } - to { - opacity: 1; - transform: scale(1); - } - } - - @keyframes content-hide { - from { - opacity: 1; - transform: scale(1); - } - to { - opacity: 0; - transform: scale(0.96); - } - } - - @keyframes caret-blink { - 0%, - 70%, - 100% { - opacity: 1; - } - 20%, - 50% { - opacity: 0; - } - } - - --animate-accordion-down: accordion-down 0.2s ease-out; - --animate-accordion-up: accordion-up 0.2s ease-out; - --animate-content-show: content-show 0.2s ease-out; - --animate-content-hide: content-hide 0.2s ease-out; - --animate-caret-blink: caret-blink 1.25s ease-out infinite; -} - -@utility container { - margin-inline: auto; - padding-inline: 2rem; - @media (width >= --theme(--breakpoint-sm)) { - max-width: none; - } - @media (width >= 1400px) { - max-width: 1400px; - } -} - -@utility step { - counter-increment: step; - - &:before { - @apply absolute w-9 h-9 bg-muted rounded-full font-mono font-medium text-center text-base inline-flex items-center justify-center -indent-px border-4 border-background; - @apply ml-[-50px] mt-[-4px]; - content: counter(step); - } -} - -@layer base { - :root { - --color-background: hsl(0 0% 100%); - --color-foreground: hsl(240 10% 3.9%); - - --color-muted: hsl(240 4.8% 95.9%); - --color-muted-foreground: hsl(240 3.8% 46.1%); - - --color-popover: hsl(0, 83%, 30%); - --color-popover-foreground: hsl(240 10% 3.9%); - - --color-border: hsl(240 5.9% 90%); - --color-input: hsl(240 5.9% 90%); - - --color-card: hsl(0 0% 100%); - --color-card-foreground: hsl(240 10% 3.9%); - - --color-primary: hsl(240 5.9% 10%); - --color-primary-foreground: hsl(0 0% 98%); - - --color-secondary: hsl(240 4.8% 95.9%); - --color-secondary-foreground: hsl(240 5.9% 10%); - - --color-accent: hsl(240 4.8% 95.9%); - --color-accent-foreground: hsl(240 5.9% 10%); - - --color-destructive: hsl(0 84.2% 60.2%); - --color-destructive-foreground: hsl(0 0% 98%); - - --color-info: hsl(204 94% 94%); - --color-info-foreground: hsl(199 89% 48%); - - --color-success: hsl(149 80% 90%); - --color-success-foreground: hsl(160 84% 39%); - - --color-warning: hsl(48 96% 89%); - --color-warning-foreground: hsl(25 95% 53%); - - --color-error: hsl(0 93% 94%); - --color-error-foreground: hsl(0 84% 60%); - - --color-ring: hsl(240 5.9% 10%); - } - - .dark, - [data-kb-theme="dark"] { - --color-background: hsl(240 10% 3.9%); - --color-foreground: hsl(0 0% 98%); - - --color-muted: hsl(240 3.7% 15.9%); - --color-muted-foreground: hsl(240 5% 64.9%); - - --color-accent: hsl(240 3.7% 15.9%); - --color-accent-foreground: hsl(0 0% 98%); - - --color-popover: hsl(240 10% 3.9%); - --color-popover-foreground: hsl(0 0% 98%); - - --color-border: hsl(240 3.7% 15.9%); - --color-input: hsl(240 3.7% 15.9%); - - --color-card: hsl(240 10% 3.9%); - --color-card-foreground: hsl(0 0% 98%); - - --color-primary: hsl(0 0% 98%); - --color-primary-foreground: hsl(240 5.9% 10%); - - --color-secondary: hsl(240 3.7% 15.9%); - --color-secondary-foreground: hsl(0 0% 98%); - - --color-destructive: hsl(0 62.8% 30.6%); - --color-destructive-foreground: hsl(0 0% 98%); - - --color-info: hsl(226.2 57% 21%); - --color-info-foreground: hsl(199 89% 48%); - - --color-success: hsl(165.7 91.3% 9%); - --color-success-foreground: hsl(160 84% 39%); - - --color-warning: hsl(26 83.3% 14.1%); - --color-warning-foreground: hsl(25 95% 53%); - - --color-error: hsl(0 74.7% 15.5%); - --color-error-foreground: hsl(0 84% 60%); - - --color-ring: hsl(240 4.9% 83.9%); - } - - * { - @apply border-border; - } - - body { - @apply bg-background text-foreground; - font-feature-settings: "rlig" 1, "calt" 1; - } - - @media (max-width: 640px) { - .container { - @apply px-4; - } - } - - ::-webkit-scrollbar { - width: 16px; - } - - ::-webkit-scrollbar-thumb { - border-radius: 9999px; - border: 4px solid transparent; - background-clip: content-box; - @apply bg-accent; - } - - ::-webkit-scrollbar-corner { - display: none; - } - - *, - ::after, - ::before, - ::backdrop, - ::file-selector-button { - border-color: var(--color-gray-200, currentColor); - } -} diff --git a/apps/webui/package.json b/apps/webui/package.json index 0150fdb..58eeba2 100644 --- a/apps/webui/package.json +++ b/apps/webui/package.json @@ -16,13 +16,15 @@ "@kobalte/core": "^0.13.9", "@kobalte/tailwindcss": "^0.9.0", "@solid-primitives/refs": "^1.1.0", + "@tailwindcss/postcss": "^4.0.9", "@tanstack/solid-router": "^1.112.2", "chart.js": "^4.4.8", "cmdk-solid": "^1.1.2", "embla-carousel-solid": "^8.5.2", + "lucide-solid": "^0.477.0", "solid-js": "^1.9.5", "solid-sonner": "^0.2.8", - "tailwindcss": "3.4.17" + "tailwindcss": "^3" }, "devDependencies": { "@rsbuild/plugin-solid": "^1.0.5", diff --git a/apps/webui/postcss.config.mjs b/apps/webui/postcss.config.mjs index 616868c..684db48 100644 --- a/apps/webui/postcss.config.mjs +++ b/apps/webui/postcss.config.mjs @@ -1,5 +1,5 @@ export default { plugins: { - tailwindcss: {}, + 'tailwindcss': {}, }, }; diff --git a/apps/webui/src/app.css b/apps/webui/src/app.css index e1c56e4..f960de6 100644 --- a/apps/webui/src/app.css +++ b/apps/webui/src/app.css @@ -93,6 +93,23 @@ --radius: 0.5rem; } + + /* custom start */ + ::-webkit-scrollbar { + width: 16px; + } + + ::-webkit-scrollbar-thumb { + border-radius: 9999px; + border: 4px solid transparent; + background-clip: content-box; + @apply bg-accent; + } + + ::-webkit-scrollbar-corner { + display: none; + } + /* custom end */ } @layer base { diff --git a/apps/webui/src/components/layout/app-sidebar.tsx b/apps/webui/src/components/layout/app-sidebar.tsx new file mode 100644 index 0000000..b2a4d53 --- /dev/null +++ b/apps/webui/src/components/layout/app-sidebar.tsx @@ -0,0 +1,174 @@ +import { + AudioWaveform, + BookOpen, + Bot, + ChartPie, + Command, + Frame, + GalleryVerticalEnd, + Map as LucideMap, + Settings2, + SquareTerminal, +} from 'lucide-solid'; +import type { ComponentProps } from 'solid-js'; +import { + Sidebar, + SidebarContent, + SidebarFooter, + SidebarHeader, + SidebarRail, +} from '~/components/ui/sidebar'; +import { NavMain } from './nav-main'; +import { NavProjects } from './nav-projects'; +import { NavUser } from './nav-user'; +import { TeamSwitcher } from './team-switcher'; +// This is sample data. + +const data = { + user: { + name: 'shadcn', + email: 'm@example.com', + avatar: '/avatars/shadcn.jpg', + }, + teams: [ + { + name: 'Acme Inc', + logo: GalleryVerticalEnd, + plan: 'Enterprise', + }, + { + name: 'Acme Corp.', + logo: AudioWaveform, + plan: 'Startup', + }, + { + name: 'Evil Corp.', + logo: Command, + plan: 'Free', + }, + ], + navMain: [ + { + title: 'Playground', + url: '#', + icon: SquareTerminal, + isActive: true, + items: [ + { + title: 'History', + url: '#', + }, + { + title: 'Starred', + url: '#', + }, + { + title: 'Settings', + url: '#', + }, + ], + }, + { + title: 'Models', + url: '#', + icon: Bot, + items: [ + { + title: 'Genesis', + url: '#', + }, + { + title: 'Explorer', + url: '#', + }, + { + title: 'Quantum', + url: '#', + }, + ], + }, + { + title: 'Documentation', + url: '#', + icon: BookOpen, + items: [ + { + title: 'Introduction', + url: '#', + }, + { + title: 'Get Started', + url: '#', + }, + { + title: 'Tutorials', + url: '#', + }, + { + title: 'Changelog', + url: '#', + }, + ], + }, + { + title: 'Settings', + url: '#', + icon: Settings2, + items: [ + { + title: 'General', + url: '#', + }, + { + title: 'Team', + url: '#', + }, + { + title: 'Billing', + url: '#', + }, + { + title: 'Limits', + url: '#', + }, + ], + }, + ], + projects: [ + { + name: 'Design Engineering', + url: '#', + icon: Frame, + }, + { + name: 'Sales & Marketing', + url: '#', + icon: ChartPie, + }, + { + name: 'Travel', + url: '#', + icon: LucideMap, + }, + ], +}; + +type AppSidebarRootProps = Omit, 'collapsible'>; + +export const AppSidebar = (props: AppSidebarRootProps) => { + return ( + + + + + + + + + + + + + + ); +}; diff --git a/apps/webui/src/components/layout/nav-main.tsx b/apps/webui/src/components/layout/nav-main.tsx new file mode 100644 index 0000000..afa7502 --- /dev/null +++ b/apps/webui/src/components/layout/nav-main.tsx @@ -0,0 +1,71 @@ +import { ChevronRight, type LucideIcon } from 'lucide-solid'; +import { For } from 'solid-js'; + +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from '~/components/ui/collapsible'; +import { + SidebarGroup, + SidebarGroupLabel, + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, + SidebarMenuSub, + SidebarMenuSubButton, + SidebarMenuSubItem, +} from '~/components/ui/sidebar'; + +export function NavMain({ + items, +}: { + items: { + title: string; + url: string; + icon?: LucideIcon; + isActive?: boolean; + items?: { + title: string; + url: string; + }[]; + }[]; +}) { + return ( + + Platform + + + {(item) => ( + + + + {item.icon && } + {item.title} + + + + + + {(subItem) => ( + + + {subItem.title} + + + )} + + + + + + )} + + + + ); +} diff --git a/apps/webui/src/components/layout/nav-projects.tsx b/apps/webui/src/components/layout/nav-projects.tsx new file mode 100644 index 0000000..05c86b6 --- /dev/null +++ b/apps/webui/src/components/layout/nav-projects.tsx @@ -0,0 +1,79 @@ +import { + Folder, + Forward, + type LucideIcon, + MoreHorizontal, + Trash2, +} from 'lucide-solid'; +import { For } from 'solid-js'; + +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from '~/components/ui/dropdown-menu'; +import { + SidebarGroup, + SidebarGroupLabel, + SidebarMenu, + SidebarMenuAction, + SidebarMenuButton, + SidebarMenuItem, +} from '~/components/ui/sidebar'; + +export function NavProjects({ + projects, +}: { + projects: { + name: string; + url: string; + icon: LucideIcon; + }[]; +}) { + return ( + + Projects + + + {(item) => ( + + + + {item.name} + + + + + More + + + + + View Project + + + + Share Project + + + + + Delete Project + + + + + )} + + + + + More + + + + + ); +} diff --git a/apps/webui/src/components/layout/nav-user.tsx b/apps/webui/src/components/layout/nav-user.tsx new file mode 100644 index 0000000..ed7932f --- /dev/null +++ b/apps/webui/src/components/layout/nav-user.tsx @@ -0,0 +1,106 @@ +import { + BadgeCheck, + Bell, + ChevronsUpDown, + CreditCard, + LogOut, + Sparkles, +} from 'lucide-solid'; + +import { Avatar, AvatarFallback, AvatarImage } from '~/components/ui/avatar'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from '~/components/ui/dropdown-menu'; +import { + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, + useSidebar, +} from '~/components/ui/sidebar'; + +export function NavUser({ + user, +}: { + user: { + name: string; + email: string; + avatar: string; + }; +}) { + const { isMobile } = useSidebar(); + + return ( + + + + + + + CN + +
+ {user.name} + {user.email} +
+ +
+ + +
+ + + CN + +
+ {user.name} + {user.email} +
+
+
+ + + + + Upgrade to Pro + + + + + + + Account + + + + Billing + + + + Notifications + + + + + + Log out + +
+
+
+
+ ); +} diff --git a/apps/webui/src/components/layout/team-switcher.tsx b/apps/webui/src/components/layout/team-switcher.tsx new file mode 100644 index 0000000..04c5bc9 --- /dev/null +++ b/apps/webui/src/components/layout/team-switcher.tsx @@ -0,0 +1,79 @@ +import { ChevronsUpDown, type LucideIcon, Plus } from 'lucide-solid'; +import { For, createSignal } from 'solid-js'; +import { Dynamic } from 'solid-js/web'; + +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuTrigger, +} from '~/components/ui/dropdown-menu'; +import { + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, +} from '~/components/ui/sidebar'; + +export function TeamSwitcher(props: { + teams: { + name: string; + logo: LucideIcon; + plan: string; + }[]; +}) { + const [activeTeam, setActiveTeam] = createSignal(props.teams[0]); + + const logo = activeTeam().logo; + + return ( + + + + +
+ +
+
+ {activeTeam().name} + {activeTeam().plan} +
+ +
+ + + Teams + + + {(team, index) => ( + setActiveTeam(team)} + class="gap-2 p-2" + > +
+ +
+ {team.name} + ⌘{index() + 1} +
+ )} +
+ + +
+ +
+
Add team
+
+
+
+
+
+ ); +} diff --git a/apps/webui/src/main.tsx b/apps/webui/src/main.tsx index 1fdc859..d147a18 100644 --- a/apps/webui/src/main.tsx +++ b/apps/webui/src/main.tsx @@ -1,3 +1,8 @@ +import { + ColorModeProvider, + ColorModeScript, + createLocalStorageManager, +} from '@kobalte/core'; import { RouterProvider, createRouter } from '@tanstack/solid-router'; import { render } from 'solid-js/web'; import './app.css'; @@ -23,6 +28,18 @@ declare module '@tanstack/solid-router' { // Render the app const rootElement = document.getElementById('root'); +const App = () => { + const storageManager = createLocalStorageManager('vite-ui-theme'); + return ( + <> + + + + + + ); +}; + if (rootElement && !rootElement.innerHTML) { - render(() => , rootElement); + render(() => , rootElement); } diff --git a/apps/webui/src/routes/__root.tsx b/apps/webui/src/routes/__root.tsx index f407156..0566754 100644 --- a/apps/webui/src/routes/__root.tsx +++ b/apps/webui/src/routes/__root.tsx @@ -1,16 +1,16 @@ -import { Link, Outlet, createRootRoute } from '@tanstack/solid-router'; -import { - NavigationMenu, - NavigationMenuItem, - NavigationMenuLink, - NavigationMenuTrigger, -} from '~/components/ui/navigation-menu'; +import { Outlet, createRootRoute } from '@tanstack/solid-router'; +// import { +// NavigationMenu, +// NavigationMenuItem, +// NavigationMenuLink, +// NavigationMenuTrigger, +// } from '~/components/ui/navigation-menu'; export const Route = createRootRoute({ component: () => { return ( <> -
+ {/*
@@ -25,8 +25,7 @@ export const Route = createRootRoute({ -
-
+
*/} ); diff --git a/apps/webui/src/routes/index.tsx b/apps/webui/src/routes/index.tsx index 376b454..4e55341 100644 --- a/apps/webui/src/routes/index.tsx +++ b/apps/webui/src/routes/index.tsx @@ -1,13 +1,56 @@ -import { createFileRoute } from '@tanstack/solid-router' +import { createFileRoute } from '@tanstack/solid-router'; +import { AppSidebar } from '~/components/layout/app-sidebar'; +import { + Breadcrumb, + BreadcrumbItem, + BreadcrumbLink, + BreadcrumbList, + BreadcrumbSeparator, +} from '~/components/ui/breadcrumb'; +import { Separator } from '~/components/ui/separator'; +import { + SidebarInset, + SidebarProvider, + SidebarTrigger, +} from '~/components/ui/sidebar'; export const Route = createFileRoute('/')({ - component: Index, -}) + component: Home, +}); -function Index() { +function Home() { return ( -
-

Welcome Home!

-
- ) + + + +
+
+ + + + + + + +
+
+
+
+
+
+
+
+
+
+ + + ); } diff --git a/package.json b/package.json index 7327bb5..5b81fb7 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "clsx": "^2.1.1", "class-variance-authority": "^0.7.1", "tailwind-merge": "^3.0.2", - "tailwindcss": "3.4.17", + "tailwindcss": "^4.0.9", "tailwindcss-animate": "^1.0.7", "@tanstack/router-devtools": "^1.112.6", "@tanstack/react-router": "^1.112.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ce39ff0..6ad6e3e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -39,11 +39,11 @@ importers: specifier: ^3.0.2 version: 3.0.2 tailwindcss: - specifier: 3.4.17 - version: 3.4.17(ts-node@10.9.2(@types/node@22.13.8)(typescript@5.8.2)) + specifier: ^4.0.9 + version: 4.0.9 tailwindcss-animate: specifier: ^1.0.7 - version: 1.0.7(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@22.13.8)(typescript@5.8.2))) + version: 1.0.7(tailwindcss@4.0.9) devDependencies: '@auto-it/all-contributors': specifier: ^11.3.0 @@ -178,6 +178,9 @@ importers: '@solid-primitives/refs': specifier: ^1.1.0 version: 1.1.0(solid-js@1.9.5) + '@tailwindcss/postcss': + specifier: ^4.0.9 + version: 4.0.9 '@tanstack/solid-router': specifier: ^1.112.2 version: 1.112.2(solid-js@1.9.5) @@ -190,6 +193,9 @@ importers: embla-carousel-solid: specifier: ^8.5.2 version: 8.5.2(solid-js@1.9.5) + lucide-solid: + specifier: ^0.477.0 + version: 0.477.0(solid-js@1.9.5) solid-js: specifier: ^1.9.5 version: 1.9.5 @@ -197,7 +203,7 @@ importers: specifier: ^0.2.8 version: 0.2.8(solid-js@1.9.5) tailwindcss: - specifier: 3.4.17 + specifier: ^3 version: 3.4.17(ts-node@10.9.2(@types/node@22.13.8)(typescript@5.8.2)) devDependencies: '@rsbuild/plugin-solid': @@ -4268,6 +4274,11 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lucide-solid@0.477.0: + resolution: {integrity: sha512-YshJtVmqCghkI0cur+/PgL48kXEhIh1fMLy6pDwex7heMqXS962eNcb/e+mIiTzSFsjg9r2dBKAWT9tX7Ci6mg==} + peerDependencies: + solid-js: ^1.4.7 + magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} @@ -10440,6 +10451,10 @@ snapshots: dependencies: yallist: 3.1.1 + lucide-solid@0.477.0(solid-js@1.9.5): + dependencies: + solid-js: 1.9.5 + magic-string@0.30.17: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 @@ -11595,9 +11610,9 @@ snapshots: tailwind-merge@3.0.2: {} - tailwindcss-animate@1.0.7(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@22.13.8)(typescript@5.8.2))): + tailwindcss-animate@1.0.7(tailwindcss@4.0.9): dependencies: - tailwindcss: 3.4.17(ts-node@10.9.2(@types/node@22.13.8)(typescript@5.8.2)) + tailwindcss: 4.0.9 tailwindcss@3.4.17(ts-node@10.9.2(@types/node@22.13.8)(typescript@5.8.2)): dependencies: