mirror of
https://github.com/khoj-ai/khoj.git
synced 2024-11-27 17:35:07 +01:00
New Agents Page User Interface (#866)
Changes for new agents page - Modernized agent cards - Responsive design to support mobile users - Button for users to create their own agents (coming soon) - Optimized to use tailwind and icon utils - Side panel added for quick access to conversations
This commit is contained in:
parent
52db15706d
commit
5dcac18ba5
8 changed files with 1095 additions and 343 deletions
|
@ -6,7 +6,7 @@ div.titleBar {
|
|||
.agentPersonality p {
|
||||
white-space: inherit;
|
||||
overflow: hidden;
|
||||
height: 78px;
|
||||
height: 77px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,16 @@ div.agentPersonality {
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
div.sidePanel {
|
||||
position: fixed;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
div.chatLayout {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
|
||||
button.infoButton {
|
||||
|
@ -30,7 +40,7 @@ button.infoButton {
|
|||
div.agentList {
|
||||
display: grid;
|
||||
gap: 20px;
|
||||
padding: 20px;
|
||||
padding-top: 30px;
|
||||
margin-right: auto;
|
||||
grid-auto-flow: row;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
|
@ -40,7 +50,7 @@ div.agentList {
|
|||
|
||||
@media only screen and (max-width: 700px) {
|
||||
div.agentList {
|
||||
width: 90%;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
.agentsLayout {
|
||||
max-width: 70vw;
|
||||
max-width: 100vw;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,27 +1,34 @@
|
|||
|
||||
import type { Metadata } from "next";
|
||||
import NavMenu from '../components/navMenu/navMenu';
|
||||
import styles from './agentsLayout.module.css';
|
||||
|
||||
import { Noto_Sans } from "next/font/google";
|
||||
import "../globals.css";
|
||||
|
||||
const inter = Noto_Sans({ subsets: ["latin"] });
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Khoj AI - Agents",
|
||||
description: "Use Agents with Khoj AI for deeper, more personalized queries.",
|
||||
icons: {
|
||||
icon: '/static/favicon.ico',
|
||||
},
|
||||
title: "Khoj AI - Chat",
|
||||
description: "Use this page to chat with Khoj AI.",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<div className={`${styles.agentsLayout}`}>
|
||||
<NavMenu selected="Agents" showLogo={true} />
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<html lang="en">
|
||||
<meta httpEquiv="Content-Security-Policy"
|
||||
content="default-src 'self' https://assets.khoj.dev;
|
||||
media-src * blob:;
|
||||
script-src 'self' https://assets.khoj.dev 'unsafe-inline' 'unsafe-eval';
|
||||
connect-src 'self' https://ipapi.co/json ws://localhost:42110;
|
||||
style-src 'self' https://assets.khoj.dev 'unsafe-inline' https://fonts.googleapis.com;
|
||||
img-src 'self' data: https://*.khoj.dev https://*.googleusercontent.com https://*.google.com/ https://*.gstatic.com;
|
||||
font-src 'self' https://assets.khoj.dev https://fonts.gstatic.com;
|
||||
child-src 'none';
|
||||
object-src 'none';"></meta>
|
||||
<body className={inter.className}>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
import styles from './agents.module.css';
|
||||
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
|
@ -11,47 +10,31 @@ import { useEffect, useState } from 'react';
|
|||
import { useAuthenticatedData, UserProfile } from '../common/auth';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Lightbulb,
|
||||
Robot,
|
||||
Aperture,
|
||||
GraduationCap,
|
||||
Jeep,
|
||||
Island,
|
||||
MathOperations,
|
||||
Asclepius,
|
||||
Couch,
|
||||
Code,
|
||||
Atom,
|
||||
ClockCounterClockwise,
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
} from "@/components/ui/alert-dialog"
|
||||
|
||||
import {
|
||||
PaperPlaneTilt,
|
||||
Info,
|
||||
UserCircle
|
||||
Lightning,
|
||||
Plus,
|
||||
} from "@phosphor-icons/react";
|
||||
|
||||
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader, DialogTrigger } from '@/components/ui/dialog';
|
||||
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTrigger } from '@/components/ui/dialog';
|
||||
import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger } from '@/components/ui/drawer';
|
||||
import LoginPrompt from '../components/loginPrompt/loginPrompt';
|
||||
import Loading, { InlineLoading } from '../components/loading/loading';
|
||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
||||
|
||||
interface IconMap {
|
||||
[key: string]: (color: string, width: string, height: string) => JSX.Element | null;
|
||||
}
|
||||
|
||||
const iconMap: IconMap = {
|
||||
Lightbulb: (color: string, width: string, height: string) => <Lightbulb className={`${width} ${height} ${color} mr-2`} />,
|
||||
Robot: (color: string, width: string, height: string) => <Robot className={`${width} ${height} ${color} mr-2`} />,
|
||||
Aperture: (color: string, width: string, height: string) => <Aperture className={`${width} ${height} ${color} mr-2`} />,
|
||||
GraduationCap: (color: string, width: string, height: string) => <GraduationCap className={`${width} ${height} ${color} mr-2`} />,
|
||||
Jeep: (color: string, width: string, height: string) => <Jeep className={`${width} ${height} ${color} mr-2`} />,
|
||||
Island: (color: string, width: string, height: string) => <Island className={`${width} ${height} ${color} mr-2`} />,
|
||||
MathOperations: (color: string, width: string, height: string) => <MathOperations className={`${width} ${height} ${color} mr-2`} />,
|
||||
Asclepius: (color: string, width: string, height: string) => <Asclepius className={`${width} ${height} ${color} mr-2`} />,
|
||||
Couch: (color: string, width: string, height: string) => <Couch className={`${width} ${height} ${color} mr-2`} />,
|
||||
Code: (color: string, width: string, height: string) => <Code className={`${width} ${height} ${color} mr-2`} />,
|
||||
Atom: (color: string, width: string, height: string) => <Atom className={`${width} ${height} ${color} mr-2`} />,
|
||||
ClockCounterClockwise: (color: string, width: string, height: string) => <ClockCounterClockwise className={`${width} ${height} ${color} mr-2`} />,
|
||||
};
|
||||
import { InlineLoading } from '../components/loading/loading';
|
||||
import SidePanel from '../components/sidePanel/chatHistorySidePanel';
|
||||
import NavMenu from '../components/navMenu/navMenu';
|
||||
import { getIconFromIconName } from '../common/iconUtils';
|
||||
import { convertColorToTextClass } from '../common/colorUtils';
|
||||
|
||||
export interface AgentData {
|
||||
slug: string;
|
||||
|
@ -83,62 +66,12 @@ async function openChat(slug: string, userData: UserProfile | null) {
|
|||
|
||||
const agentsFetcher = () => window.fetch('/api/agents').then(res => res.json()).catch(err => console.log(err));
|
||||
|
||||
|
||||
interface AgentCardProps {
|
||||
data: AgentData;
|
||||
userProfile: UserProfile | null;
|
||||
isMobileWidth: boolean;
|
||||
}
|
||||
|
||||
function getIconFromIconName(iconName: string, color: string = 'gray', width: string = 'w-8', height: string = 'h-8') {
|
||||
const icon = iconMap[iconName];
|
||||
const colorName = color.toLowerCase();
|
||||
const colorClass = convertColorToTextClass(colorName);
|
||||
|
||||
return icon ? icon(colorClass, width, height) : null;
|
||||
}
|
||||
|
||||
function convertColorToClass(color: string) {
|
||||
// We can't dyanmically generate the classes for tailwindcss, so we have to explicitly use the whole string.
|
||||
// See models/__init__.py 's definition of the Agent model for the color choices.
|
||||
if (color === 'red') return `bg-red-500 hover:bg-red-600`;
|
||||
if (color === 'yellow') return `bg-yellow-500 hover:bg-yellow-600`;
|
||||
if (color === 'green') return `bg-green-500 hover:bg-green-600`;
|
||||
if (color === 'blue') return `bg-blue-500 hover:bg-blue-600`;
|
||||
if (color === 'orange') return `bg-orange-500 hover:bg-orange-600`;
|
||||
if (color === 'purple') return `bg-purple-500 hover:bg-purple-600`;
|
||||
if (color === 'pink') return `bg-pink-500 hover:bg-pink-600`;
|
||||
if (color === 'teal') return `bg-teal-500 hover:bg-teal-600`;
|
||||
if (color === 'cyan') return `bg-cyan-500 hover:bg-cyan-600`;
|
||||
if (color === 'lime') return `bg-lime-500 hover:bg-lime-600`;
|
||||
if (color === 'indigo') return `bg-indigo-500 hover:bg-indigo-600`;
|
||||
if (color === 'fuschia') return `bg-fuschia-500 hover:bg-fuschia-600`;
|
||||
if (color === 'rose') return `bg-rose-500 hover:bg-rose-600`;
|
||||
if (color === 'sky') return `bg-sky-500 hover:bg-sky-600`;
|
||||
if (color === 'amber') return `bg-amber-500 hover:bg-amber-600`;
|
||||
if (color === 'emerald') return `bg-emerald-500 hover:bg-emerald-600`;
|
||||
return `bg-gray-500 hover:bg-gray-600`;
|
||||
}
|
||||
|
||||
function convertColorToTextClass(color: string) {
|
||||
if (color === 'red') return `text-red-500`;
|
||||
if (color === 'yellow') return `text-yellow-500`;
|
||||
if (color === 'green') return `text-green-500`;
|
||||
if (color === 'blue') return `text-blue-500`;
|
||||
if (color === 'orange') return `text-orange-500`;
|
||||
if (color === 'purple') return `text-purple-500`;
|
||||
if (color === 'pink') return `text-pink-500`;
|
||||
if (color === 'teal') return `text-teal-500`;
|
||||
if (color === 'cyan') return `text-cyan-500`;
|
||||
if (color === 'lime') return `text-lime-500`;
|
||||
if (color === 'indigo') return `text-indigo-500`;
|
||||
if (color === 'fuschia') return `text-fuschia-500`;
|
||||
if (color === 'rose') return `text-rose-500`;
|
||||
if (color === 'sky') return `text-sky-500`;
|
||||
if (color === 'amber') return `text-amber-500`;
|
||||
if (color === 'emerald') return `text-emerald-500`;
|
||||
return `text-gray-500`;
|
||||
}
|
||||
|
||||
function AgentCard(props: AgentCardProps) {
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
|
@ -152,10 +85,10 @@ function AgentCard(props: AgentCardProps) {
|
|||
window.history.pushState({}, `Khoj AI - Agent ${props.data.slug}`, `/agents?agent=${props.data.slug}`);
|
||||
}
|
||||
|
||||
const stylingString = convertColorToClass(props.data.color);
|
||||
const stylingString = convertColorToTextClass(props.data.color);
|
||||
|
||||
return (
|
||||
<Card className='shadow-md bg-secondary rounded-lg hover:shadow-lg'>
|
||||
<Card className={`shadow-sm bg-gradient-to-b from-white 20% to-${props.data.color ? props.data.color : "gray"}-100/50 dark:from-[hsl(var(--background))] dark:to-${props.data.color ? props.data.color : "gray"}-950/50 rounded-xl hover:shadow-md`}>
|
||||
{
|
||||
showLoginPrompt &&
|
||||
<LoginPrompt
|
||||
|
@ -173,7 +106,7 @@ function AgentCard(props: AgentCardProps) {
|
|||
window.history.pushState({}, `Khoj AI - Agents`, `/agents`);
|
||||
}}>
|
||||
<DialogTrigger>
|
||||
<div className='flex items-center'>
|
||||
<div className='flex items-center relative top-2'>
|
||||
{
|
||||
getIconFromIconName(props.data.icon, props.data.color) || <Image
|
||||
src={props.data.avatar}
|
||||
|
@ -185,7 +118,22 @@ function AgentCard(props: AgentCardProps) {
|
|||
{props.data.name}
|
||||
</div>
|
||||
</DialogTrigger>
|
||||
<DialogContent className='whitespace-pre-line'>
|
||||
<div className="float-right">
|
||||
{props.userProfile ? (
|
||||
<Button
|
||||
className={`bg-[hsl(var(--background))] w-14 h-14 rounded-xl border dark:border-neutral-700 shadow-sm hover:bg-stone-100 dark:hover:bg-neutral-900`}
|
||||
onClick={() => openChat(props.data.slug, userData)}>
|
||||
<PaperPlaneTilt className='w-6 h-6' color={props.data.color} />
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
className={`bg-[hsl(var(--background))] w-14 h-14 rounded-xl border dark:border-neutral-700 shadow-sm hover:bg-stone-100 dark:hover:bg-neutral-900`}
|
||||
onClick={() => setShowLoginPrompt(true)}>
|
||||
<PaperPlaneTilt className='w-6 h-6' color={props.data.color} />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<DialogContent className='whitespace-pre-line max-h-[80vh]'>
|
||||
<DialogHeader>
|
||||
<div className='flex items-center'>
|
||||
{
|
||||
|
@ -196,18 +144,21 @@ function AgentCard(props: AgentCardProps) {
|
|||
height={50}
|
||||
/>
|
||||
}
|
||||
{props.data.name}
|
||||
<p className="font-bold text-lg">{props.data.name}</p>
|
||||
</div>
|
||||
</DialogHeader>
|
||||
{props.data.personality}
|
||||
<div className="max-h-[60vh] overflow-y-scroll text-neutral-500 dark:text-white">
|
||||
{props.data.personality}
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
className={`${stylingString}`}
|
||||
className={`pt-6 pb-6 ${stylingString} bg-white dark:bg-[hsl(var(--background))] text-neutral-500 dark:text-white border-2 border-stone-100 shadow-sm rounded-xl hover:bg-stone-100 dark:hover:bg-neutral-900 dark:border-neutral-700`}
|
||||
onClick={() => {
|
||||
openChat(props.data.slug, userData);
|
||||
setShowModal(false);
|
||||
}}>
|
||||
Chat
|
||||
<PaperPlaneTilt className='mr-2 w-6 h-6' color={props.data.color} />
|
||||
Start Chatting
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
|
@ -232,6 +183,21 @@ function AgentCard(props: AgentCardProps) {
|
|||
{props.data.name}
|
||||
</div>
|
||||
</DrawerTrigger>
|
||||
<div className="float-right">
|
||||
{props.userProfile ? (
|
||||
<Button
|
||||
className={`bg-[hsl(var(--background))] w-14 h-14 rounded-xl border dark:border-neutral-700 shadow-sm hover:bg-stone-100`}
|
||||
onClick={() => openChat(props.data.slug, userData)}>
|
||||
<PaperPlaneTilt className='w-6 h-6' color={props.data.color} />
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
className={`bg-[hsl(var(--background))] w-14 h-14 rounded-xl border dark:border-neutral-700 shadow-sm`}
|
||||
onClick={() => setShowLoginPrompt(true)}>
|
||||
<PaperPlaneTilt className='w-6 h-6' color={props.data.color} />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<DrawerContent className='whitespace-pre-line p-2'>
|
||||
<DrawerHeader>
|
||||
<DrawerTitle>{props.data.name}</DrawerTitle>
|
||||
|
@ -250,31 +216,20 @@ function AgentCard(props: AgentCardProps) {
|
|||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className={styles.agentPersonality}>
|
||||
<button className={styles.infoButton} onClick={() => setShowModal(true)}>
|
||||
<button className={`${styles.infoButton} text-neutral-500 dark:text-white`} onClick={() => setShowModal(true)}>
|
||||
<p>{props.data.personality}</p>
|
||||
</button>
|
||||
</div>
|
||||
</CardContent>
|
||||
<CardFooter className='flex justify-end'>
|
||||
{
|
||||
props.userProfile ?
|
||||
<Button
|
||||
className={`${stylingString}`}
|
||||
onClick={() => openChat(props.data.slug, userData)}>
|
||||
<PaperPlaneTilt className='w-6 h-6' />
|
||||
</Button>
|
||||
:
|
||||
<Button
|
||||
className={`${stylingString}`}
|
||||
onClick={() => setShowLoginPrompt(true)}>
|
||||
<PaperPlaneTilt className='w-6 h-6' />
|
||||
</Button>
|
||||
}
|
||||
</CardFooter>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
function createAgent() {
|
||||
//just show a dialog for now similar to the agent card when the text is pressed
|
||||
}
|
||||
|
||||
|
||||
export default function Agents() {
|
||||
const { data, error } = useSWR<AgentData[]>('agents', agentsFetcher, { revalidateOnFocus: false });
|
||||
const authenticatedData = useAuthenticatedData();
|
||||
|
@ -318,41 +273,78 @@ export default function Agents() {
|
|||
}
|
||||
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
<h3
|
||||
className='text-xl py-4'>
|
||||
Agents
|
||||
</h3>
|
||||
<main className={`${styles.main} w-full ml-auto mr-auto`}>
|
||||
<div className="float-right w-fit h-fit">
|
||||
<NavMenu selected="Agents" />
|
||||
</div>
|
||||
{
|
||||
showLoginPrompt &&
|
||||
<LoginPrompt
|
||||
loginRedirectMessage="Sign in to start chatting with a specialized agent"
|
||||
onOpenChange={setShowLoginPrompt} />
|
||||
}
|
||||
|
||||
<Alert>
|
||||
<Info className="h-4 w-4" />
|
||||
<AlertTitle>How this works</AlertTitle>
|
||||
<AlertDescription>
|
||||
You can use any of these specialized agents to tailor to tune your conversation to your needs.
|
||||
{
|
||||
!authenticatedData &&
|
||||
<>
|
||||
<div className='mt-3' />
|
||||
<Button onClick={() => setShowLoginPrompt(true)}>
|
||||
<UserCircle className='w-4 h-4 mr-2' /> Sign In
|
||||
</Button>
|
||||
</>
|
||||
|
||||
}
|
||||
<div className='mt-3' />
|
||||
<strong>Coming Soon:</strong> Support for making your own agents.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
<div className={styles.agentList}>
|
||||
{data.map(agent => (
|
||||
<AgentCard key={agent.slug} data={agent} userProfile={authenticatedData} isMobileWidth={isMobileWidth} />
|
||||
))}
|
||||
<div className={`${styles.chatLayout} w-full ml-auto mr-auto`}>
|
||||
<div className={`${styles.sidePanel} top-0`}>
|
||||
<SidePanel
|
||||
webSocketConnected={true}
|
||||
conversationId={null}
|
||||
uploadedFiles={[]}
|
||||
isMobileWidth={isMobileWidth}
|
||||
/>
|
||||
</div>
|
||||
<div className={`ml-auto mr-auto ${isMobileWidth ? "w-11/12" : "w-1/2"} pt-10`}>
|
||||
<div className="pt-8 flex">
|
||||
<h1 className="text-3xl relative top-2">Agents</h1>
|
||||
<div className="ml-auto float-right">
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button
|
||||
className={`bg-[hsl(var(--background))] rounded-xl border dark:border-neutral-700 shadow-sm h-14 hover:bg-stone-100 dark:hover:bg-neutral-900`}
|
||||
onClick={() => createAgent()}
|
||||
>
|
||||
<Plus className='w-6 h-6' color='gray' />
|
||||
<p className="text-black dark:text-white ml-2">
|
||||
<strong>Create Agent</strong>
|
||||
</p>
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Custom Agents</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
Custom Agents will be coming to Khoj soon!
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogAction asChild>
|
||||
<Button className="bg-stone-100 dark:bg-[hsl(var(--background))] text-neutral-500 dark:text-white hover:bg-stone-100 dark:hover:bg-neutral-900" onClick={() => { }}>
|
||||
Close
|
||||
</Button>
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Card className={`mt-8 mb-6 pt-1 pb-1 bg-stone-100 dark:bg-[hsl(var(--background))]`}>
|
||||
<CardContent>
|
||||
<CardDescription className="flex flex-rows">
|
||||
<Lightning className='w-4 h-4 mr-2 relative top-3' weight="fill" color="#a068f5" />
|
||||
<p className="relative top-3">
|
||||
<strong className="text-black dark:text-white pr-2">How it works</strong>
|
||||
Use any of these specialized agents to tune your conversation to your needs.
|
||||
</p>
|
||||
</CardDescription>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
<div className={`${styles.agentList}`}>
|
||||
{data.map(agent => (
|
||||
<AgentCard key={agent.slug} data={agent} userProfile={authenticatedData} isMobileWidth={isMobileWidth} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
|
|
|
@ -700,6 +700,10 @@ export default function SidePanel(props: SidePanelProps) {
|
|||
}
|
||||
}, [chatSessions]);
|
||||
|
||||
function newConvo() {
|
||||
window.location.href = '/';
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`${styles.panel} ${enabled ? styles.expanded : styles.collapsed}`}>
|
||||
<div className={`flex items-center justify-between ${(enabled || props.isMobileWidth) ? 'flex-row' : 'flex-col'}`}>
|
||||
|
|
|
@ -8,8 +8,6 @@ import {
|
|||
} from "@/components/ui/card"
|
||||
|
||||
import styles from "./suggestions.module.css";
|
||||
|
||||
|
||||
import { getIconFromIconName } from "@/app/common/iconUtils";
|
||||
|
||||
|
||||
|
|
|
@ -2,6 +2,10 @@ import type { Config } from "tailwindcss"
|
|||
|
||||
const config = {
|
||||
safelist: [
|
||||
{
|
||||
pattern: /to-(blue|yellow|green|pink|purple|orange|red)-(50|100|200|950)/,
|
||||
variants: ['dark'],
|
||||
},
|
||||
],
|
||||
darkMode: ["class"],
|
||||
content: [
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue