diff --git a/config/project.ts b/config/project.ts
index ab948a3..4ae94eb 100644
--- a/config/project.ts
+++ b/config/project.ts
@@ -1,12 +1,13 @@
-const PROJECT_NAME = 'MyLinks';
-const PROJECT_DESCRIPTION =
- 'Another bookmark manager that lets you manage and share your favorite links in an intuitive interface';
-const PROJECT_URL = 'https://www.mylinks.app';
-const APP_COLOR = '#f0eef6';
+export const APP_COLOR = '#f0eef6';
-export default {
- name: PROJECT_NAME,
- description: PROJECT_DESCRIPTION,
- url: PROJECT_URL,
- color: APP_COLOR,
-};
+export const PROJECT_NAME = 'MyLinks';
+export const PROJECT_DESCRIPTION =
+ 'Another bookmark manager that lets you manage and share your favorite links in an intuitive interface';
+export const PROJECT_URL = 'https://www.mylinks.app';
+export const PROJECT_REPO_GITHUB_URL = 'https://github.com/my-links/my-links';
+export const PROJECT_EXTENSION_URL =
+ 'https://chromewebstore.google.com/detail/mylinks/agkmlplihacolkakgeccnbhphnepphma';
+
+export const AUTHOR_NAME = 'Sonny';
+export const AUTHOR_GITHUB_URL = 'https://github.com/Sonny93';
+export const AUTHOR_WEBSITE_URL = 'https://www.sonny.dev/?utm_source=mylinks';
diff --git a/inertia/app/app.tsx b/inertia/app/app.tsx
index 744c577..f0e47c8 100644
--- a/inertia/app/app.tsx
+++ b/inertia/app/app.tsx
@@ -4,6 +4,7 @@ import { isSSREnableForPage } from 'config-ssr';
import 'dayjs/locale/en';
import 'dayjs/locale/fr';
import { createRoot, hydrateRoot } from 'react-dom/client';
+import DefaultLayout from '~/layouts/default_layout';
import '../i18n/index';
const appName = import.meta.env.VITE_APP_NAME || 'MyLinks';
@@ -13,11 +14,17 @@ createInertiaApp({
title: (title) => `${appName}${title && ` - ${title}`}`,
- resolve: (name) => {
- return resolvePageComponent(
+ resolve: async (name) => {
+ const currentPage: any = await resolvePageComponent(
`../pages/${name}.tsx`,
import.meta.glob('../pages/**/*.tsx')
);
+
+ currentPage.default.layout =
+ currentPage.default.layout ||
+ ((p: any) => );
+
+ return currentPage;
},
setup({ el, App, props }) {
diff --git a/inertia/app/ssr.tsx b/inertia/app/ssr.tsx
index 1a90544..907e5ee 100644
--- a/inertia/app/ssr.tsx
+++ b/inertia/app/ssr.tsx
@@ -1,5 +1,6 @@
import { createInertiaApp } from '@inertiajs/react';
import ReactDOMServer from 'react-dom/server';
+import DefaultLayout from '~/layouts/default_layout';
export default function render(page: any) {
return createInertiaApp({
@@ -7,7 +8,11 @@ export default function render(page: any) {
render: ReactDOMServer.renderToString,
resolve: (name) => {
const pages = import.meta.glob('../pages/**/*.tsx', { eager: true });
- return pages[`../pages/${name}.tsx`];
+ let pageComponent: any = pages[`../pages/${name}.tsx`];
+ pageComponent.default.layout =
+ pageComponent?.default?.layout ||
+ ((pageChildren: any) => );
+ return pageComponent;
},
setup: ({ App, props }) => ,
});
diff --git a/inertia/components/common/floating_navbar/floating_navbar.module.css b/inertia/components/common/floating_navbar/floating_navbar.module.css
new file mode 100644
index 0000000..0e0c2b8
--- /dev/null
+++ b/inertia/components/common/floating_navbar/floating_navbar.module.css
@@ -0,0 +1,36 @@
+.navbarWrapper {
+ z-index: 9;
+}
+
+.navbar {
+ height: rem(60);
+ background-color: color-mix(
+ in srgb,
+ var(--mantine-color-body) 50%,
+ transparent
+ );
+ padding-inline: var(--mantine-spacing-lg);
+ transition: transform 400ms ease;
+ backdrop-filter: blur(16px);
+ overflow: hidden;
+ position: sticky;
+ top: 0;
+ left: 0;
+ right: 0;
+ z-index: 9;
+}
+
+.navbar__content {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ height: 100%;
+ max-width: 100%;
+ margin-inline: auto;
+}
+
+.navbar__content > div:last-child {
+ flex: 1;
+ justify-content: flex-end;
+}
diff --git a/inertia/components/common/floating_navbar/floating_navbar.tsx b/inertia/components/common/floating_navbar/floating_navbar.tsx
new file mode 100644
index 0000000..dbc6258
--- /dev/null
+++ b/inertia/components/common/floating_navbar/floating_navbar.tsx
@@ -0,0 +1,108 @@
+import {
+ PROJECT_EXTENSION_URL,
+ PROJECT_NAME,
+ PROJECT_REPO_GITHUB_URL,
+} from '#config/project';
+import {
+ Box,
+ Burger,
+ Button,
+ Drawer,
+ Flex,
+ Group,
+ Image,
+ rem,
+ useMantineTheme,
+} from '@mantine/core';
+import { useDisclosure, useMediaQuery } from '@mantine/hooks';
+import { useEffect } from 'react';
+import { UserDropdown } from '~/components/common/floating_navbar/user_dropdown';
+import { ExternalLinkUnstyled } from '~/components/common/links/external_link_unstyled';
+import { InternalLink } from '~/components/common/links/internal_link';
+import { useAuth } from '~/hooks/use_auth';
+import classes from './floating_navbar.module.css';
+
+interface FloatingNavbarProps {
+ width: string;
+}
+
+export function FloatingNavbar({ width }: FloatingNavbarProps) {
+ const auth = useAuth();
+ const theme = useMantineTheme();
+ const [opened, handler] = useDisclosure(false);
+ const isMobile = useMediaQuery(`(max-width: ${theme.breakpoints.sm})`, false);
+
+ useEffect(() => {
+ if (opened && !isMobile) {
+ handler.close();
+ }
+ }, [isMobile]);
+
+ const links = (
+ <>
+
+ Dashboard
+
+
+ Github
+
+
+ Extension
+
+ >
+ );
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+ {!isMobile && {links}}
+ {isMobile && }
+ {auth.isAuthenticated && }
+ {!auth.isAuthenticated && (
+
+ )}
+
+
+
+ {/* Mobile drawer */}
+
+
+ {links}
+
+
+
+ >
+ );
+}
diff --git a/inertia/components/common/floating_navbar/user_dropdown.module.css b/inertia/components/common/floating_navbar/user_dropdown.module.css
new file mode 100644
index 0000000..0f2b469
--- /dev/null
+++ b/inertia/components/common/floating_navbar/user_dropdown.module.css
@@ -0,0 +1,24 @@
+.user {
+ color: light-dark(var(--mantine-color-black), var(--mantine-color-dark-0));
+ padding: var(--mantine-spacing-xs) var(--mantine-spacing-sm);
+ border-radius: var(--mantine-radius-sm);
+ transition: background-color 100ms ease;
+
+ &:hover {
+ background-color: light-dark(
+ var(--mantine-color-white),
+ var(--mantine-color-dark-8)
+ );
+ }
+
+ @media (max-width: 768px) {
+ display: none;
+ }
+}
+
+.userActive {
+ background-color: light-dark(
+ var(--mantine-color-white),
+ var(--mantine-color-dark-8)
+ );
+}
diff --git a/inertia/components/common/floating_navbar/user_dropdown.tsx b/inertia/components/common/floating_navbar/user_dropdown.tsx
new file mode 100644
index 0000000..f7847ec
--- /dev/null
+++ b/inertia/components/common/floating_navbar/user_dropdown.tsx
@@ -0,0 +1,73 @@
+import { Avatar, Group, Menu, Text, UnstyledButton } from '@mantine/core';
+import { useDisclosure } from '@mantine/hooks';
+import cx from 'clsx';
+import { useTranslation } from 'react-i18next';
+import { TbChevronDown, TbLogout, TbUser } from 'react-icons/tb';
+import { InternalLink } from '~/components/common/links/internal_link';
+import { InternalLinkUnstyled } from '~/components/common/links/internal_link_unstyled';
+import { useAuth } from '~/hooks/use_auth';
+import classes from './user_dropdown.module.css';
+
+export function UserDropdown() {
+ const auth = useAuth();
+ const [userMenuOpened, { open: openUserMenu, close: closeUserMenu }] =
+ useDisclosure(false);
+ const { t } = useTranslation();
+ return (
+
+ );
+}
diff --git a/inertia/components/common/footer/footer.module.css b/inertia/components/common/footer/footer.module.css
new file mode 100644
index 0000000..066e6c8
--- /dev/null
+++ b/inertia/components/common/footer/footer.module.css
@@ -0,0 +1,23 @@
+.footer {
+ background-color: color-mix(
+ in srgb,
+ var(--mantine-color-body) 50%,
+ transparent
+ );
+ padding: var(--mantine-spacing-sm) var(--mantine-spacing-lg);
+}
+
+.footer__content {
+ max-width: 100%;
+ gap: var(--mantine-spacing-xs);
+ margin-inline: auto;
+}
+
+.footer__content p {
+ font-size: var(--mantine-font-size-sm) !important;
+ color: var(--mantine-color-dimmed) !important;
+}
+
+.footer__content a {
+ font-size: var(--mantine-font-size-sm) !important;
+}
diff --git a/inertia/components/common/footer/footer.tsx b/inertia/components/common/footer/footer.tsx
new file mode 100644
index 0000000..647fbe5
--- /dev/null
+++ b/inertia/components/common/footer/footer.tsx
@@ -0,0 +1,33 @@
+import { AUTHOR_GITHUB_URL, AUTHOR_NAME } from '#config/project';
+import PATHS from '#core/constants/paths';
+import { Anchor, Group, Text } from '@mantine/core';
+import ExternalLink from '~/components/common/external_link';
+import { ExternalLinkStyled } from '~/components/common/links/external_link_styled';
+import { LocaleSwitcher } from '~/components/common/locale_switcher';
+import { ThemeSwitcher } from '~/components/common/theme_switcher';
+import packageJson from '../../../../package.json';
+import classes from './footer.module.css';
+
+export const Footer = () => (
+
+
+
+ Made with ❤️ by{' '}
+
+ {AUTHOR_NAME}
+
+
+ •
+
+
+ {packageJson.version}
+
+
+ •
+
+
+
+
+
+
+);
diff --git a/inertia/components/common/links/external_link_styled.tsx b/inertia/components/common/links/external_link_styled.tsx
new file mode 100644
index 0000000..dd260ae
--- /dev/null
+++ b/inertia/components/common/links/external_link_styled.tsx
@@ -0,0 +1,28 @@
+import { Anchor } from '@mantine/core';
+import { AnchorHTMLAttributes, CSSProperties, ReactNode } from 'react';
+
+interface ExternalLinkStyledProps
+ extends AnchorHTMLAttributes {
+ children: ReactNode;
+ style?: CSSProperties;
+ title?: string;
+ className?: string;
+}
+
+export const ExternalLinkStyled = ({
+ children,
+ title,
+ href,
+ ...props
+}: ExternalLinkStyledProps) => (
+
+ component="a"
+ target="_blank"
+ rel="noreferrer"
+ title={title}
+ href={href}
+ {...props}
+ >
+ {children}
+
+);
diff --git a/inertia/components/common/links/external_link_unstyled.tsx b/inertia/components/common/links/external_link_unstyled.tsx
new file mode 100644
index 0000000..b4b1a52
--- /dev/null
+++ b/inertia/components/common/links/external_link_unstyled.tsx
@@ -0,0 +1,26 @@
+import { Anchor, CSSProperties } from '@mantine/core';
+import { AnchorHTMLAttributes, ReactNode } from 'react';
+
+interface ExternalLinkUnstyledProps
+ extends AnchorHTMLAttributes {
+ children: ReactNode;
+ style?: CSSProperties;
+ title?: string;
+ className?: string;
+ newTab?: boolean;
+}
+export const ExternalLinkUnstyled = ({
+ children,
+ newTab = true,
+ ...props
+}: ExternalLinkUnstyledProps) => (
+
+ {children}
+
+);
diff --git a/inertia/components/common/links/internal_link.tsx b/inertia/components/common/links/internal_link.tsx
new file mode 100644
index 0000000..1a6ce18
--- /dev/null
+++ b/inertia/components/common/links/internal_link.tsx
@@ -0,0 +1,50 @@
+import { ApiRouteName } from '#shared/types/index';
+import { Link } from '@inertiajs/react';
+import { Anchor } from '@mantine/core';
+import { useTuyau } from '@tuyau/inertia/react';
+import { CSSProperties } from 'react';
+
+interface InternalLinkProps {
+ children: React.ReactNode;
+ onClick?: (event: React.MouseEvent) => void;
+ route?: ApiRouteName;
+ href?: string;
+ forceRefresh?: boolean;
+ style?: CSSProperties;
+ className?: string;
+ params?: Record;
+}
+
+export const InternalLink = ({
+ children,
+ onClick,
+ route,
+ href,
+ forceRefresh,
+ style,
+ className,
+ params,
+}: InternalLinkProps) => {
+ const tuyau = useTuyau();
+
+ if ((!route && !href) || !tuyau) {
+ throw new Error('InternalLink: route, href or tuyau is missing');
+ }
+
+ const url = route ? tuyau.$route(route, params).path : href;
+ if (!url) {
+ throw new Error('InternalLink: url not found');
+ }
+
+ return (
+
+ component={forceRefresh ? 'a' : Link}
+ href={url}
+ style={style}
+ onClick={onClick}
+ className={className}
+ >
+ {children}
+
+ );
+};
diff --git a/inertia/components/common/links/internal_link_unstyled.tsx b/inertia/components/common/links/internal_link_unstyled.tsx
new file mode 100644
index 0000000..755d556
--- /dev/null
+++ b/inertia/components/common/links/internal_link_unstyled.tsx
@@ -0,0 +1,61 @@
+import { ApiRouteName } from '#shared/types/index';
+import { Link } from '@inertiajs/react';
+import { useTuyau } from '@tuyau/inertia/react';
+import { CSSProperties } from 'react';
+
+interface InternalLinkProps {
+ children: React.ReactNode;
+ onClick?: (event: React.MouseEvent) => void;
+ route?: ApiRouteName;
+ href?: string;
+ forceRefresh?: boolean;
+ style?: CSSProperties;
+ className?: string;
+ params?: Record;
+}
+
+export const InternalLinkUnstyled = ({
+ children,
+ onClick,
+ route,
+ href,
+ forceRefresh,
+ style,
+ className,
+ params,
+}: InternalLinkProps) => {
+ const tuyau = useTuyau();
+
+ if ((!route && !href) || !tuyau) {
+ throw new Error('InternalLink: route, href or tuyau is missing');
+ }
+
+ const url = route ? tuyau.$route(route, params).path : href;
+ if (!url) {
+ throw new Error('InternalLink: url not found');
+ }
+
+ if (forceRefresh) {
+ return (
+
+ {children}
+
+ );
+ }
+
+ return (
+
+ {children}
+
+ );
+};
diff --git a/inertia/components/common/language_switcher.tsx b/inertia/components/common/locale_switcher.tsx
similarity index 92%
rename from inertia/components/common/language_switcher.tsx
rename to inertia/components/common/locale_switcher.tsx
index 49799e0..06a5459 100644
--- a/inertia/components/common/language_switcher.tsx
+++ b/inertia/components/common/locale_switcher.tsx
@@ -2,7 +2,7 @@ import { ActionIcon, Image } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import { LS_LANG_KEY } from '~/constants';
-export function MantineLanguageSwitcher() {
+export function LocaleSwitcher() {
const { i18n } = useTranslation();
const newLanguage = i18n.language === 'en' ? 'fr' : 'en';
return (
diff --git a/inertia/components/common/theme_switcher.tsx b/inertia/components/common/theme_switcher.tsx
index 5e515a1..22f4d43 100644
--- a/inertia/components/common/theme_switcher.tsx
+++ b/inertia/components/common/theme_switcher.tsx
@@ -2,7 +2,7 @@ import { ActionIcon, useMantineColorScheme } from '@mantine/core';
import { TbMoonStars, TbSun } from 'react-icons/tb';
import { makeRequest } from '~/lib/request';
-export function MantineThemeSwitcher() {
+export function ThemeSwitcher() {
const { colorScheme, toggleColorScheme } = useMantineColorScheme();
const handleThemeChange = () => {
toggleColorScheme();
diff --git a/inertia/components/common/user_card.tsx b/inertia/components/common/user_card.tsx
index 920fe90..7318423 100644
--- a/inertia/components/common/user_card.tsx
+++ b/inertia/components/common/user_card.tsx
@@ -3,7 +3,7 @@ import { Avatar, Group, Menu, Text, UnstyledButton } from '@mantine/core';
import { forwardRef } from 'react';
import { useTranslation } from 'react-i18next';
import { TbChevronRight } from 'react-icons/tb';
-import useUser from '~/hooks/use_user';
+import useUser from '~/hooks/use_auth';
interface UserButtonProps extends React.ComponentPropsWithoutRef<'button'> {
image: string;
diff --git a/inertia/components/dashboard/dashboard_navbar.tsx b/inertia/components/dashboard/dashboard_navbar.tsx
index 630ac71..370805c 100644
--- a/inertia/components/dashboard/dashboard_navbar.tsx
+++ b/inertia/components/dashboard/dashboard_navbar.tsx
@@ -20,8 +20,8 @@ import { PiGearLight } from 'react-icons/pi';
import { MantineUserCard } from '~/components/common/user_card';
import { FavoriteList } from '~/components/dashboard/favorite/favorite_list';
import { SearchSpotlight } from '~/components/search/search';
+import useUser from '~/hooks/use_auth';
import useShortcut from '~/hooks/use_shortcut';
-import useUser from '~/hooks/use_user';
import { appendCollectionId } from '~/lib/navigation';
import { useActiveCollection } from '~/stores/collection_store';
import { useGlobalHotkeysStore } from '~/stores/global_hotkeys_store';
diff --git a/inertia/components/footer/footer.tsx b/inertia/components/footer/footer.tsx
index 2deffc0..fb14127 100644
--- a/inertia/components/footer/footer.tsx
+++ b/inertia/components/footer/footer.tsx
@@ -4,8 +4,8 @@ import { route } from '@izzyjs/route/client';
import { Anchor, Group, Text } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import ExternalLink from '~/components/common/external_link';
-import { MantineLanguageSwitcher } from '~/components/common/language_switcher';
-import { MantineThemeSwitcher } from '~/components/common/theme_switcher';
+import { LocaleSwitcher } from '~/components/common/locale_switcher';
+import { ThemeSwitcher } from '~/components/common/theme_switcher';
import packageJson from '../../../package.json';
import classes from './footer.module.css';
@@ -46,8 +46,8 @@ export function MantineFooter() {
-
-
+
+
diff --git a/inertia/components/navbar/navbar.tsx b/inertia/components/navbar/navbar.tsx
index b488e27..c5f2697 100644
--- a/inertia/components/navbar/navbar.tsx
+++ b/inertia/components/navbar/navbar.tsx
@@ -15,9 +15,9 @@ import {
import { useDisclosure } from '@mantine/hooks';
import { useTranslation } from 'react-i18next';
import ExternalLink from '~/components/common/external_link';
-import { MantineLanguageSwitcher } from '~/components/common/language_switcher';
-import { MantineThemeSwitcher } from '~/components/common/theme_switcher';
-import useUser from '~/hooks/use_user';
+import { LocaleSwitcher } from '~/components/common/locale_switcher';
+import { ThemeSwitcher } from '~/components/common/theme_switcher';
+import useUser from '~/hooks/use_auth';
import classes from './mobile.module.css';
export default function Navbar() {
@@ -47,8 +47,8 @@ export default function Navbar() {
-
-
+
+
{!isAuthenticated ? (
-
+
diff --git a/inertia/pages/admin/dashboard.tsx b/inertia/pages/admin/dashboard.tsx
index e23d0c2..1856274 100644
--- a/inertia/pages/admin/dashboard.tsx
+++ b/inertia/pages/admin/dashboard.tsx
@@ -1,15 +1,8 @@
-import { ReactNode } from 'react';
import {
UsersTable,
UsersTableProps,
} from '~/components/admin/users/users_table';
-import { ContentLayout } from '~/layouts/content_layout';
-function AdminDashboardPage(props: UsersTableProps) {
+export default function AdminDashboardPage(props: UsersTableProps) {
return ;
}
-
-AdminDashboardPage.layout = (page: ReactNode) => (
-
-);
-export default AdminDashboardPage;
diff --git a/inertia/pages/home.module.css b/inertia/pages/home.module.css
index bde5e18..e872eff 100644
--- a/inertia/pages/home.module.css
+++ b/inertia/pages/home.module.css
@@ -7,7 +7,6 @@
font-family:
Greycliff CF,
var(--mantine-font-family);
- font-weight: 900;
margin-bottom: var(--mantine-spacing-md);
text-align: center;
diff --git a/inertia/pages/home.tsx b/inertia/pages/home.tsx
index d3c709e..7d4677f 100644
--- a/inertia/pages/home.tsx
+++ b/inertia/pages/home.tsx
@@ -1,11 +1,9 @@
import { Container, Text, Title } from '@mantine/core';
-import { ReactNode } from 'react';
import { useTranslation } from 'react-i18next';
import { FeatureList } from '~/components/home/feature_list';
import classes from './home.module.css';
-import { ContentLayout } from '~/layouts/content_layout';
-function HomePage() {
+export default function HomePage() {
const { t } = useTranslation('about');
return (
@@ -21,6 +19,3 @@ function HomePage() {
);
}
-
-HomePage.layout = (page: ReactNode) => ;
-export default HomePage;
diff --git a/inertia/pages/privacy.tsx b/inertia/pages/privacy.tsx
index 3539fc4..badc928 100644
--- a/inertia/pages/privacy.tsx
+++ b/inertia/pages/privacy.tsx
@@ -1,8 +1,6 @@
-import { ReactNode } from 'react';
import { useTranslation } from 'react-i18next';
-import { ContentLayout } from '~/layouts/content_layout';
-function PrivacyPage() {
+export default function PrivacyPage() {
const { t } = useTranslation('privacy');
return (
<>
@@ -43,6 +41,3 @@ function PrivacyPage() {
>
);
}
-
-PrivacyPage.layout = (page: ReactNode) => ;
-export default PrivacyPage;
diff --git a/inertia/pages/shared.tsx b/inertia/pages/shared.tsx
index f3b43fa..f1582be 100644
--- a/inertia/pages/shared.tsx
+++ b/inertia/pages/shared.tsx
@@ -1,8 +1,7 @@
import { Flex, Text } from '@mantine/core';
-import { ReactNode, useEffect } from 'react';
+import { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { LinkList } from '~/components/dashboard/link/list/link_list';
-import { ContentLayout } from '~/layouts/content_layout';
import { useCollectionsSetter } from '~/stores/collection_store';
import type { CollectionWithLinks, PublicUser } from '~/types/app';
@@ -10,7 +9,7 @@ interface SharedPageProps {
collection: CollectionWithLinks & { author: PublicUser };
}
-function SharedPage({ collection }: SharedPageProps) {
+export default function SharedPage({ collection }: SharedPageProps) {
const { t } = useTranslation('common');
const { setActiveCollection } = useCollectionsSetter();
@@ -43,6 +42,3 @@ function SharedPage({ collection }: SharedPageProps) {
>
);
}
-
-SharedPage.layout = (page: ReactNode) => {page};
-export default SharedPage;
diff --git a/inertia/pages/terms.tsx b/inertia/pages/terms.tsx
index 7a6f799..3d5fcf4 100644
--- a/inertia/pages/terms.tsx
+++ b/inertia/pages/terms.tsx
@@ -1,10 +1,8 @@
import { Link } from '@inertiajs/react';
import { route } from '@izzyjs/route/client';
-import { ReactNode } from 'react';
import { Trans, useTranslation } from 'react-i18next';
-import { ContentLayout } from '~/layouts/content_layout';
-function TermsPage() {
+export default function TermsPage() {
const { t } = useTranslation('terms');
return (
<>
@@ -52,6 +50,3 @@ function TermsPage() {
>
);
}
-
-TermsPage.layout = (page: ReactNode) => ;
-export default TermsPage;
diff --git a/inertia/styles/index.css b/inertia/styles/index.css
index cc1a475..81f2a76 100644
--- a/inertia/styles/index.css
+++ b/inertia/styles/index.css
@@ -3,11 +3,26 @@
--ml-bg-dark: rgb(34, 40, 49);
}
-html,
-body {
- min-height: 100svh;
- width: 100%;
- background-color: light-dark(var(--ml-bg-light), var(--ml-bg-dark));
+/* Fix nprogress position */
+#nprogress {
+ position: relative;
+ z-index: 9999999;
+}
+
+#app {
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+}
+
+/* For light mode */
+:root[data-mantine-color-scheme='light'] {
+ --mantine-color-body: var(--ml-bg-light) !important;
+}
+
+/* For dark mode */
+:root[data-mantine-color-scheme='dark'] {
+ --mantine-color-body: var(--ml-bg-dark) !important;
}
.__transition_fadeIn {
diff --git a/package.json b/package.json
index f5e36ba..b852d88 100644
--- a/package.json
+++ b/package.json
@@ -22,6 +22,7 @@
"#adonis/api": "./.adonisjs/api.ts",
"#auth/*": "./app/auth/*.js",
"#collections/*": "./app/collections/*.js",
+ "#config/*": "./config/*.js",
"#core/*": "./app/core/*.js",
"#favicons/*": "./app/favicons/*.js",
"#home/*": "./app/home/*.js",
@@ -34,8 +35,7 @@
"#database/*": "./database/*.js",
"#tests/*": "./tests/*.js",
"#shared/*": "./shared/*.js",
- "#start/*": "./start/*.js",
- "#config/*": "./config/*.js"
+ "#start/*": "./start/*.js"
},
"devDependencies": {
"@adonisjs/assembler": "^7.8.2",
@@ -90,6 +90,7 @@
"@vinejs/vine": "^3.0.1",
"@vitejs/plugin-react-oxc": "^0.3.0",
"bentocache": "^1.5.0",
+ "clsx": "^2.1.1",
"dayjs": "^1.11.13",
"edge.js": "^6.3.0",
"i18next": "^25.3.2",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index bb0e9e8..16845b4 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -74,6 +74,9 @@ importers:
bentocache:
specifier: ^1.5.0
version: 1.5.0(knex@3.1.0(pg@8.16.3))
+ clsx:
+ specifier: ^2.1.1
+ version: 2.1.1
dayjs:
specifier: ^1.11.13
version: 1.11.13
diff --git a/vite.config.ts b/vite.config.ts
index c44a9e4..e4bbd88 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,4 +1,4 @@
-import project from '#config/project';
+import { APP_COLOR, PROJECT_DESCRIPTION, PROJECT_NAME } from '#config/project';
import { getDirname } from '@adonisjs/core/helpers';
import inertia from '@adonisjs/inertia/client';
import adonisjs from '@adonisjs/vite/client';
@@ -16,11 +16,11 @@ export default defineConfig({
enabled: true,
},
manifest: {
- name: project.name,
- short_name: project.name,
- description: project.description,
- theme_color: project.color,
- background_color: project.color,
+ name: PROJECT_NAME,
+ short_name: PROJECT_NAME,
+ description: PROJECT_DESCRIPTION,
+ theme_color: APP_COLOR,
+ background_color: APP_COLOR,
scope: '/',
display: 'standalone',
orientation: 'portrait',