feat(frontend): dark mode (#2369)

# Description

Please include a summary of the changes and the related issue. Please
also include relevant motivation and context.

## Checklist before requesting a review

Please delete options that are not relevant.

- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my code
- [ ] I have commented hard-to-understand areas
- [ ] I have ideally added tests that prove my fix is effective or that
my feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged

## Screenshots (if appropriate):
This commit is contained in:
Antoine Dewez 2024-03-21 00:01:21 -07:00 committed by GitHub
parent d42323d34a
commit d7d1a0155b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
75 changed files with 584 additions and 415 deletions

View File

@ -20,6 +20,7 @@ import { ChatsProvider } from "@/lib/context/ChatsProvider";
import { MenuProvider } from "@/lib/context/MenuProvider/Menu-provider";
import { SearchModalProvider } from "@/lib/context/SearchModalProvider/search-modal-provider";
import { useSupabase } from "@/lib/context/SupabaseProvider";
import { UserSettingsProvider } from "@/lib/context/UserSettingsProvider/User-settings.provider";
import { IntercomProvider } from "@/lib/helpers/intercom/IntercomProvider";
import { UpdateMetadata } from "@/lib/helpers/updateMetadata";
import { usePageTracking } from "@/services/analytics/june/usePageTracking";
@ -90,19 +91,21 @@ const queryClient = new QueryClient();
const AppWithQueryClient = ({ children }: PropsWithChildren): JSX.Element => {
return (
<QueryClientProvider client={queryClient}>
<BrainProvider>
<KnowledgeToFeedProvider>
<BrainCreationProvider>
<MenuProvider>
<ChatsProvider>
<ChatProvider>
<App>{children}</App>
</ChatProvider>
</ChatsProvider>
</MenuProvider>
</BrainCreationProvider>
</KnowledgeToFeedProvider>
</BrainProvider>
<UserSettingsProvider>
<BrainProvider>
<KnowledgeToFeedProvider>
<BrainCreationProvider>
<MenuProvider>
<ChatsProvider>
<ChatProvider>
<App>{children}</App>
</ChatProvider>
</ChatsProvider>
</MenuProvider>
</BrainCreationProvider>
</KnowledgeToFeedProvider>
</BrainProvider>
</UserSettingsProvider>
</QueryClientProvider>
);
};

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/Spacings.module.scss";
@use "@/styles/Typography.module.scss";
@ -15,7 +14,7 @@
&:hover,
&.selected {
background-color: Colors.$primary-light;
background-color: var(--background-special-1);
font-weight: 550;
}

View File

@ -1,13 +1,13 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/BoxShadow.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/Spacings.module.scss";
.mentions_list_wrapper {
display: flex;
background-color: Colors.$white;
background-color: var(--background-0);
flex-direction: column;
padding: Spacings.$spacing03;
box-shadow: 0 1px 2px rgb(0, 0, 0, 0.25);
box-shadow: BoxShadow.$small;
border-radius: Radius.$normal;
gap: Spacings.$spacing03;
max-width: 300px;

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/IconSizes.module.scss";
@use "@/styles/Spacings.module.scss";
@ -8,6 +7,6 @@
cursor: pointer;
&:hover {
color: Colors.$accent;
color: var(--accent);
}
}

View File

@ -1,14 +1,13 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/Spacings.module.scss";
.chat_container {
display: flex;
flex-direction: column;
background-color: Colors.$white;
background-color: var(--background-0);
gap: Spacings.$spacing03;
border-radius: Radius.$big;
border: 1px solid Colors.$lighter-grey;
border: 1px solid var(--border-0);
overflow: hidden;
.chat_wrapper {

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/ScreenSizes.module.scss";
@use "@/styles/Spacings.module.scss";
@use "@/styles/Typography.module.scss";
@ -26,7 +25,7 @@
}
.uploaded_knowledges_title {
color: Colors.$dark-grey;
color: var(--text-2);
display: flex;
justify-content: space-between;
}

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/ScreenSizes.module.scss";
@use "@/styles/Spacings.module.scss";
@ -11,14 +10,14 @@
column-gap: Spacings.$spacing05;
justify-content: center;
align-items: center;
border: 1px dashed Colors.$lighter-grey;
border: 1px dashed var(--border-0);
border-radius: Radius.$big;
box-sizing: border-box;
cursor: pointer;
&.dragging {
border: 3px dashed Colors.$accent;
background-color: Colors.$lightest-black;
border: 3px dashed var(--accent);
background-color: var(--background-3);
}
.input {

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/Spacings.module.scss";
@ -30,11 +29,11 @@
display: flex;
gap: Spacings.$spacing02;
align-items: center;
color: Colors.$dark-grey;
color: var(--text-2);
}
.message_row_content {
background-color: Colors.$lightest-grey;
background-color: var(--background-2);
}
}
@ -47,7 +46,7 @@
.message_row_content {
align-self: flex-start;
background-color: Colors.$primary-lightest;
background-color: var(--background-special-0);
margin-left: 1px;
position: relative;

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Spacings.module.scss";
@use "@/styles/Typography.module.scss";

View File

@ -1,4 +1,4 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/Spacings.module.scss";
@use "@/styles/Typography.module.scss";
@ -6,9 +6,13 @@
display: flex;
align-items: center;
gap: Spacings.$spacing02;
color: Colors.$black;
color: var(--text-3);
overflow: hidden;
.dark_image {
filter: invert(100%);
}
.brain_name {
@include Typography.EllipsisOverflow;
}

View File

@ -2,7 +2,7 @@ import Image from "next/image";
import { Fragment, useEffect, useState } from "react";
import { useBrainApi } from "@/lib/api/brain/useBrainApi";
import Icon from "@/lib/components/ui/Icon/Icon";
import { useUserSettingsContext } from "@/lib/context/UserSettingsProvider/hooks/useUserSettingsContext";
import styles from "./QuestionBrain.module.scss";
@ -17,7 +17,7 @@ export const QuestionBrain = ({
const [brainLogoUrl, setBrainLogoUrl] = useState<string | undefined>(
undefined
);
const { isDarkMode } = useUserSettingsContext();
const { getBrain } = useBrainApi();
const getBrainLogoUrl = async () => {
@ -41,11 +41,13 @@ export const QuestionBrain = ({
return (
<div data-testid="brain-tags" className={styles.brain_name_wrapper}>
{brainLogoUrl ? (
<Image src={brainLogoUrl} alt="brainLogo" width={18} height={18} />
) : (
<Icon name="brain" color="primary" size="normal" />
)}
<Image
className={isDarkMode ? styles.dark_image : ""}
src={brainLogoUrl ? brainLogoUrl : "/default_brain_image.png"}
alt="logo_image"
width={18}
height={18}
/>
<span className={styles.brain_name}>{brainName}</span>
</div>
);

View File

@ -1,11 +1,10 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Spacings.module.scss";
@use "@/styles/Typography.module.scss";
.prompt_name_wrapper {
display: flex;
align-items: center;
color: Colors.$black;
color: var(--text-3);
font-size: Typography.$small;
overflow: hidden;

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/Spacings.module.scss";
@use "@/styles/Typography.module.scss";
@ -11,7 +10,7 @@
gap: Spacings.$spacing03;
overflow: hidden;
height: 100%;
border: 1px solid Colors.$lightest-black;
border: 1px solid var(--border-1);
border-radius: Radius.$big;
.title_wrapper {

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Spacings.module.scss";
.main_container {
@ -9,7 +8,7 @@
.chat_page_container {
display: flex;
flex: 1 1 0%;
background-color: Colors.$white;
background-color: var(--background-0);
padding-block: Spacings.$spacing06;
padding-inline: Spacings.$spacing09;
display: flex;

48
frontend/app/colors.css Normal file
View File

@ -0,0 +1,48 @@
:root {
/* White */
--white-0: #ffffff;
--white-1: #fafafa;
--white-2: #fcfaf6;
/* Black */
--black-0: #111111;
--black-1: #171717;
--black-2: #1c1c1c;
--black-3: #222222;
--black-4: #272727;
--black-5: #2d2d2d;
--black-6: #323232;
--black-7: #383838;
/* Grey */
--grey-O: #707070;
--grey-1: #c8c8c8;
--grey-2: #cbcbcb;
--grey-3: #e7e9ec;
--grey-4: #d3d3d3;
--grey-5: #f5f5f5;
/* Primary */
--primary-0: #6142d4;
--primary-1: #d0c6f2;
--primary-2: #f5f3fd;
/* Accent */
--accent: #13abba;
/* Gold */
--gold: #b8860b;
/* Error */
--dangerous-dark: #e30c17;
--dangerous: #9b373c;
--dangerous-lightest: #eedddd;
/* Warning */
--warning: #c77d33;
--warning-lightest: #e9d9c9;
/* Success */
--success: #47a455;
--success-lightest: #d0edd4;
}

View File

@ -1,6 +1,7 @@
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
@import './colors.css';
main {
@apply max-w-screen-xl mx-auto flex flex-col;
@ -48,3 +49,70 @@ div:focus {
@apply h-[100vh] supports-[height:100cqh]:h-[100cqh] supports-[height:100svh]:h-[100svh];
}
}
:root {
/* Backgrounds */
--background-0: var(--white-0);
--background-1: var(--white-1);
--background-2: var(--grey-5);
--background-3: var(--grey-3);
--background-4: var(--grey-0);
--background-5: var(--black-0);
--background-special-0: var(--primary-2);
--background-special-1: var(--primary-1);
--background-blur: rgba(0, 0, 0, 0.9);
/* Borders */
--border-0: var(--grey-4);
--border-1: var(--grey-3);
--border-2: var(--grey-1);
/* Icons */
--icon-0: var(--white-0);
--icon-1: var(--grey-1);
--icon-2: var(--grey-0);
--icon-3: var(--black-0);
/* Text */
--text-0: var(--white-0);
--text-1: var(--grey-1);
--text-2: var(--grey-0);
--text-3: var(--black-0);
/* Box Shadow */
--box-shadow: rgba(0, 0, 0, 0.25);
}
body.dark_mode {
/* Backgrounds */
--background-0: var(--black-0);
--background-1: var(--black-1);
--background-2: var(--black-2);
--background-3: var(--black-3);
--background-4: var(--black-4);
--background-5: var(--black-5);
--background-special-0: var(--black-3);
--background-special-1: var(--black-5);
--background-opposite: var(--white-0);
--background-blur: rgba(0, 0, 0, 0.9);
/* Borders */
--border-0: var(--white-0);
--border-1: var(--grey-1);
--border-2: var(--grey-2);
/* Icons */
--icon-0: var(--black-0);
--icon-1: var(--grey-0);
--icon-2: var(--grey-1);
--icon-3: var(--white-0);
/* Text */
--text-0: var(--black-0);
--text-1: var(--grey-0);
--text-2: var(--grey-1);
--text-3: var(--white-0);
/* Box Shadow */
--box-shadow: rgba(255, 255, 255, 0.25);
}

View File

@ -0,0 +1,8 @@
.body {
color: var(--text-3);
background-color: var(--background-0);
display: flex;
flex-direction: column;
width: 100%;
height: 100vh;
}

View File

@ -1,6 +1,5 @@
import { createServerComponentSupabaseClient } from "@supabase/auth-helpers-nextjs";
import { Analytics as VercelAnalytics } from "@vercel/analytics/react";
import { Outfit } from "next/font/google";
import { cookies, headers } from "next/headers";
import { ToastProvider } from "@/lib/components/ui/Toast";
@ -8,8 +7,7 @@ import { SupabaseProvider } from "@/lib/context/SupabaseProvider";
import { App } from "./App";
import "./globals.css";
const inter = Outfit({ subsets: ["latin"] });
import styles from "./layout.module.scss";
export const metadata = {
title: "Quivr - Get a Second Brain with Generative AI",
@ -34,7 +32,8 @@ const RootLayout = async ({
return (
<html lang="en">
<body
className={`bg-white text-black h-screen flex flex-col dark:bg-black dark:text-white w-full ${inter.className}`}
className={styles.body}
// className={`bg-white text-black h-screen flex flex-col dark:bg-black dark:text-white w-full ${inter.className}`}
>
<ToastProvider>
<SupabaseProvider session={session}>

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/IconSizes.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/ScreenSizes.module.scss";
@ -17,7 +16,7 @@
}
.search_page_container {
background-color: Colors.$white;
background-color: var(--background-0);
width: 100%;
height: 100%;
display: flex;
@ -48,14 +47,14 @@
@include Typography.Big;
.quivr_text_primary {
color: Colors.$primary;
color: var(--primary-0);
}
}
}
}
.shortcuts_card_wrapper {
background-color: Colors.$lightest-grey;
background-color: var(--background-2);
padding: Spacings.$spacing05;
gap: Spacings.$spacing03;
border-radius: Radius.$big;
@ -66,7 +65,7 @@
gap: Spacings.$spacing02;
.shortcut {
color: Colors.$primary;
color: var(--primary-0);
}
}
}

View File

@ -9,6 +9,7 @@ import PageHeader from "@/lib/components/PageHeader/PageHeader";
import { UploadDocumentModal } from "@/lib/components/UploadDocumentModal/UploadDocumentModal";
import { SearchBar } from "@/lib/components/ui/SearchBar/SearchBar";
import { useSupabase } from "@/lib/context/SupabaseProvider";
import { useUserSettingsContext } from "@/lib/context/UserSettingsProvider/hooks/useUserSettingsContext";
import { redirectToLogin } from "@/lib/router/redirectToLogin";
import { ButtonType } from "@/lib/types/QuivrButton";
@ -18,6 +19,7 @@ const Search = (): JSX.Element => {
const pathname = usePathname();
const { session } = useSupabase();
const { setIsBrainCreationModalOpened } = useBrainCreationContext();
const { isDarkMode } = useUserSettingsContext();
useEffect(() => {
if (session === null) {
@ -44,7 +46,7 @@ const Search = (): JSX.Element => {
<div className={styles.search_page_container}>
<div className={styles.main_wrapper}>
<div className={styles.quivr_logo_wrapper}>
<QuivrLogo size={80} color="black" />
<QuivrLogo size={80} color={isDarkMode ? "white" : "black"} />
<div className={styles.quivr_text}>
<span>Talk to </span>
<span className={styles.quivr_text_primary}>Quivr</span>

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/ScreenSizes.module.scss";
@use "@/styles/Spacings.module.scss";
@ -14,15 +13,19 @@
align-items: center;
cursor: pointer;
margin-block: Spacings.$spacing03;
border: 1px solid Colors.$lightest-black;
border: 1px solid var(--border-1);
border-radius: Radius.$normal;
padding-block: Spacings.$spacing03;
position: relative;
overflow: visible;
&:hover {
border-color: Colors.$primary;
background-color: Colors.$primary-lightest;
border-color: var(--primary-0);
background-color: var(--background-special-0);
}
.dark_image {
filter: invert(100%);
}
.brain_info_wrapper {
@ -37,13 +40,13 @@
.name {
@include Typography.EllipsisOverflow;
width: 200px;
color: Colors.$black;
color: var(--text-3);
}
.description {
@include Typography.EllipsisOverflow;
flex: 1;
color: Colors.$dark-grey;
color: var(--text-2);
}
@media (max-width: ScreenSizes.$small) {

View File

@ -9,6 +9,7 @@ import Icon from "@/lib/components/ui/Icon/Icon";
import { OptionsModal } from "@/lib/components/ui/OptionsModal/OptionsModal";
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
import { MinimalBrainForUser } from "@/lib/context/BrainProvider/types";
import { useUserSettingsContext } from "@/lib/context/UserSettingsProvider/hooks/useUserSettingsContext";
import { Option } from "@/lib/types/Options";
import styles from "./BrainItem.module.scss";
@ -32,6 +33,7 @@ export const BrainItem = ({ brain, even }: BrainItemProps): JSX.Element => {
brainId: brain.id,
userAccessibleBrains: allBrains,
});
const { isDarkMode } = useUserSettingsContext();
const iconRef = useRef<HTMLDivElement | null>(null);
const optionsRef = useRef<HTMLDivElement | null>(null);
@ -67,7 +69,7 @@ export const BrainItem = ({ brain, even }: BrainItemProps): JSX.Element => {
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, []);
}, [isDarkMode]);
return (
<>
@ -77,16 +79,18 @@ export const BrainItem = ({ brain, even }: BrainItemProps): JSX.Element => {
${styles.brain_item_wrapper}
`}
>
{brain.integration_logo_url ? (
<Image
src={brain.integration_logo_url}
alt="logo_image"
width={18}
height={18}
/>
) : (
<Icon name="brain" size="normal" color="primary" />
)}
<Image
className={isDarkMode ? styles.dark_image : ""}
src={
brain.integration_logo_url
? brain.integration_logo_url
: "/default_brain_image.png"
}
alt="logo_image"
width={18}
height={18}
/>
<Link
className={styles.brain_info_wrapper}
href={`/studio/${brain.id}`}
@ -94,7 +98,6 @@ export const BrainItem = ({ brain, even }: BrainItemProps): JSX.Element => {
<span className={styles.name}>{brain.name}</span>
<span className={styles.description}>{brain.description}</span>
</Link>
<div>
<div
ref={iconRef}

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/ScreenSizes.module.scss";
@use "@/styles/Spacings.module.scss";
@ -17,7 +16,7 @@
display: flex;
padding: Spacings.$spacing05;
gap: Spacings.$spacing05;
background-color: Colors.$highlight;
background-color: var(--background-1);
margin-bottom: Spacings.$spacing03;
padding-left: Spacings.$spacing09;
@ -26,7 +25,7 @@
}
.description {
color: Colors.$dark-grey;
color: var(--text-2);
}
@media (max-width: ScreenSizes.$small) {

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/ScreenSizes.module.scss";
@use "@/styles/Spacings.module.scss";
@ -13,15 +12,15 @@
justify-content: space-between;
align-items: center;
margin-block: Spacings.$spacing03;
border: 1px solid Colors.$lightest-black;
border: 1px solid var(--border-1);
border-radius: Radius.$normal;
padding-block: Spacings.$spacing03;
position: relative;
overflow: visible;
&:hover {
border-color: Colors.$primary;
background-color: Colors.$primary-lightest;
border-color: var(--primary-0);
background-color: var(--background-special-0);
}
.left {

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/Spacings.module.scss";
@use "@/styles/Typography.module.scss";

View File

@ -1,4 +1,4 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/BoxShadow.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/Spacings.module.scss";
@use "@/styles/Typography.module.scss";
@ -11,9 +11,9 @@
.section_wrapper {
border-radius: Radius.$big;
background-color: Colors.$highlight;
background-color: var(--background-1);
padding: Spacings.$spacing05;
box-shadow: 0 1px 2px rgb(0, 0, 0, 0.25);
box-shadow: BoxShadow.$small;
display: flex;
flex-direction: column;
gap: Spacings.$spacing05;

View File

@ -1,4 +1,4 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/BoxShadow.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/ScreenSizes.module.scss";
@use "@/styles/Spacings.module.scss";
@ -15,10 +15,10 @@
flex-direction: column;
width: 100%;
gap: Spacings.$spacing05;
box-shadow: 0 1px 2px rgb(0, 0, 0, 0.25);
box-shadow: BoxShadow.$small;
border-radius: Radius.$big;
padding: Spacings.$spacing06;
background-color: Colors.$highlight;
background-color: var(--background-1);
.section_title {
@include Typography.H3;
@ -63,10 +63,10 @@
flex-direction: column;
width: 100%;
gap: Spacings.$spacing05;
box-shadow: 0 1px 2px rgb(0, 0, 0, 0.25);
box-shadow: BoxShadow.$small;
border-radius: Radius.$big;
padding: Spacings.$spacing06;
background-color: Colors.$highlight;
background-color: var(--background-1);
.section_title {
@include Typography.H3;

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/Spacings.module.scss";
@ -18,7 +17,7 @@
width: 80%;
height: 15px;
border-radius: Radius.$normal;
background: Colors.$primary-light;
background: var(--primary-1);
outline: none;
-webkit-transition: 0.2s;
transition: opacity 0.2s;
@ -34,15 +33,7 @@
width: 15px;
height: 30px;
border-radius: Radius.$big;
background: Colors.$primary; /* Changez la couleur ici */
cursor: pointer;
}
.slider::-moz-range-thumb {
width: 25px;
height: 25px;
border-radius: 50%;
background: #4caf50; /* Changez la couleur ici */
background: var(--primary-0);
cursor: pointer;
}
}

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/ScreenSizes.module.scss";
@use "@/styles/Spacings.module.scss";
@ -40,6 +39,10 @@
cursor: pointer;
width: 120px;
.dark_image {
filter: invert(100%);
}
.brain_title {
font-size: Typography.$medium;
font-weight: 500;
@ -47,11 +50,11 @@
&:hover,
&.selected {
border-color: Colors.$primary;
background-color: Colors.$primary-lightest;
border-color: var(--primary-0);
background-color: var(--background-special-0);
.brain_title {
color: Colors.$primary;
color: var(--primary-0);
}
}
}

View File

@ -5,6 +5,7 @@ import { IntegrationBrains } from "@/lib/api/brain/types";
import { MessageInfoBox } from "@/lib/components/ui/MessageInfoBox/MessageInfoBox";
import { Tag } from "@/lib/components/ui/Tag/Tag";
import Tooltip from "@/lib/components/ui/Tooltip/Tooltip";
import { useUserSettingsContext } from "@/lib/context/UserSettingsProvider/hooks/useUserSettingsContext";
import styles from "./BrainCatalogue.module.scss";
@ -19,6 +20,7 @@ export const BrainCatalogue = ({
}): JSX.Element => {
const { setCurrentSelectedBrain, currentSelectedBrain } =
useBrainCreationContext();
const { isDarkMode } = useUserSettingsContext();
return (
<div className={styles.cards_wrapper}>
@ -47,6 +49,7 @@ export const BrainCatalogue = ({
}`}
>
<Image
className={isDarkMode ? styles.dark_image : ""}
src={brain.integration_logo_url}
alt={brain.integration_name}
width={50}

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/Spacings.module.scss";
@use "@/styles/Typography.module.scss";
@ -17,7 +16,7 @@
.circle {
width: 2.5rem;
height: 2.5rem;
background-color: Colors.$primary;
background-color: var(--primary-0);
border-radius: Radius.$circle;
display: flex;
justify-content: center;
@ -35,43 +34,43 @@
&.done_step {
.circle {
background-color: Colors.$success;
background-color: var(--success);
}
.step_info {
.step_status {
color: Colors.$success;
color: var(--success);
}
}
}
&.current_step {
.circle {
background-color: Colors.$white;
border: 1px solid Colors.$primary;
background-color: var(--background-0);
border: 1px solid var(--primary-0);
}
.inside_circle {
background-color: Colors.$primary;
background-color: var(--primary-0);
width: 70%;
height: 70%;
}
.step_info {
.step_status {
color: Colors.$primary;
color: var(--primary-0);
}
}
}
&.pending_step {
.circle {
background-color: Colors.$primary-light;
background-color: var(--primary-1);
}
.step_info {
.step_status {
color: Colors.$normal-grey;
color: var(--text-1);
}
}
}
@ -85,7 +84,7 @@
.step_index {
white-space: nowrap;
color: Colors.$normal-grey;
color: var(--text-1);
}
}
}
@ -94,12 +93,12 @@
flex-grow: 1;
height: 4px;
border-radius: Radius.$big;
background-color: Colors.$primary-light;
background-color: var(--primary-1);
margin: 0 8px;
margin-top: Spacings.$spacing05;
&.done {
background-color: Colors.$success;
background-color: var(--success);
}
}
}

View File

@ -1,13 +1,12 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Spacings.module.scss";
@use "@/styles/Typography.module.scss";
.current_brain_wrapper {
background-color: Colors.$lightest-grey;
background-color: var(--background-2);
padding-inline: Spacings.$spacing05;
padding-block: Spacings.$spacing01;
font-size: Typography.$small;
color: Colors.$normal-grey;
color: var(--text-1);
.brain_infos {
display: flex;
@ -31,8 +30,12 @@
align-items: center;
overflow: hidden;
.dark_image {
filter: invert(100%);
}
.brain_name {
color: Colors.$black;
color: var(--text-3);
@include Typography.EllipsisOverflow;
}
}

View File

@ -1,6 +1,7 @@
import Image from "next/image";
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
import { useUserSettingsContext } from "@/lib/context/UserSettingsProvider/hooks/useUserSettingsContext";
import styles from "./CurrentBrain.module.scss";
@ -14,7 +15,7 @@ export const CurrentBrain = ({
allowingRemoveBrain,
}: CurrentBrainProps): JSX.Element => {
const { currentBrain, setCurrentBrainId } = useBrainContext();
const { isDarkMode } = useUserSettingsContext();
const removeCurrentBrain = (): void => {
setCurrentBrainId(null);
};
@ -29,16 +30,17 @@ export const CurrentBrain = ({
<div className={styles.left}>
<span className={styles.title}>Talking to</span>
<div className={styles.brain_name_wrapper}>
{currentBrain.integration_logo_url ? (
<Image
src={currentBrain.integration_logo_url}
alt="brain"
width={18}
height={18}
/>
) : (
<Icon size="small" name="brain" color="primary" />
)}
<Image
className={isDarkMode ? styles.dark_image : ""}
src={
currentBrain.integration_logo_url
? currentBrain.integration_logo_url
: "/default_brain_image.png"
}
alt="logo_image"
width={18}
height={18}
/>
<span className={styles.brain_name}>{currentBrain.name}</span>
</div>
</div>

View File

@ -1,10 +1,9 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Spacings.module.scss";
@use "@/styles/ZIndexes.module.scss";
.menu_container {
background-color: Colors.$highlight;
border-right: 1px solid Colors.$lightest-black;
background-color: var(--background-1);
border-right: 1px solid var(--border-1);
.menu_wrapper {
padding-top: Spacings.$spacing05;
@ -37,7 +36,7 @@
.social_buttons_wrapper {
padding-block: Spacings.$spacing04;
border-top: 1px solid Colors.$lightest-black;
border-top: 1px solid var(--border-1);
}
}
}

View File

@ -7,6 +7,7 @@ import { useChatsList } from "@/app/chat/[chatId]/hooks/useChatsList";
import { QuivrLogo } from "@/lib/assets/QuivrLogo";
import { nonProtectedPaths } from "@/lib/config/routesConfig";
import { useMenuContext } from "@/lib/context/MenuProvider/hooks/useMenuContext";
import { useUserSettingsContext } from "@/lib/context/UserSettingsProvider/hooks/useUserSettingsContext";
import styles from "./Menu.module.scss";
import { AnimatedDiv } from "./components/AnimationDiv";
@ -23,6 +24,7 @@ export const Menu = (): JSX.Element => {
const router = useRouter();
const pathname = usePathname() ?? "";
const [isLogoHovered, setIsLogoHovered] = useState<boolean>(false);
const { isDarkMode } = useUserSettingsContext();
useChatsList();
@ -53,7 +55,9 @@ export const Menu = (): JSX.Element => {
>
<QuivrLogo
size={50}
color={isLogoHovered ? "primary" : "black"}
color={
isLogoHovered ? "primary" : isDarkMode ? "white" : "black"
}
/>
</div>

View File

@ -1,4 +1,4 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/BoxShadow.module.scss";
@use "@/styles/IconSizes.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/Spacings.module.scss";
@ -8,11 +8,11 @@
padding: Spacings.$spacing03;
justify-content: space-between;
align-items: center;
border: 1px solid Colors.$lighter-grey;
border: 1px solid var(--border-0);
border-radius: Radius.$big;
background-color: Colors.$white;
background-color: var(--background-0);
cursor: pointer;
color: Colors.$dark-grey;
color: var(--text-2);
transition: box-shadow 0.3s ease;
.left_wrapper {
@ -30,7 +30,7 @@
align-items: center;
justify-content: center;
font-size: 12px;
border: 1px solid Colors.$lighter-grey;
border: 1px solid var(--border-0);
border-radius: Radius.$small;
width: 16px;
height: 16px;
@ -39,7 +39,7 @@
}
&:hover {
border-color: Colors.$primary;
box-shadow: 0 0 0 1px Colors.$primary;
border-color: var(--primary-0);
box-shadow: BoxShadow.$primary;
}
}

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/Spacings.module.scss";
@use "@/styles/Typography.module.scss";
@ -14,7 +13,7 @@
border-left: 2px solid transparent;
&.selected {
border-left: 2px solid Colors.$primary;
border-left: 2px solid var(--primary-0);
border-radius: 0 Radius.$normal Radius.$normal 0;
}
@ -27,19 +26,19 @@
.title {
@include Typography.EllipsisOverflow;
font-size: Typography.$small;
color: Colors.$black;
color: var(--text-3);
&.gold {
color: Colors.$gold;
color: var(--gold);
}
&.primary {
color: Colors.$primary;
color: var(--primary-0);
}
}
}
&:hover {
background-color: Colors.$lightest-black;
background-color: var(--background-3);
}
}

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/ScreenSizes.module.scss";
@use "@/styles/Spacings.module.scss";
@use "@/styles/Typography.module.scss";
@ -7,7 +6,7 @@
position: relative;
padding: Spacings.$spacing03;
padding-top: 0;
color: Colors.$dark-grey;
color: var(--text-2);
font-size: Typography.$small;
max-height: 300px;
overflow-y: scroll;

View File

@ -1,11 +1,10 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/Spacings.module.scss";
@use "@/styles/Typography.module.scss";
@use "@/styles/ZIndexes.module.scss";
.thread_item_wrapper {
color: Colors.$black;
color: var(--text-3);
display: flex;
justify-content: space-between;
gap: Spacings.$spacing03;
@ -15,13 +14,13 @@
.edit_thread_name {
@include Typography.EllipsisOverflow;
color: Colors.$black;
color: var(--text-3);
border: none;
height: 21px;
outline: none;
padding: 0;
font-size: Typography.$small;
background-color: Colors.$lighter-grey;
background-color: var(--background-3);
border-radius: Radius.$small;
&:focus {
@ -33,7 +32,7 @@
@include Typography.EllipsisOverflow;
&:hover {
color: Colors.$primary;
color: var(--primary-0);
}
}

View File

@ -1,8 +1,7 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Spacings.module.scss";
.chats_wrapper {
border-left: 1px solid Colors.$normal-grey;
border-left: 1px solid var(--border-2);
padding-left: Spacings.$spacing03;
margin: Spacings.$spacing02;
display: flex;

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/ScreenSizes.module.scss";
@use "@/styles/Spacings.module.scss";
@use "@/styles/Typography.module.scss";
@ -9,7 +8,7 @@
justify-content: space-between;
padding: Spacings.$spacing04;
padding-left: Spacings.$spacing09;
border-bottom: 1px solid Colors.$lightest-black;
border-bottom: 1px solid var(--border-1);
.left {
@include Typography.H2;
@ -32,5 +31,6 @@
display: flex;
gap: Spacings.$spacing03;
align-self: flex-end;
align-items: center;
}
}

View File

@ -1,4 +1,7 @@
import { useEffect, useState } from "react";
import { useMenuContext } from "@/lib/context/MenuProvider/hooks/useMenuContext";
import { useUserSettingsContext } from "@/lib/context/UserSettingsProvider/hooks/useUserSettingsContext";
import { ButtonType } from "@/lib/types/QuivrButton";
import styles from "./PageHeader.module.scss";
@ -18,6 +21,16 @@ export const PageHeader = ({
buttons,
}: Props): JSX.Element => {
const { isOpened } = useMenuContext();
const { isDarkMode, setIsDarkMode } = useUserSettingsContext();
const [lightModeIconName, setLightModeIconName] = useState("sun");
const toggleTheme = () => {
setIsDarkMode(!isDarkMode);
};
useEffect(() => {
setLightModeIconName(isDarkMode ? "sun" : "moon");
}, [isDarkMode]);
return (
<div className={styles.page_header_wrapper}>
@ -36,6 +49,13 @@ export const PageHeader = ({
hidden={button.hidden}
/>
))}
<Icon
name={lightModeIconName}
color="black"
handleHover={true}
size="small"
onClick={toggleTheme}
/>
</div>
</div>
);

View File

@ -1,11 +1,10 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/ScreenSizes.module.scss";
@use "@/styles/Spacings.module.scss";
@use "@/styles/ZIndexes.module.scss";
.search_modal_wrapper {
display: flex;
background-color: rgba(Colors.$dark-black, 0.94);
background-color: var(--background-blur);
height: 100%;
width: 100%;
position: absolute;

View File

@ -1,6 +1,6 @@
import { StripePricingTable } from "./components/PricingTable/PricingTable";
import { Modal } from "../../ui/ModalPayment";
import { Modal } from "../../ui/Modal/Modal";
type StripePricingModalProps = {
Trigger: JSX.Element;
@ -10,7 +10,7 @@ export const StripePricingModal = ({
Trigger,
}: StripePricingModalProps): JSX.Element => {
return (
<Modal Trigger={Trigger} CloseTrigger={<div />}>
<Modal Trigger={Trigger} CloseTrigger={<div />} unforceWhite={true}>
<StripePricingTable />
</Modal>
);

View File

@ -0,0 +1,9 @@
@use "@/styles/Spacings.module.scss";
.info_content {
padding: Spacings.$spacing06;
.bold {
font-weight: 800;
}
}

View File

@ -1,38 +1,25 @@
import { MessageInfoBox } from "@/lib/components/ui/MessageInfoBox/MessageInfoBox";
import styles from "./PricingTable.module.scss";
const PRICING_TABLE_ID = process.env.NEXT_PUBLIC_STRIPE_PRICING_TABLE_ID;
const PUBLISHABLE_KEY = process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY;
export const StripePricingTable = (): JSX.Element => {
return (
<>
<div className="grid md:grid-cols-2 gap-4 p-2 bg-highlight">
<div className="space-y-3 text-center">
<h3 className="text-2xl font-semibold text-black">Free Tier</h3>
<ul className="list-none space-y-2">
<li className="text-lg font-medium text-gray-800">🧠 3 brains</li>
<li className="text-lg font-medium text-gray-800">
🙋 100 questions per month
</li>
<li className="text-lg font-medium text-gray-800">
💾 Up to 30Mb of storage
</li>
</ul>
</div>
<div className="space-y-3 text-center">
<h3 className="text-2xl font-semibold text-black">
Premium Features
</h3>
<ul className="list-none space-y-2">
<li className="text-lg font-medium text-gray-800">
🧠 Bigger & more Brains
</li>
<li className="text-lg font-medium text-gray-800">
🙋 More credits & access to premium models (GPT4, Mistral)
</li>
<li className="text-lg font-medium text-gray-800">
🚀 GPT3.5 = 1 credit & GPT4 = 20 credits
</li>
</ul>
</div>
<div className={styles.info_content}>
<MessageInfoBox type="info" unforceWhite={true}>
<div>
{"The free tier allows you to have"}
<span className={styles.bold}> 3 brains </span>
{"and"}
<span className={styles.bold}> 100 chat credits </span>
{
"per month. You can upgrade to unlock more brains, more chat credits and access to premium models."
}
</div>
</MessageInfoBox>
</div>
<div className="p-2">
<script async src="https://js.stripe.com/v3/pricing-table.js"></script>

View File

@ -0,0 +1,12 @@
import * as React from "react";
declare global {
namespace JSX {
interface IntrinsicElements {
"stripe-pricing-table": React.DetailedHTMLProps<
React.HTMLAttributes<HTMLElement>,
HTMLElement
>;
}
}
}

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Spacings.module.scss";
@use "@/styles/ZIndexes.module.scss";
@ -7,7 +6,7 @@
display: flex;
flex-direction: column;
justify-content: space-between;
background-color: Colors.$white;
background-color: var(--background-0);
width: 100%;
flex: 1;

View File

@ -1,17 +1,17 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Radius.module.scss";
.selection {
border-radius: Radius.$normal;
box-shadow: none;
cursor: pointer;
background-color: var(--background-0);
&:hover {
background-color: Colors.$lightest-black;
background-color: var(--background-3);
}
&:focus {
box-shadow: none;
border-color: Colors.$primary;
border-color: var(--primary-0);
}
}

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/Spacings.module.scss";
@use "@/styles/Typography.module.scss";
@ -8,7 +7,7 @@
display: flex;
flex-direction: column;
border-radius: Radius.$normal;
border: 1px dashed Colors.$lighter-grey;
border: 1px dashed var(--border-0);
overflow: hidden;
font-size: Typography.$small;
@ -16,7 +15,7 @@
overflow: hidden;
transition: max-height 0.3s Transitions.$easeOutBack;
}
.contentCollapsed {
max-height: 0;
}
@ -44,10 +43,10 @@
}
&:hover {
background-color: Colors.$lightest-black;
background-color: var(--background-3);
}
}
.iconRotate {
transition: transform 0.3s Transitions.$easeOutBack;
}

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/IconSizes.module.scss";
.small {
@ -30,55 +29,55 @@
}
.black {
color: Colors.$black;
color: var(--icon-3);
&.hovered {
color: Colors.$primary;
color: var(--primary-0);
}
}
.dark-grey {
color: Colors.$dark-grey;
color: var(--icon-2);
}
.grey {
color: Colors.$normal-grey;
color: var(--icon-1);
}
.primary {
color: Colors.$primary;
color: var(--primary-0);
}
.accent {
color: Colors.$accent;
color: var(--accent);
}
.gold {
color: Colors.$gold;
color: var(--gold);
}
.white {
color: Colors.$white;
color: var(--icon-0);
}
.dangerous {
color: Colors.$dangerous;
color: var(--dangerous);
&.hovered {
color: Colors.$dangerous-dark;
color: var(--dangerous-dark);
}
}
.warning {
color: Colors.$warning;
color: var(--warning);
}
.success {
color: Colors.$success;
color: var(--success);
}
.disabled {
color: Colors.$black;
color: var(--icon-3);
pointer-events: none;
opacity: 0.2;
}

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/IconSizes.module.scss";
.loader_icon {

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/Spacings.module.scss";
@ -8,25 +7,29 @@
align-items: center;
gap: Spacings.$spacing03;
width: fit-content;
border: 1px solid Colors.$normal-grey;
color: Colors.$black;
border: 1px solid var(--border-2);
color: var(--text-3);
border-radius: Radius.$normal;
&.success {
border-color: Colors.$success;
color: Colors.$success;
background-color: Colors.$success-lightest;
border-color: var(--success);
color: var(--success);
background-color: var(--success-lightest);
}
&.info {
border-color: Colors.$primary;
color: Colors.$primary;
background-color: Colors.$primary-lightest;
border-color: var(--primary-0);
color: var(--primary-0);
background-color: var(--primary-2);
}
&.warning {
border-color: Colors.$warning;
color: Colors.$warning;
background-color: Colors.$warning-lightest;
border-color: var(--warning);
color: var(--warning);
background-color: var(--warning-lightest);
}
&.dark {
background-color: var(--background-special-0);
}
}

View File

@ -1,3 +1,4 @@
import { useUserSettingsContext } from "@/lib/context/UserSettingsProvider/hooks/useUserSettingsContext";
import { iconList } from "@/lib/helpers/iconList";
import { Color } from "@/lib/types/Colors";
@ -8,11 +9,13 @@ import { Icon } from "../Icon/Icon";
export type MessageInfoBoxProps = {
children: React.ReactNode;
type: "info" | "success" | "warning" | "error";
unforceWhite?: boolean;
};
export const MessageInfoBox = ({
children,
type,
unforceWhite,
}: MessageInfoBoxProps): JSX.Element => {
const getIconProps = (): {
iconName: keyof typeof iconList;
@ -30,8 +33,14 @@ export const MessageInfoBox = ({
}
};
const { isDarkMode } = useUserSettingsContext();
return (
<div className={`${styles.message_info_box_wrapper} ${styles[type]} `}>
<div
className={`${styles.message_info_box_wrapper} ${styles[type]} ${
isDarkMode && !unforceWhite ? styles.dark : ""
}`}
>
<Icon
name={getIconProps().iconName}
size="normal"

View File

@ -1,4 +1,4 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/BoxShadow.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/ScreenSizes.module.scss";
@use "@/styles/Spacings.module.scss";
@ -6,7 +6,7 @@
.modal_container {
display: flex;
background-color: rgba(Colors.$dark-black, 0.94);
background-color: var(--background-blur);
height: 100%;
width: 100%;
position: absolute;
@ -18,10 +18,10 @@
display: flex;
flex-direction: column;
border-radius: Radius.$big;
background-color: Colors.$white;
background-color: var(--background-0);
padding: Spacings.$spacing05;
cursor: auto;
box-shadow: 0 2px 4px rgb(0, 0, 0, 0.25);
box-shadow: BoxShadow.$medium;
max-width: 90vw;
overflow: scroll;
@ -30,6 +30,11 @@
height: 90vh;
}
&.white {
background-color: var(--white-0);
color: var(--text-0);
}
@media (max-width: ScreenSizes.$small) {
&.big_modal {
width: 90vw;

View File

@ -20,6 +20,7 @@ type CommonModalProps = {
isOpen?: undefined;
setOpen?: undefined;
bigModal?: boolean;
unforceWhite?: boolean;
};
type ModalProps =
@ -38,6 +39,7 @@ export const Modal = ({
isOpen: customIsOpen,
setOpen: customSetOpen,
bigModal,
unforceWhite,
}: ModalProps): JSX.Element => {
const [isOpen, setOpen] = useState(false);
const { t } = useTranslation(["translation"]);
@ -64,7 +66,7 @@ export const Modal = ({
<motion.div
className={`${styles.modal_content_wrapper} ${
bigModal ? styles.big_modal : ""
}`}
} ${unforceWhite ? styles.white : ""}`}
initial={{ opacity: 0, y: "-40%" }}
animate={{ opacity: 1, y: "0%" }}
exit={{ opacity: 0, y: "40%" }}

View File

@ -1,105 +0,0 @@
/*eslint max-lines: ["error", 200 ]*/
"use client";
import * as Dialog from "@radix-ui/react-dialog";
import { AnimatePresence, motion } from "framer-motion";
import { ReactNode, useState } from "react";
import { useTranslation } from "react-i18next";
import { MdClose } from "react-icons/md";
import Button from "./Button";
type CommonModalProps = {
title?: string;
desc?: string;
children?: ReactNode;
Trigger?: ReactNode;
CloseTrigger?: ReactNode;
isOpen?: undefined;
setOpen?: undefined;
};
type ModalProps =
| CommonModalProps
| (Omit<CommonModalProps, "isOpen" | "setOpen"> & {
isOpen: boolean;
setOpen: (isOpen: boolean) => void;
});
export const Modal = ({
title,
desc,
children,
Trigger,
CloseTrigger,
isOpen: customIsOpen,
setOpen: customSetOpen,
}: ModalProps): JSX.Element => {
const [isOpen, setOpen] = useState(false);
const { t } = useTranslation(["translation"]);
return (
<Dialog.Root
open={customIsOpen ?? isOpen}
onOpenChange={customSetOpen ?? setOpen}
>
{Trigger !== undefined && (
<Dialog.Trigger asChild>{Trigger}</Dialog.Trigger>
)}
<AnimatePresence>
{customIsOpen ?? isOpen ? (
<Dialog.Portal forceMount>
<Dialog.Overlay asChild forceMount>
<motion.div
className="z-[10000] py-20 fixed inset-0 flex justify-center overflow-auto cursor-pointer bg-black/50 backdrop-blur-sm"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
<Dialog.Content asChild forceMount>
<motion.div
initial={{ opacity: 0, y: "-40%" }}
animate={{ opacity: 1, y: "0%" }}
exit={{ opacity: 0, y: "40%" }}
className="w-[90vw] my-auto flex flex-col h-fit max-w-6xl rounded-xl bg-white dark:bg-black border border-black/10 dark:border-white/25 p-10 shadow-xl dark:shadow-primary/50 focus:outline-none cursor-auto"
>
<Dialog.Title
className="m-0 text-2xl font-bold"
data-testid="modal-title"
>
{title}
</Dialog.Title>
<Dialog.Description
className="opacity-50"
data-testid="modal-description"
>
{desc}
</Dialog.Description>
{children}
<Dialog.Close asChild>
{CloseTrigger !== undefined ? (
CloseTrigger
) : (
<Button variant={"secondary"} className="self-end">
{t("doneButton")}
</Button>
)}
</Dialog.Close>
<Dialog.Close asChild>
<button
className="absolute top-0 p-5 right-0 inline-flex appearance-none items-center justify-center rounded-full focus:shadow-sm focus:outline-none"
aria-label="Close"
>
<MdClose />
</button>
</Dialog.Close>
</motion.div>
</Dialog.Content>
</motion.div>
</Dialog.Overlay>
</Dialog.Portal>
) : null}
</AnimatePresence>
</Dialog.Root>
);
};

View File

@ -1,13 +1,13 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/BoxShadow.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/Spacings.module.scss";
@use "@/styles/ZIndexes.module.scss";
.options_modal_wrapper {
background-color: Colors.$highlight;
background-color: var(--background-1);
border-radius: Radius.$normal;
border-radius: Radius.$normal;
box-shadow: 0 1px 2px rgb(0, 0, 0, 0.25);
box-shadow: BoxShadow.$small;
width: fit-content;
.option {
@ -21,11 +21,11 @@
overflow: hidden;
&:not(:first-child) {
border-top: 1px solid Colors.$light-grey;
border-top: 1px solid var(--border-2);
}
&:hover {
background-color: Colors.$primary-lightest;
background-color: var(--background-special-0);
}
&.disabled {

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/ScreenSizes.module.scss";
@use "@/styles/Spacings.module.scss";
@ -14,46 +13,50 @@
cursor: pointer;
display: flex;
width: fit-content;
background-color: Colors.$white;
background-color: var(--background-0);
&.hidden {
display: none;
}
&.primary {
border-color: Colors.$primary;
color: Colors.$primary;
border-color: var(--primary-0);
color: var(--primary-0);
&:hover {
background-color: Colors.$primary;
color: Colors.$white;
background-color: var(--primary-0);
color: var(--text-0);
}
}
&.dangerous {
border-color: Colors.$dangerous;
color: Colors.$dangerous;
border-color: var(--dangerous);
color: var(--dangerous);
&:hover {
background-color: Colors.$dangerous;
color: Colors.$white;
background-color: var(--dangerous);
color: var(--text-0);
}
}
&.gold {
border-color: Colors.$gold;
color: Colors.$gold;
border-color: var(--gold);
color: var(--gold);
&:hover {
background-color: Colors.$gold;
color: Colors.$white;
background-color: var(--gold);
color: var(--text-0);
}
}
&.disabled {
border-color: Colors.$normal-grey;
border-color: var(--border-2);
pointer-events: none;
color: Colors.$normal-grey;
color: var(--text-1);
&.dark {
opacity: 0.2;
}
}
}

View File

@ -1,5 +1,6 @@
import { useState } from "react";
import { useUserSettingsContext } from "@/lib/context/UserSettingsProvider/hooks/useUserSettingsContext";
import { ButtonType } from "@/lib/types/QuivrButton";
import styles from "./QuivrButton.module.scss";
@ -17,6 +18,7 @@ export const QuivrButton = ({
hidden,
}: ButtonType): JSX.Element => {
const [hovered, setHovered] = useState<boolean>(false);
const { isDarkMode } = useUserSettingsContext();
return (
<div
@ -24,10 +26,11 @@ export const QuivrButton = ({
${styles.button_wrapper}
${styles[color]}
${disabled ? styles.disabled : ""}
${isDarkMode ? styles.dark : ""}
${hidden ? styles.hidden : ""}
`}
// eslint-disable-next-line @typescript-eslint/no-misused-promises
onClick={() => onClick?.()}
// eslint-disable-next-line @typescript-eslint/no-misused-promises, @typescript-eslint/prefer-optional-chain, @typescript-eslint/no-unnecessary-condition
onClick={() => onClick && onClick()}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>

View File

@ -1,4 +1,4 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/BoxShadow.module.scss";
@use "@/styles/IconSizes.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/Spacings.module.scss";
@ -7,11 +7,11 @@
display: flex;
flex-direction: column;
gap: Spacings.$spacing03;
background-color: Colors.$white;
background-color: var(--background-0);
border-radius: Radius.$big;
border: 1px solid Colors.$lighter-grey;
border: 1px solid var(--border-0);
overflow: hidden;
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.25);
box-shadow: BoxShadow.$large;
.editor_wrapper {
display: flex;
@ -26,11 +26,11 @@
.search_icon {
width: IconSizes.$big;
height: IconSizes.$big;
color: Colors.$accent;
color: var(--accent);
cursor: pointer;
&.disabled {
color: Colors.$black;
color: var(--text-3);
pointer-events: none;
opacity: 0.2;
}

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/IconSizes.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/Spacings.module.scss";
@ -9,12 +8,12 @@
display: flex;
flex-direction: column;
position: relative;
background-color: Colors.$white;
background-color: var(--background-0);
.first_line_wrapper {
display: flex;
justify-content: space-between;
border: 1px solid Colors.$normal-grey;
border: 1px solid var(--border-2);
border-radius: Radius.$big;
align-items: center;
cursor: pointer;
@ -37,14 +36,14 @@
.label {
@include Typography.EllipsisOverflow;
background-color: Colors.$primary-light;
background-color: var(--background-special-1);
border-radius: Radius.$normal;
padding-inline: Spacings.$spacing05;
padding-block: Spacings.$spacing02;
white-space: nowrap;
&.not_set {
color: Colors.$normal-grey;
color: var(--text-1);
background-color: transparent;
padding-inline: 0;
}
@ -67,10 +66,10 @@
.options {
position: absolute;
background-color: Colors.$white;
background-color: var(--background-0);
width: 100%;
top: 100%;
border: 1px solid Colors.$normal-grey;
border: 1px solid var(--border-2);
border-top: none;
border-radius: 0 0 Radius.$big Radius.$big;
overflow: hidden;
@ -88,7 +87,7 @@
&:hover {
.option_name {
background-color: Colors.$primary-lightest;
background-color: var(--background-special-1);
}
}
@ -98,7 +97,7 @@
.option_name {
@include Typography.EllipsisOverflow;
border: 1px solid Colors.$lightest-black;
border: 1px solid var(--border-1);
border-radius: Radius.$small;
padding-inline: Spacings.$spacing05;
padding-block: Spacings.$spacing02;

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/ScreenSizes.module.scss";
@use "@/styles/Spacings.module.scss";
@ -12,25 +11,25 @@
align-items: center;
justify-content: center;
flex: 1;
border-bottom: 2px solid Colors.$lightest-grey;
border-bottom: 2px solid var(--border-0);
padding-block: Spacings.$spacing03;
cursor: pointer;
box-sizing: border-box;
gap: Spacings.$spacing03;
&.selected {
border-bottom-color: Colors.$primary;
color: Colors.$primary;
background-color: Colors.$lightest-grey;
border-bottom-color: var(--primary-0);
color: var(--primary-0);
background-color: var(--background-2);
}
&:hover {
color: Colors.$primary;
color: var(--primary-0);
}
&.disabled {
pointer-events: none;
color: Colors.$normal-grey;
color: var(--text-1);
}
@media (max-width: ScreenSizes.$small) {

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/Spacings.module.scss";
@use "@/styles/Typography.module.scss";
@ -11,10 +10,10 @@
font-size: Typography.$tiny;
&.primary {
background-color: Colors.$primary-light;
background-color: var(--primary-1);
}
&.gold {
background-color: Colors.$gold;
background-color: var(--gold);
}
}

View File

@ -1,17 +1,16 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/Spacings.module.scss";
.text_area_input_container {
display: flex;
border: 1px solid Colors.$lighter-grey;
border: 1px solid var(--border-0);
gap: Spacings.$spacing03;
padding-block: Spacings.$spacing02;
padding-inline: Spacings.$spacing03;
border-radius: Radius.$big;
align-items: center;
width: 100%;
background-color: Colors.$white;
background-color: var(--background-0);
&.simple {
border: none;
@ -30,10 +29,11 @@
}
.text_area_input {
caret-color: Colors.$accent;
caret-color: var(--accent);
border: none;
flex: 1;
resize: none;
background-color: transparent;
&:focus {
box-shadow: none;

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Spacings.module.scss";
.text_button_wrapper {
@ -10,17 +9,17 @@
}
.black {
color: Colors.$black;
color: var(--text-3);
&.hovered {
color: Colors.$primary;
color: var(--primary-0);
}
}
.dangerous {
color: Colors.$dangerous;
color: var(--dangerous);
&.hovered {
color: Colors.$dangerous-dark;
color: var(--dangerous)-dark;
}
}

View File

@ -1,17 +1,16 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/Spacings.module.scss";
.text_input_container {
display: flex;
border: 1px solid Colors.$lighter-grey;
border: 1px solid var(--border-0);
gap: Spacings.$spacing03;
padding-block: Spacings.$spacing02;
padding-inline: Spacings.$spacing03;
border-radius: Radius.$big;
align-items: center;
width: 100%;
background-color: Colors.$white;
background-color: var(--background-0);
&.simple {
border: none;
@ -30,7 +29,7 @@
}
.text_input {
caret-color: Colors.$accent;
caret-color: var(--accent);
border: none;
flex: 1;
background-color: transparent;

View File

@ -1,4 +1,3 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/Spacings.module.scss";
@use "@/styles/Typography.module.scss";
@ -6,7 +5,7 @@
.tooltip_content_wrapper {
z-index: ZIndexes.$tooltip;
background-color: Colors.$lightest-black;
background-color: var(--background-3);
padding: Spacings.$spacing03;
border-radius: Radius.$normal;
font-size: Typography.$small;

View File

@ -0,0 +1,76 @@
import { createContext, useEffect, useState } from "react";
import { parseBoolean } from "@/lib/helpers/parseBoolean";
type UserSettingsContextType = {
isDarkMode: boolean;
setIsDarkMode: React.Dispatch<React.SetStateAction<boolean>>;
};
export const UserSettingsContext = createContext<
UserSettingsContextType | undefined
>(undefined);
export const UserSettingsProvider = ({
children,
}: {
children: React.ReactNode;
}): JSX.Element => {
const [isDarkMode, setIsDarkMode] = useState<boolean>(() => {
if (typeof window !== "undefined") {
const localIsDarkMode = localStorage.getItem("isDarkMode");
return parseBoolean(localIsDarkMode);
}
return false;
});
useEffect(() => {
if (typeof window !== "undefined") {
const prefersDarkMode = window.matchMedia(
"(prefers-color-scheme: dark)"
).matches;
const localIsDarkMode = localStorage.getItem("isDarkMode");
const newState =
localIsDarkMode !== null
? parseBoolean(localIsDarkMode)
: prefersDarkMode;
setIsDarkMode(newState);
newState
? document.body.classList.add("dark_mode")
: document.body.classList.remove("dark_mode");
const mediaQueryList = window.matchMedia("(prefers-color-scheme: dark)");
const listener = (event: MediaQueryListEvent) => {
const updatedState = event.matches;
setIsDarkMode(updatedState);
localStorage.setItem("isDarkMode", JSON.stringify(updatedState));
};
mediaQueryList.addEventListener("change", listener);
return () => {
mediaQueryList.removeEventListener("change", listener);
};
}
}, []);
useEffect(() => {
isDarkMode
? document.body.classList.add("dark_mode")
: document.body.classList.remove("dark_mode");
localStorage.setItem("isDarkMode", JSON.stringify(isDarkMode));
}, [isDarkMode]);
return (
<UserSettingsContext.Provider
value={{
isDarkMode,
setIsDarkMode,
}}
>
{children}
</UserSettingsContext.Provider>
);
};

View File

@ -0,0 +1,15 @@
import { useContext } from "react";
import { UserSettingsContext } from "../User-settings.provider";
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export const useUserSettingsContext = () => {
const context = useContext(UserSettingsContext);
if (context === undefined) {
throw new Error(
"useUserSettingsContext must be used within a UserSettingsProvider"
);
}
return context;
};

View File

@ -14,10 +14,12 @@ import {
FaGithub,
FaKey,
FaLinkedin,
FaMoon,
FaRegFileAlt,
FaRegKeyboard,
FaRegStar,
FaRegUserCircle,
FaSun,
FaTwitter,
FaUnlock,
} from "react-icons/fa";
@ -100,6 +102,7 @@ export const iconList: { [name: string]: IconType } = {
linkedin: FaLinkedin,
loader: AiOutlineLoading3Quarters,
logout: IoMdLogOut,
moon: FaMoon,
options: SlOptions,
paragraph: BsTextParagraph,
prompt: FaRegKeyboard,
@ -110,6 +113,7 @@ export const iconList: { [name: string]: IconType } = {
share: IoShareSocial,
software: CgSoftwareDownload,
star: FaRegStar,
sun: FaSun,
twitter: FaTwitter,
unlock: FaUnlock,
upload: FiUpload,

View File

@ -0,0 +1,7 @@
export const parseBoolean = (value: string | null): boolean => {
if (value === null) {
return false;
}
return value.toLowerCase() === "true";
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -0,0 +1,4 @@
$small: 0 1px 2px var(--box-shadow);
$medium: 0 2px 4px var(--box-shadow);
$large: 0 4px 8px var(--box-shadow);
$primary: 0 0 0 1px var(--box-shadow);

View File

@ -1,18 +1,18 @@
// WHITE
$white: #ffffff;
$dark-black: #081621;
$secondary: #f3ecff;
$tertiary: #f6f4ff;
$accent: #13abba;
$highlight: #fafafa;
$ivory: #fcfaf6;
$chat-bg-gray: #d9d9d9;
//PRIMARY
// PRIMARY
$primary: #6142d4;
$primary-light: #d0c6f2;
$primary-lightest: #f5f3fd;
//ACCENT
$accent: #13abba;
// BLACK
$dark-black: #081621;
$black: #11243e;
$light-black: #293a51;
$lightest-black: #e7e9ec;