feat(frontend): main page
This commit is contained in:
11
.env.example
11
.env.example
@@ -1,3 +1,12 @@
|
||||
APP_ENVIRONMENT=dev
|
||||
APP__EMAIL__HOST=mail.example.com
|
||||
APP__EMAIL__PORT=465
|
||||
APP__EMAIL__TLS=true
|
||||
APP__EMAIL__STARTTLS=no
|
||||
APP__EMAIL__USER="username"
|
||||
APP__EMAIL__PASSWORD="changeme"
|
||||
APP__EMAIL__RECIPIENT="Recipient <user@example.com>"
|
||||
APP__EMAIL__FROM="Contact Form <noreply@example.com>"
|
||||
NUXT_PUBLIC_BACKEND_URL=http://localhost:3100
|
||||
NUXT_PUBLIC_TURNSTILE_SITE_KEY="changeme"
|
||||
NUXT_TURNSTILE_SECRET_KEY="changeme"
|
||||
NUXT_BACKEND=http://localhost:3001
|
||||
|
||||
5
.envrc.local
Normal file
5
.envrc.local
Normal file
@@ -0,0 +1,5 @@
|
||||
export NIX_SHELL_NAME=frontend
|
||||
|
||||
# Local Variables:
|
||||
# mode: dotenv
|
||||
# End:
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -34,3 +34,4 @@ node_modules
|
||||
|
||||
# Nix
|
||||
result
|
||||
.data/
|
||||
|
||||
9
.volarrc
Normal file
9
.volarrc
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"vueCompilerOptions": {
|
||||
"target": 3.5,
|
||||
"extensions": [".vue"]
|
||||
},
|
||||
"typescript": {
|
||||
"tsdk": "frontend/node_modules/typescript/lib"
|
||||
}
|
||||
}
|
||||
@@ -107,3 +107,28 @@ bun run preview
|
||||
#+end_src
|
||||
|
||||
Check out the [[https://nuxt.com/docs/getting-started/deployment][deployment documentation]] for more information.
|
||||
|
||||
* Known Issues
|
||||
** =better-sqlite3= self-registration error
|
||||
If you encounter an error stating that =better-sqlite3= does not
|
||||
self-register when running =pnpm run dev=, this is typically caused by
|
||||
the native module being compiled for a different Node.js version.
|
||||
|
||||
*Solution:* Rebuild the native module for your current Node.js version:
|
||||
|
||||
#+begin_src bash
|
||||
# Rebuild just better-sqlite3
|
||||
pnpm rebuild better-sqlite3
|
||||
|
||||
# Or rebuild all native modules
|
||||
pnpm rebuild
|
||||
|
||||
# Or reinstall everything (nuclear option)
|
||||
rm -rf node_modules
|
||||
pnpm install
|
||||
#+end_src
|
||||
|
||||
*Why this happens:* =better-sqlite3= contains native C++ code that
|
||||
needs to be compiled for each specific Node.js version. When you
|
||||
update Node.js or switch between versions, native modules need to be
|
||||
rebuilt.
|
||||
|
||||
@@ -1,5 +1,25 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Nuxt works!</h1>
|
||||
</div>
|
||||
<UApp :locale="locales[locale]">
|
||||
<AppNavbar />
|
||||
<UMain>
|
||||
<NuxtLayout>
|
||||
<NuxtPage />
|
||||
</NuxtLayout>
|
||||
</UMain>
|
||||
<AppFooter />
|
||||
</UApp>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import * as locales from '@nuxt/ui/locale';
|
||||
const { locale } = useI18n();
|
||||
const lang = computed(() => locales[locale.value].code);
|
||||
const dir = computed(() => locales[locale.value].dir);
|
||||
|
||||
useHead({
|
||||
htmlAttrs: {
|
||||
dir,
|
||||
lang,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
132
frontend/app/assets/css/colors.css
Normal file
132
frontend/app/assets/css/colors.css
Normal file
@@ -0,0 +1,132 @@
|
||||
:root {
|
||||
--text-50: oklch(96.68% 0.005 95.1);
|
||||
--text-100: oklch(93.31% 0.012 96.43);
|
||||
--text-200: oklch(86.46% 0.023 98.68);
|
||||
--text-300: oklch(79.55% 0.036 98.17);
|
||||
--text-400: oklch(72.45% 0.047 99.12);
|
||||
--text-500: oklch(65.27% 0.06 98.88);
|
||||
--text-600: oklch(55.54% 0.05 99.33);
|
||||
--text-700: oklch(45.43% 0.04 98.55);
|
||||
--text-800: oklch(34.63% 0.028 99.26);
|
||||
--text-900: oklch(22.99% 0.017 97.01);
|
||||
--text: oklch(17.69% 0.01 97.92);
|
||||
--text-950: oklch(16.34% 0.008 95.54);
|
||||
|
||||
--background: oklch(97.33% 0.007 88.64);
|
||||
--background-50: oklch(96.7% 0.008 91.48);
|
||||
--background-100: oklch(93.46% 0.017 88);
|
||||
--background-200: oklch(86.85% 0.034 88.07);
|
||||
--background-300: oklch(80.17% 0.051 88.07);
|
||||
--background-400: oklch(73.62% 0.069 89.26);
|
||||
--background-500: oklch(66.8% 0.085 88.59);
|
||||
--background-600: oklch(56.88% 0.071 88.9);
|
||||
--background-700: oklch(46.26% 0.056 87.6);
|
||||
--background-800: oklch(35.24% 0.04 87.71);
|
||||
--background-900: oklch(23.27% 0.023 87.9);
|
||||
--background-950: oklch(16.86% 0.012 91.89);
|
||||
|
||||
--primary-50: oklch(97.22% 0.012 96.42);
|
||||
--primary-100: oklch(94.41% 0.025 97.12);
|
||||
--primary-200: oklch(88.75% 0.05 98.42);
|
||||
--primary-300: oklch(83.15% 0.074 98.36);
|
||||
--primary-400: oklch(77.55% 0.097 98.29);
|
||||
--primary: oklch(74.12% 0.109 98.34);
|
||||
--primary-500: oklch(72% 0.116 97.93);
|
||||
--primary-600: oklch(61.14% 0.097 98.09);
|
||||
--primary-700: oklch(49.77% 0.077 98.34);
|
||||
--primary-800: oklch(37.71% 0.055 98.79);
|
||||
--primary-900: oklch(24.68% 0.033 97.74);
|
||||
--primary-950: oklch(17.23% 0.018 97.53);
|
||||
|
||||
--secondary-50: oklch(97.69% 0.019 100.12);
|
||||
--secondary-100: oklch(95.28% 0.036 96.71);
|
||||
--secondary-200: oklch(90.57% 0.07 97.74);
|
||||
--secondary-300: oklch(86.23% 0.103 98.42);
|
||||
--secondary: oklch(83.86% 0.116 98.04);
|
||||
--secondary-400: oklch(81.72% 0.129 98.31);
|
||||
--secondary-500: oklch(77.44% 0.146 97.07);
|
||||
--secondary-600: oklch(65.69% 0.123 97.5);
|
||||
--secondary-700: oklch(53.48% 0.099 97.52);
|
||||
--secondary-800: oklch(40.18% 0.072 97.19);
|
||||
--secondary-900: oklch(26.04% 0.043 96.76);
|
||||
--secondary-950: oklch(18.17% 0.026 97.52);
|
||||
|
||||
--accent-50: oklch(97.77% 0.019 96.86);
|
||||
--accent-100: oklch(95.53% 0.039 97.44);
|
||||
--accent-200: oklch(91.16% 0.076 97.81);
|
||||
--accent-300: oklch(86.92% 0.11 97.94);
|
||||
--accent: oklch(82.74% 0.136 98);
|
||||
--accent-400: oklch(82.74% 0.136 98);
|
||||
--accent-500: oklch(78.81% 0.152 96.76);
|
||||
--accent-600: oklch(66.8% 0.128 96.97);
|
||||
--accent-700: oklch(54.33% 0.103 96.65);
|
||||
--accent-800: oklch(40.98% 0.076 96.95);
|
||||
--accent-900: oklch(26.42% 0.045 97.53);
|
||||
--accent-950: oklch(18.44% 0.029 102.49);
|
||||
}
|
||||
.dark {
|
||||
--text-50: oklch(16.34% 0.008 95.54);
|
||||
--text: oklch(96.05% 0.007 97.35);
|
||||
--text-100: oklch(22.99% 0.017 97.01);
|
||||
--text-200: oklch(34.63% 0.028 99.26);
|
||||
--text-300: oklch(45.43% 0.04 98.55);
|
||||
--text-400: oklch(55.54% 0.05 99.33);
|
||||
--text-500: oklch(65.27% 0.06 98.88);
|
||||
--text-600: oklch(72.45% 0.047 99.12);
|
||||
--text-700: oklch(79.55% 0.036 98.17);
|
||||
--text-800: oklch(86.46% 0.023 98.68);
|
||||
--text-900: oklch(93.31% 0.012 96.43);
|
||||
--text-950: oklch(96.68% 0.005 95.1);
|
||||
|
||||
--background-50: oklch(16.86% 0.012 91.89);
|
||||
--background-100: oklch(23.27% 0.023 87.9);
|
||||
--background-200: oklch(35.24% 0.04 87.71);
|
||||
--background-300: oklch(46.26% 0.056 87.6);
|
||||
--background-400: oklch(56.88% 0.071 88.9);
|
||||
--background-500: oklch(66.8% 0.085 88.59);
|
||||
--background-600: oklch(73.62% 0.069 89.26);
|
||||
--background-700: oklch(80.17% 0.051 88.07);
|
||||
--background-800: oklch(86.85% 0.034 88.07);
|
||||
--background-900: oklch(93.46% 0.017 88);
|
||||
--background-950: oklch(96.7% 0.008 91.48);
|
||||
--background: oklch(15.48% 0.011 89.86);
|
||||
|
||||
--primary-50: oklch(17.23% 0.018 97.53);
|
||||
--primary-100: oklch(24.68% 0.033 97.74);
|
||||
--primary-200: oklch(37.71% 0.055 98.79);
|
||||
--primary-300: oklch(49.77% 0.077 98.34);
|
||||
--primary-400: oklch(61.14% 0.097 98.09);
|
||||
--primary: oklch(67.74% 0.108 98.2);
|
||||
--primary-500: oklch(72% 0.116 97.93);
|
||||
--primary-600: oklch(77.55% 0.097 98.29);
|
||||
--primary-700: oklch(83.15% 0.074 98.36);
|
||||
--primary-800: oklch(88.75% 0.05 98.42);
|
||||
--primary-900: oklch(94.41% 0.025 97.12);
|
||||
--primary-950: oklch(97.22% 0.012 96.42);
|
||||
|
||||
--secondary-50: oklch(18.17% 0.026 97.52);
|
||||
--secondary-100: oklch(26.04% 0.043 96.76);
|
||||
--secondary-200: oklch(40.18% 0.072 97.19);
|
||||
--secondary-300: oklch(53.48% 0.099 97.52);
|
||||
--secondary: oklch(59.61% 0.111 97.84);
|
||||
--secondary-400: oklch(65.69% 0.123 97.5);
|
||||
--secondary-500: oklch(77.44% 0.146 97.07);
|
||||
--secondary-600: oklch(81.72% 0.129 98.31);
|
||||
--secondary-700: oklch(86.23% 0.103 98.42);
|
||||
--secondary-800: oklch(90.57% 0.07 97.74);
|
||||
--secondary-900: oklch(95.28% 0.036 96.71);
|
||||
--secondary-950: oklch(97.69% 0.019 100.12);
|
||||
|
||||
--accent-50: oklch(18.44% 0.029 102.49);
|
||||
--accent-100: oklch(26.42% 0.045 97.53);
|
||||
--accent-200: oklch(40.98% 0.076 96.95);
|
||||
--accent-300: oklch(54.33% 0.103 96.65);
|
||||
--accent: oklch(66.8% 0.128 96.97);
|
||||
--accent-400: oklch(66.8% 0.128 96.97);
|
||||
--accent-500: oklch(78.81% 0.152 96.76);
|
||||
--accent-600: oklch(82.74% 0.136 98);
|
||||
--accent-700: oklch(86.92% 0.11 97.94);
|
||||
--accent-800: oklch(91.16% 0.076 97.81);
|
||||
--accent-900: oklch(95.53% 0.039 97.44);
|
||||
--accent-950: oklch(97.77% 0.019 96.86);
|
||||
}
|
||||
7
frontend/app/assets/css/main.css
Normal file
7
frontend/app/assets/css/main.css
Normal file
@@ -0,0 +1,7 @@
|
||||
@import 'tailwindcss';
|
||||
@import '@nuxt/ui';
|
||||
@import './colors.css';
|
||||
@import './ui/index.css';
|
||||
@import './tailwind.css';
|
||||
|
||||
@source "../../../content/**/*";
|
||||
84
frontend/app/assets/css/tailwind.css
Normal file
84
frontend/app/assets/css/tailwind.css
Normal file
@@ -0,0 +1,84 @@
|
||||
@layer base {
|
||||
--color-text-50: var(--text-50);
|
||||
--color-text: var(--text);
|
||||
--color-text-100: var(--text-100);
|
||||
--color-text-200: var(--text-200);
|
||||
--color-text-300: var(--text-300);
|
||||
--color-text-400: var(--text-400);
|
||||
--color-text-500: var(--text-500);
|
||||
--color-text-600: var(--text-600);
|
||||
--color-text-700: var(--text-700);
|
||||
--color-text-800: var(--text-800);
|
||||
--color-text-900: var(--text-900);
|
||||
--color-text-950: var(--text-950);
|
||||
|
||||
--color-background-50: var(--background-50);
|
||||
--color-background-100: var(--background-100);
|
||||
--color-background-200: var(--background-200);
|
||||
--color-background-300: var(--background-300);
|
||||
--color-background-400: var(--background-400);
|
||||
--color-background-500: var(--background-500);
|
||||
--color-background-600: var(--background-600);
|
||||
--color-background-700: var(--background-700);
|
||||
--color-background-800: var(--background-800);
|
||||
--color-background-900: var(--background-900);
|
||||
--color-background-950: var(--background-950);
|
||||
--color-background: var(--background);
|
||||
|
||||
--color-primary-50: var(--primary-50);
|
||||
--color-primary-100: var(--primary-100);
|
||||
--color-primary-200: var(--primary-200);
|
||||
--color-primary-300: var(--primary-300);
|
||||
--color-primary-400: var(--primary-400);
|
||||
--color-primary: var(--primary);
|
||||
--color-primary-500: var(--primary-500);
|
||||
--color-primary-600: var(--primary-600);
|
||||
--color-primary-700: var(--primary-700);
|
||||
--color-primary-800: var(--primary-800);
|
||||
--color-primary-900: var(--primary-900);
|
||||
--color-primary-950: var(--primary-950);
|
||||
|
||||
--color-secondary-50: var(--secondary-50);
|
||||
--color-secondary-100: var(--secondary-100);
|
||||
--color-secondary-200: var(--secondary-200);
|
||||
--color-secondary-300: var(--secondary-300);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-secondary-400: var(--secondary-400);
|
||||
--color-secondary-500: var(--secondary-500);
|
||||
--color-secondary-600: var(--secondary-600);
|
||||
--color-secondary-700: var(--secondary-700);
|
||||
--color-secondary-800: var(--secondary-800);
|
||||
--color-secondary-900: var(--secondary-900);
|
||||
--color-secondary-950: var(--secondary-950);
|
||||
|
||||
--color-accent-50: var(--accent-50);
|
||||
--color-accent-100: var(--accent-100);
|
||||
--color-accent-200: var(--accent-200);
|
||||
--color-accent-300: var(--accent-300);
|
||||
--color-accent: var(--accent);
|
||||
--color-accent-400: var(--accent-400);
|
||||
--color-accent-500: var(--accent-500);
|
||||
--color-accent-600: var(--accent-600);
|
||||
--color-accent-700: var(--accent-700);
|
||||
--color-accent-800: var(--accent-800);
|
||||
--color-accent-900: var(--accent-900);
|
||||
--color-accent-950: var(--accent-950);
|
||||
|
||||
--text-sm: 0.75rem;
|
||||
--text-base: 1rem;
|
||||
--text-xl: 1.333rem;
|
||||
--text-2xl: 1.777rem;
|
||||
--text-3xl: 2.369rem;
|
||||
--text-4xl: 3.158rem;
|
||||
--text-5xl: 4.21rem;
|
||||
|
||||
--text-weight-normal: 400;
|
||||
--text-weight-bold: 700;
|
||||
|
||||
--font-sans:
|
||||
Noto Sans, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji",
|
||||
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
--font-title:
|
||||
Wittgenstein, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji",
|
||||
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
}
|
||||
14
frontend/app/assets/css/ui/background.css
Normal file
14
frontend/app/assets/css/ui/background.css
Normal file
@@ -0,0 +1,14 @@
|
||||
:root {
|
||||
--ui-bg: var(--background);
|
||||
--ui-bg-muted: var(--background-300);
|
||||
--ui-bg-elevated: var(--background-100);
|
||||
--ui-bg-accented: var(--backgsound-200);
|
||||
--ui-bg-inverted: var(--background-900);
|
||||
}
|
||||
.dark {
|
||||
--ui-bg: var(--background);
|
||||
--ui-bg-muted: var(--background-100);
|
||||
--ui-bg-elevated: var(--background-200);
|
||||
--ui-bg-accented: var(--background-300);
|
||||
--ui-bg-inverted: var(--background-900);
|
||||
}
|
||||
12
frontend/app/assets/css/ui/border.css
Normal file
12
frontend/app/assets/css/ui/border.css
Normal file
@@ -0,0 +1,12 @@
|
||||
:root {
|
||||
--ui-border: var(--background-200);
|
||||
--ui-border-muted: var(--background-200);
|
||||
--ui-border-accented: var(--background-300);
|
||||
--ui-border-inverted: var(--background-900);
|
||||
}
|
||||
.dark {
|
||||
--ui-border: var(--background-100);
|
||||
--ui-border-muted: var(--background-200);
|
||||
--ui-border-accented: var(--background-200);
|
||||
--ui-border-inverted: var(--background-900);
|
||||
}
|
||||
16
frontend/app/assets/css/ui/colors.css
Normal file
16
frontend/app/assets/css/ui/colors.css
Normal file
@@ -0,0 +1,16 @@
|
||||
:root {
|
||||
--ui-primary: var(--color-primary);
|
||||
--ui-secondary: var(--color-secondary);
|
||||
--ui-success: var(--color-accent);
|
||||
--ui-info: var(--ui-color-info-500);
|
||||
--ui-warning: var(--ui-color-warning-500);
|
||||
--ui-error: var(--ui-color-error-500);
|
||||
}
|
||||
.dark {
|
||||
--ui-primary: var(--color-primary-dark);
|
||||
--ui-secondary: var(--color-secondary-dark);
|
||||
--ui-success: var(--color-accent);
|
||||
--ui-info: var(--ui-color-info-400);
|
||||
--ui-warning: var(--ui-color-warning-400);
|
||||
--ui-error: var(--ui-color-error-400);
|
||||
}
|
||||
4
frontend/app/assets/css/ui/index.css
Normal file
4
frontend/app/assets/css/ui/index.css
Normal file
@@ -0,0 +1,4 @@
|
||||
@import "./colors.css";
|
||||
@import "./text.css";
|
||||
@import "./background.css";
|
||||
@import "./border.css";
|
||||
16
frontend/app/assets/css/ui/text.css
Normal file
16
frontend/app/assets/css/ui/text.css
Normal file
@@ -0,0 +1,16 @@
|
||||
:root {
|
||||
--ui-text-dimmed: var(--text-400);
|
||||
--ui-text-muted: var(--text-500);
|
||||
--ui-text-toned: var(--text-600);
|
||||
--ui-text: var(--text);
|
||||
--ui-text-highlighted: var(--text-900);
|
||||
--ui-text-inverted: var(--text-50);
|
||||
}
|
||||
.dark {
|
||||
--ui-text-dimmed: var(--text-500);
|
||||
--ui-text-muted: var(--text-400);
|
||||
--ui-text-toned: var(--text-300);
|
||||
--ui-text: var(--text);
|
||||
--ui-text-highlighted: var(--text);
|
||||
--ui-text-inverted: var(--text-50);
|
||||
}
|
||||
33
frontend/app/components/AppFooter.vue
Normal file
33
frontend/app/components/AppFooter.vue
Normal file
@@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<UFooter>
|
||||
<template #left>
|
||||
<p class="text-300 txt-sm">
|
||||
Copyright © {{ new Date().getFullYear() }}
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<UNavigationMenu :items="items" variant="link" />
|
||||
|
||||
<template #right>
|
||||
<UButton
|
||||
icon="i-simple-icons-github"
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
to="https://github.com/Phundrak"
|
||||
target="_blank"
|
||||
aria-label="GitHub"
|
||||
/>
|
||||
</template>
|
||||
</UFooter>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { NavigationMenuItem } from '@nuxt/ui';
|
||||
|
||||
const items = computed<NavigationMenuItem[]>(() => [
|
||||
{
|
||||
label: $t('footer.links.source'),
|
||||
to: 'https://labs.phundrak.com/phundrak/phundrak.com',
|
||||
},
|
||||
]);
|
||||
</script>
|
||||
29
frontend/app/components/AppNavbar.vue
Normal file
29
frontend/app/components/AppNavbar.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<UHeader toggle-side="right" mode="drawer">
|
||||
<template #title> Phundrak </template>
|
||||
<UNavigationMenu :items="items" />
|
||||
<template #right>
|
||||
<NavbarLanguageSwitcher />
|
||||
<NavbarThemeSwitcher />
|
||||
</template>
|
||||
<template #body>
|
||||
<UNavigationMenu :items="items" orientation="vertical" class="-mx-2.5" />
|
||||
</template>
|
||||
</UHeader>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const route = useRoute();
|
||||
const items = computed<NavigationMenuItem[]>(() => [
|
||||
{
|
||||
label: $t('pages.home.name'),
|
||||
to: '/',
|
||||
active: route.path == '/',
|
||||
},
|
||||
...['resume', 'vocal-synthesis', 'languages', 'contact'].map((page) => ({
|
||||
label: $t(`pages.${page}.name`),
|
||||
to: `/${page}`,
|
||||
active: route.path.startsWith(`/${page}`),
|
||||
})),
|
||||
]);
|
||||
</script>
|
||||
30
frontend/app/components/navbar/LanguageSwitcher.vue
Normal file
30
frontend/app/components/navbar/LanguageSwitcher.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<UDropdownMenu
|
||||
:key="locale"
|
||||
:items="availableLocales"
|
||||
:content="{ align: 'start' }"
|
||||
>
|
||||
<UButton color="neutral" variant="outline" icon="material-symbols:globe" />
|
||||
</UDropdownMenu>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { DropdownMenuItem } from '@nuxt/ui';
|
||||
const { locale, locales, setLocale } = useI18n();
|
||||
|
||||
const availableLocales = computed(() => {
|
||||
return locales.value.map(
|
||||
(optionLocale) =>
|
||||
({
|
||||
label: optionLocale.name,
|
||||
code: optionLocale.code,
|
||||
type: 'checkbox' as const,
|
||||
checked: optionLocale.code === locale.value,
|
||||
onUpdateChecked: () => switchLocale(optionLocale.code),
|
||||
}) as DropdownMenuItem,
|
||||
);
|
||||
});
|
||||
const switchLocale = (newLocale: string) => {
|
||||
setLocale(newLocale);
|
||||
};
|
||||
</script>
|
||||
31
frontend/app/components/navbar/ThemeSwitcher.vue
Normal file
31
frontend/app/components/navbar/ThemeSwitcher.vue
Normal file
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<UDropdownMenu
|
||||
:key="colorMode.preference"
|
||||
:items="themes"
|
||||
:content="{ align: 'start' }"
|
||||
>
|
||||
<UButton color="neutral" variant="outline" :icon="icons[currentColor]" />
|
||||
</UDropdownMenu>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
type Theme = 'light' | 'dark' | 'system';
|
||||
const icons: Dictionary<Theme, string> = {
|
||||
light: 'material-symbols:light-mode',
|
||||
dark: 'material-symbols:dark-mode',
|
||||
system: 'material-symbols:computer-outline',
|
||||
};
|
||||
const colorMode = useColorMode();
|
||||
const currentColor = computed<Theme>(() => colorMode.preference ?? 'system');
|
||||
const themes = computed<DropdownValue[]>(() =>
|
||||
['light', 'dark', 'system'].map((theme) => ({
|
||||
code: theme,
|
||||
label: $t(`theme.${theme}`),
|
||||
icon: icons[theme],
|
||||
type: 'checkbox' as const,
|
||||
checked: currentColor.value === theme,
|
||||
onUpdateChecked: () => switchColor(theme),
|
||||
})),
|
||||
);
|
||||
const switchColor = (theme: Theme) => (colorMode.preference = theme);
|
||||
</script>
|
||||
5
frontend/app/layouts/default.vue
Normal file
5
frontend/app/layouts/default.vue
Normal file
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
5
frontend/app/pages/contact.vue
Normal file
5
frontend/app/pages/contact.vue
Normal file
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
{{ $t('pages.contact.name') }}
|
||||
</div>
|
||||
</template>
|
||||
3
frontend/app/pages/index.vue
Normal file
3
frontend/app/pages/index.vue
Normal file
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<h1>{{ $t('website.name') }}</h1>
|
||||
</template>
|
||||
5
frontend/app/pages/languages.vue
Normal file
5
frontend/app/pages/languages.vue
Normal file
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
{{ $t('pages.languages.name') }}
|
||||
</div>
|
||||
</template>
|
||||
5
frontend/app/pages/resume.vue
Normal file
5
frontend/app/pages/resume.vue
Normal file
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
{{ $t('pages.resume.name') }}
|
||||
</div>
|
||||
</template>
|
||||
5
frontend/app/pages/vocal-synthesis.vue
Normal file
5
frontend/app/pages/vocal-synthesis.vue
Normal file
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
{{ $t('pages.vocal-synthesis.name') }}
|
||||
</div>
|
||||
</template>
|
||||
3
frontend/app/types/dictionary.ts
Normal file
3
frontend/app/types/dictionary.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export interface Dictionary<K, T> {
|
||||
[key: K]: T
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
<template>
|
||||
<nav>
|
||||
<!-- Your navbar content here -->
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -1,6 +1,5 @@
|
||||
import { defineCollection, defineContentConfig } from '@nuxt/content';
|
||||
import { z } from 'zod';
|
||||
z;
|
||||
|
||||
const commonSchema = z.object({});
|
||||
|
||||
|
||||
37
frontend/i18n/locales/en.json
Normal file
37
frontend/i18n/locales/en.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"website": {
|
||||
"name": "Lucien Cartier-Tilet",
|
||||
"langSwitch": "Language"
|
||||
},
|
||||
"menu": {
|
||||
"name": "Menu"
|
||||
},
|
||||
"theme": {
|
||||
"name": "theme",
|
||||
"dark": "Dark",
|
||||
"light": "Light",
|
||||
"system": "System"
|
||||
},
|
||||
"pages": {
|
||||
"home": {
|
||||
"name": "Home"
|
||||
},
|
||||
"resume": {
|
||||
"name": "Resume"
|
||||
},
|
||||
"vocal-synthesis": {
|
||||
"name": "Vocal Synthesis"
|
||||
},
|
||||
"languages": {
|
||||
"name": "Languages & Worldbuilding"
|
||||
},
|
||||
"contact": {
|
||||
"name": "Contact"
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
"links": {
|
||||
"source": "Website’s source code"
|
||||
}
|
||||
}
|
||||
}
|
||||
37
frontend/i18n/locales/fr.json
Normal file
37
frontend/i18n/locales/fr.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"website": {
|
||||
"name": "Lucien Cartier-Tilet",
|
||||
"langSwitch": "Language"
|
||||
},
|
||||
"menu": {
|
||||
"name": "Menu"
|
||||
},
|
||||
"theme": {
|
||||
"name": "Thème",
|
||||
"dark": "Sombre",
|
||||
"light": "Clair",
|
||||
"system": "Système"
|
||||
},
|
||||
"pages": {
|
||||
"home": {
|
||||
"name": "Accueil"
|
||||
},
|
||||
"resume": {
|
||||
"name": "CV"
|
||||
},
|
||||
"vocal-synthesis": {
|
||||
"name": "Synthèse Vocale"
|
||||
},
|
||||
"languages": {
|
||||
"name": "Langues et Univers Fictifs"
|
||||
},
|
||||
"contact": {
|
||||
"name": "Contact"
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
"links": {
|
||||
"source": "Code source du site web "
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,16 +8,22 @@ export default defineNuxtConfig({
|
||||
},
|
||||
|
||||
modules: [
|
||||
'@nuxt/content',
|
||||
'@nuxt/eslint',
|
||||
'@nuxt/image',
|
||||
'@nuxt/test-utils',
|
||||
'@nuxt/ui',
|
||||
'@nuxt/content',
|
||||
'@vueuse/nuxt',
|
||||
'@nuxtjs/i18n',
|
||||
'@nuxtjs/turnstile',
|
||||
'@nuxtjs/device',
|
||||
'@nuxt/icon',
|
||||
'@nuxt/fonts',
|
||||
'@nuxtjs/color-mode',
|
||||
'@nuxtjs/tailwindcss',
|
||||
],
|
||||
|
||||
css: ['~/assets/css/main.css'],
|
||||
content: {
|
||||
database: {
|
||||
type: 'sqlite',
|
||||
@@ -26,15 +32,40 @@ export default defineNuxtConfig({
|
||||
},
|
||||
i18n: {
|
||||
locales: [
|
||||
{ code: 'en', name: 'English', language: 'en-UK' },
|
||||
{ code: 'fr', name: 'Français', language: 'fr-FR' },
|
||||
{ code: 'lfn', name: 'Lingua Franca Nova', language: 'lfn' },
|
||||
{ code: 'en', name: 'English', language: 'en-UK', file: 'en.json' },
|
||||
{ code: 'fr', name: 'Français', language: 'fr-FR', file: 'fr.json' },
|
||||
// { code: 'lfn', name: 'Lingua Franca Nova', language: 'lfn', file: 'lfn.json' },
|
||||
// { code: 'ei', name: 'Eittlandic', language: 'ei-ST', file: 'ei.json' },
|
||||
],
|
||||
strategy: 'prefix_except_default',
|
||||
strategy: 'no_prefix',
|
||||
defaultLocale: 'en',
|
||||
},
|
||||
fonts: {
|
||||
provider: 'google',
|
||||
processCSSVariables: true,
|
||||
defaults: {
|
||||
weights: [400, 700],
|
||||
styles: ['normal', 'italic'],
|
||||
},
|
||||
families: [
|
||||
{ name: 'Noto Sans', provider: 'google' },
|
||||
{ name: 'Wittgenstein', provider: 'google' }
|
||||
]
|
||||
},
|
||||
icon: {
|
||||
serverBundle: {
|
||||
collections: ['material-symbols']
|
||||
}
|
||||
},
|
||||
postcss: {
|
||||
plugins: {
|
||||
'@tailwindcss/postcss': {},
|
||||
'autoprefixer': {}
|
||||
}
|
||||
},
|
||||
turnstile: {
|
||||
siteKey: '', // Overridden by NUXT_PUBLIC_TURNSTILE_SITE_KEY
|
||||
addValidateEndpoint: true
|
||||
},
|
||||
runtimeConfig: {
|
||||
turnstile: {
|
||||
|
||||
@@ -13,11 +13,17 @@
|
||||
"dependencies": {
|
||||
"@nuxt/content": "3.8.0",
|
||||
"@nuxt/eslint": "1.10.0",
|
||||
"@nuxt/fonts": "0.12.1",
|
||||
"@nuxt/icon": "2.1.0",
|
||||
"@nuxt/image": "1.11.0",
|
||||
"@nuxt/scripts": "^0.12.2",
|
||||
"@nuxt/test-utils": "3.20.1",
|
||||
"@nuxt/ui": "4.1.0",
|
||||
"@nuxtjs/color-mode": "3.5.2",
|
||||
"@nuxtjs/device": "3.2.4",
|
||||
"@nuxtjs/tailwindcss": "7.0.0-beta.0",
|
||||
"@nuxtjs/turnstile": "1.1.1",
|
||||
"@vueuse/core": "^14.0.0",
|
||||
"@vueuse/nuxt": "14.0.0",
|
||||
"better-sqlite3": "^12.4.1",
|
||||
"eslint": "^9.39.1",
|
||||
@@ -30,7 +36,14 @@
|
||||
"vue-router": "^4.6.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify-json/material-symbols": "^1.2.44",
|
||||
"@iconify-json/material-symbols-light": "^1.2.44",
|
||||
"@nuxtjs/i18n": "^10.2.0",
|
||||
"@tailwindcss/postcss": "^4.1.17",
|
||||
"autoprefixer": "^10.4.22",
|
||||
"less": "^4.4.2",
|
||||
"postcss": "^8.5.6",
|
||||
"tailwindcss": "^4.1.17",
|
||||
"zod": "^4.1.12"
|
||||
}
|
||||
}
|
||||
|
||||
1309
frontend/pnpm-lock.yaml
generated
1309
frontend/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -19,9 +19,9 @@ inputs.devenv.lib.mkShell {
|
||||
packages = with pkgs; [
|
||||
# LSP
|
||||
marksman
|
||||
nodePackages."@tailwindcss/language-server"
|
||||
vscode-langservers-extracted
|
||||
vue-language-server
|
||||
# nodePackages."@tailwindcss/language-server"
|
||||
# nodePackages."@vue/language-server"
|
||||
# vscode-langservers-extracted
|
||||
|
||||
rustywind
|
||||
nodePackages.prettier
|
||||
@@ -31,9 +31,8 @@ inputs.devenv.lib.mkShell {
|
||||
nodejs_24
|
||||
nodePackages.pnpm
|
||||
|
||||
# Typescript
|
||||
typescript
|
||||
nodePackages.typescript-language-server
|
||||
# typescript
|
||||
# nodePackages.typescript-language-server
|
||||
];
|
||||
|
||||
enterShell = ''
|
||||
|
||||
Reference in New Issue
Block a user