Migration to Nuxt3

This commit is contained in:
2023-04-30 18:36:39 +02:00
parent ed2e1824c5
commit 0480b99865
170 changed files with 13890 additions and 3699 deletions

View File

@ -23,13 +23,13 @@ jobs:
run: npm install run: npm install
- name: Build - name: Build
run: npm run build run: npm run generate
- name: Upload production-ready build files - name: Upload production-ready build files
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: production-files name: production-files
path: ./dist path: ./.output/public
deploy: deploy:
name: Deploy name: Deploy
@ -42,10 +42,10 @@ jobs:
uses: actions/download-artifact@v3 uses: actions/download-artifact@v3
with: with:
name: production-files name: production-files
path: ./dist path: ./.output/public
- name: Deploy to GitHub Pages - name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3 uses: peaceiris/actions-gh-pages@v3
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./dist publish_dir: ./.output/public

33
.gitignore vendored
View File

@ -1,28 +1,9 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules node_modules
.DS_Store *.log*
.nuxt
.nitro
.cache
.output
.env
dist dist
dist-ssr .DS_Store
coverage
*.local
/cypress/videos/
/cypress/screenshots/
# Editor directories and files
.vscode/*
.vscode
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

2
.npmrc Normal file
View File

@ -0,0 +1,2 @@
shamefully-hoist=true
strict-peer-dependencies=false

43
app.vue Normal file
View File

@ -0,0 +1,43 @@
<template>
<div v-if="init"
class="bg-gray-100 text-gray-900 min-h-screen h-full
dark:bg-gray-900 dark:text-slate-50"
style="font-family: 'Comfortaa'; overflow-x: hidden; ">
<!-- Prevents scrollbar shift -->
<div style="margin-right: calc(-1 * (100vw - 100%));">
<div class="container mx-auto pb-8 px-3 md:px-8 min-h-screen w-screen flex flex-col">
<NuxtLayout>
<NuxtPage/>
</NuxtLayout>
</div>
</div>
<Cookie />
<EastereggBanner ref="easteregg" />
</div>
</template>
<script setup>
const { locale } = useI18n();
const easteregg = ref();
const init = ref(false);
onMounted(() => {
applyTheme();
document.addEventListener("easteregg", (e) => {
// @ts-ignore
easteregg.value.show(e.detail);
});
init.value = true;
});
useHead({ htmlAttrs: { lang: locale.value } });
watch(locale, (new_locale) => {
useHead({ htmlAttrs: { lang: new_locale } });
})
</script>

View File

Before

Width:  |  Height:  |  Size: 159 KiB

After

Width:  |  Height:  |  Size: 159 KiB

View File

Before

Width:  |  Height:  |  Size: 119 KiB

After

Width:  |  Height:  |  Size: 119 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 772 B

After

Width:  |  Height:  |  Size: 772 B

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 679 B

After

Width:  |  Height:  |  Size: 679 B

View File

Before

Width:  |  Height:  |  Size: 634 B

After

Width:  |  Height:  |  Size: 634 B

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 553 B

After

Width:  |  Height:  |  Size: 553 B

View File

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

Before

Width:  |  Height:  |  Size: 963 B

After

Width:  |  Height:  |  Size: 963 B

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 609 B

After

Width:  |  Height:  |  Size: 609 B

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

Before

Width:  |  Height:  |  Size: 687 B

After

Width:  |  Height:  |  Size: 687 B

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 64 KiB

View File

Before

Width:  |  Height:  |  Size: 501 B

After

Width:  |  Height:  |  Size: 501 B

View File

Before

Width:  |  Height:  |  Size: 592 B

After

Width:  |  Height:  |  Size: 592 B

View File

Before

Width:  |  Height:  |  Size: 548 B

After

Width:  |  Height:  |  Size: 548 B

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 845 B

After

Width:  |  Height:  |  Size: 845 B

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 298"><g fill="none" fill-rule="nonzero"><path fill="#00C58E" d="M227.92099 82.07407l-13.6889 23.7037-46.8148-81.08641L23.7037 273.58025h97.3037c0 13.0912 10.61252 23.7037 23.70371 23.7037H23.70371c-8.46771 0-16.29145-4.52017-20.5246-11.85382-4.23315-7.33366-4.23272-16.36849.00114-23.70174L146.89383 12.83951c4.23415-7.33433 12.0596-11.85252 20.5284-11.85252 8.46878 0 16.29423 4.51819 20.52839 11.85252l39.97037 69.23456z"/><path fill="#2F495E" d="M331.6642 261.7284l-90.05432-155.95062-13.6889-23.7037-13.68888 23.7037-90.04445 155.95061c-4.23385 7.33325-4.23428 16.36808-.00113 23.70174 4.23314 7.33365 12.05689 11.85382 20.5246 11.85382h166.4c8.46946 0 16.29644-4.51525 20.532-11.84955 4.23555-7.3343 4.23606-16.37123.00132-23.706h.01976zM144.7111 273.58024L227.921 129.48148l83.19012 144.09877h-166.4z"/><path fill="#108775" d="M396.04938 285.4321c-4.23344 7.33254-12.05656 11.85185-20.52345 11.85185H311.1111c13.0912 0 23.7037-10.6125 23.7037-23.7037h40.66173L260.09877 73.74815l-18.4889 32.02963-13.68888-23.7037L239.5753 61.8963c4.23416-7.33433 12.0596-11.85252 20.5284-11.85252 8.46879 0 16.29423 4.51819 20.52839 11.85252l115.41728 199.8321c4.23426 7.33395 4.23426 16.36975 0 23.7037z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

Before

Width:  |  Height:  |  Size: 365 B

After

Width:  |  Height:  |  Size: 365 B

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

Before

Width:  |  Height:  |  Size: 262 KiB

After

Width:  |  Height:  |  Size: 262 KiB

View File

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

Before

Width:  |  Height:  |  Size: 745 B

After

Width:  |  Height:  |  Size: 745 B

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 466 B

After

Width:  |  Height:  |  Size: 466 B

View File

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 55 KiB

View File

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 458 KiB

View File

Before

Width:  |  Height:  |  Size: 156 KiB

After

Width:  |  Height:  |  Size: 156 KiB

View File

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 134 KiB

View File

Before

Width:  |  Height:  |  Size: 206 KiB

After

Width:  |  Height:  |  Size: 206 KiB

View File

Before

Width:  |  Height:  |  Size: 224 KiB

After

Width:  |  Height:  |  Size: 224 KiB

View File

Before

Width:  |  Height:  |  Size: 165 KiB

After

Width:  |  Height:  |  Size: 165 KiB

View File

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 164 KiB

View File

Before

Width:  |  Height:  |  Size: 136 KiB

After

Width:  |  Height:  |  Size: 136 KiB

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 309 KiB

After

Width:  |  Height:  |  Size: 309 KiB

View File

Before

Width:  |  Height:  |  Size: 141 KiB

After

Width:  |  Height:  |  Size: 141 KiB

View File

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 95 KiB

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 102 KiB

View File

@ -1,12 +1,12 @@
<template> <template>
<p class="my-4 text-left md:text-center"> <div class="my-4 text-left md:text-center">
<a :href="props.url"> <a :href="props.url">
<div class="inline-flex items-center"> <div class="inline-flex items-center">
<img :src="props.icon" alt="Github" class="h-7 mr-2 select-none dark:invert" /> <img :src="props.icon" alt="Github" class="h-7 mr-2 select-none dark:invert" />
<span class="text-xl text-left">{{ props.label }}</span> <span class="text-xl text-left">{{ props.label }}</span>
</div> </div>
</a> </a>
</p> </div>
</template> </template>

View File

@ -1,50 +1,49 @@
<template> <template>
<ClientOnly>
<div v-if="show_banner" class="w-full h-full"> <div v-if="show_banner" class="w-full h-full">
<div ref="container_cookie" class="absolute top-0 left-0 h-full w-full pointer-events-none z-40"> <div ref="container_cookie" class="absolute top-0 left-0 h-full w-full pointer-events-none z-40">
<canvas ref="canvas_cookie" :width="canvas_width" :height="canvas_height"></canvas> <canvas ref="canvas_cookie" :width="canvas_width" :height="canvas_height"></canvas>
</div> </div>
<div ref="banner" class="fixed bottom-0 left-0 z-50 bg-slate-200/90 border-slate-700 dark:bg-slate-800/90 dark:border-slate-400 <div ref="banner" class="fixed bottom-0 left-0 z-50 bg-slate-200/90 border-slate-700 dark:bg-slate-800/90 dark:border-slate-400
border rounded w-fit lg:w-1/2 p-3 px-5 m-5"> border rounded w-fit lg:w-1/2 p-3 px-5 m-5">
<p class="text-sm">{{ t("cookie policy title") }}</p> <p class="text-sm">{{ $t("cookie policy title") }}</p>
<div class="text-xs"> <div class="text-xs">
<span>{{ t("cookie policy") }} <button :onclick="throwCookie" class="underline">{{ t("cookie policy link") }}</button></span> <span>{{ $t("cookie policy") }} <button @click="throwCookie" class="underline">{{ $t("cookie policy link") }}</button></span>
</div> </div>
<div class="flex justify-end mt-2 text-sm"> <div class="flex justify-end mt-2 text-sm">
<button :onclick="refuseCookieBanner" class="mx-1 hover:text-slate-500 dark:hover:text-slate-300">{{ t("reject") }}</button> <button @click="refuseCookieBanner" class="mx-1 hover:text-slate-500 dark:hover:text-slate-300">{{ $t("reject") }}</button>
<button :onclick="acceptCookieBanner" class="rounded p-2 mx-1 bg-slate-300 hover:bg-slate-400 dark:bg-slate-700 dark:hover:bg-slate-600">{{ t("accept") }}</button> <button @click="acceptCookieBanner" class="rounded p-2 mx-1 bg-slate-300 hover:bg-slate-400 dark:bg-slate-700 dark:hover:bg-slate-600">{{ $t("accept") }}</button>
</div> </div>
</div> </div>
</div> </div>
</ClientOnly>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue';
// @ts-ignore // @ts-ignore
import { Engine, Render, Bodies, Composite, Body, Runner } from "matter-js"; import { Engine, Render, Bodies, Composite, Body, Runner } from "matter-js";
import cookie_image from "@/assets/images/cookie.png"; import cookie_image from "@/assets/images/cookie.png";
import { randomInt, random } from "@/utilities/random";
import { shouldShowCookie, acceptCookie, refuseCookie } from "@/utilities/cookie_handler";
import { useI18n } from "vue-i18n";
import { addFoundEasterEgg } from "@/utilities/easteregg_handler";
import locale from "@/locales/cookie";
const { t } = useI18n({ messages: locale }); const container_cookie = ref(null);
const canvas_cookie = ref(null);
const container_cookie = ref(); const show_banner = ref(true);
const canvas_cookie = ref(); const canvas_width = ref(0);
const canvas_height = ref(0);
const show_banner = ref(shouldShowCookie());
const canvas_width = ref(getWidth());
const canvas_height = ref(getHeight());
let engine:any = null; let engine:any = null;
onMounted(() => { onMounted(async () => {
if (shouldShowCookie()) { show_banner.value = shouldShowCookie()
canvas_width.value = getWidth();
canvas_height.value = getHeight();
if (shouldShowCookie()) {
await nextTick()
initCanvas(); initCanvas();
new ResizeObserver(() => { new ResizeObserver(() => {

View File

@ -9,10 +9,8 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, ref } from "vue";
const is_loading = ref(true); const is_loading = ref(true);
onMounted(() => { onMounted(() => {
if (document.querySelector("#script-goodreads")) { document.querySelector("#script-goodreads")?.remove() }; if (document.querySelector("#script-goodreads")) { document.querySelector("#script-goodreads")?.remove() };

View File

@ -0,0 +1,33 @@
<template>
<div>
<button id="button-dropdown-locales" data-dropdown-toggle="dropdown-locales" type="button"
class="rounded-full p-1 hover:bg-slate-200 dark:hover:bg-slate-700">
<div class="w-5 h-5 flex items-center justify-center">
<img src="~/assets/images/icons/globe.svg" alt="Language" class="h-full w-full dark:invert" />
</div>
</button>
<div id="dropdown-locales" class="z-10 hidden bg-white divide-y divide-gray-100 rounded shadow dark:bg-gray-700 dark:divide-gray-600">
<ul class="p-3 space-y-1 text-sm text-gray-700 dark:text-gray-200" aria-labelledby="button-dropdown-locales">
<li v-for="locale in $i18n.locales" :key="`locale-${locale.code}`">
<NuxtLink :to="switchLocalePath(locale.code)" class="text-sm font-medium uppercase text-gray-900 rounded dark:text-gray-300">
<span class="flex items-center p-2 px-5 rounded hover:bg-gray-100 dark:hover:bg-gray-600">
{{ locale.code }}
</span>
</NuxtLink>
</li>
</ul>
</div>
</div>
</template>
<script setup lang="ts">
import { initDropdowns } from "flowbite";
const switchLocalePath = useSwitchLocalePath()
onMounted(() => {
initDropdowns();
})
</script>

View File

@ -0,0 +1,79 @@
<template>
<div class="relative">
<div class="flex items-center h-60 w-60">
<img v-show="picture === 'dark'" src="~/assets/images/profile/picture-dark.png" alt="" class="max-h-full max-w-full">
<img v-show="picture === 'light'" src="~/assets/images/profile/picture-light.png" alt="" class="max-h-full max-w-full">
<img v-show="picture === 'bright'" src="~/assets/images/profile/picture-bright.png" alt="" class="max-h-full max-w-full">
<img v-show="picture === 'no light'" src="~/assets/images/profile/picture-nolight.png" alt="" class="max-h-full max-w-full">
</div>
<div v-if="message" class="absolute bottom-0 left-0 w-full">
<p class="w-fit mx-auto px-2 pt-1 mb-1 bg-gray-200 dark:bg-gray-700">{{ message }}</p>
</div>
</div>
</template>
<script setup lang="ts">
const { t } = useI18n();
const picture = ref(getTheme());
const message = ref("");
// Finite-state automata to handle theme changes
interface State {
image: string; // Image to display
message: string; // Message to display (string to parse with i18n)
expect: string; // Expected active theme when this state is applied
next: string; // Next state
}
onMounted(() => {
let current_state = document.querySelector("html")?.classList.contains("dark") ? "dark1" : "light1";
let states:Record<string, State> = {
"dark1": { image: "dark", message: "", expect: "dark", next: "bright" },
"bright": { image: "bright", message: "that's bright", expect: "light", next: "dark2" },
"dark2": { image: "dark", message: "better", expect: "dark", next: "light_final" },
"light1": { image: "light", message: "", expect: "light", next: "nolights" },
"nolights": { image: "no light", message: "where lights", expect: "dark", next: "light2" },
"light2": { image: "light", message: "here lights", expect: "light", next: "dark_final" },
"dark_final": { image: "dark", message: "", expect: "dark", next: "light_final" },
"light_final": { image: "light", message: "", expect: "light", next: "dark_final" },
}
function applyState() {
picture.value = states[current_state]?.image;
message.value = states[current_state]?.message !== "" ? t(states[current_state]?.message) : "";
}
function nextState() {
current_state = states[current_state]?.next;
}
applyState();
let observer = new MutationObserver(function(mutations) {
if (!useRoute().name?.toString().startsWith("about")) { observer.disconnect(); return; }
const is_dark_theme = document.querySelector("html")?.classList.contains("dark");
if ( states[states[current_state].next].expect === (is_dark_theme ? "dark" : "light") ) {
nextState();
applyState();
if (current_state === "bright") { addFoundEasterEgg("picture-bright"); }
else if (current_state === "nolights") { addFoundEasterEgg("picture-nolights"); }
}
});
// Observes for theme changes
observer.observe((document.querySelector("html") as Node), { attributes: true, attributeFilter: ['class'] });
});
</script>

View File

@ -1,23 +1,17 @@
<template> <template>
<div class="w-52"> <div v-if="current_name !== ''" class="w-52">
<img :src="current_image" alt="" class="h-40 max-w-xs max-w- mx-auto" :onclick="userChangeThing"> <img :src="current_image" alt="" class="h-40 max-w-xs max-w- mx-auto" @click="userChangeThing">
<p class="text-center text-sm mt-2 select-none">{{ t(current_name) }}</p> <p class="text-center text-sm mt-2 select-none">{{ $t(current_name) }}</p>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'; import { ref } from 'vue';
import { randomOfArray } from "@/utilities/random";
import { useI18n } from "vue-i18n";
import { addFoundEasterEgg } from "@/utilities/easteregg_handler";
import penguin_image from "@/assets/images/penguin.png"; import penguin_image from "@/assets/images/penguin.png";
import llama_image from "@/assets/images/llama.png"; import llama_image from "@/assets/images/llama.png";
import rock_image from "@/assets/images/rock.png"; import rock_image from "@/assets/images/rock.png";
import coconut_image from "@/assets/images/coconut.png"; import coconut_image from "@/assets/images/coconut.png";
import red_panda_image from "@/assets/images/red-panda.png"; import red_panda_image from "@/assets/images/red-panda.png";
import locale from "@/locales/something";
const { t } = useI18n({ messages: locale });
const things = [ const things = [
{ name: "penguin", image: penguin_image }, { name: "penguin", image: penguin_image },
@ -30,8 +24,6 @@
const current_name = ref(""); const current_name = ref("");
const current_image = ref(""); const current_image = ref("");
changeThing();
function changeThing() { function changeThing() {
const to_show_thing = randomOfArray(things.filter((thing) => thing.name !== current_name.value)); const to_show_thing = randomOfArray(things.filter((thing) => thing.name !== current_name.value));
@ -44,4 +36,9 @@
addFoundEasterEgg("change-something"); addFoundEasterEgg("change-something");
changeThing(); changeThing();
} }
onMounted(() => {
changeThing();
})
</script> </script>

View File

@ -1,25 +1,25 @@
<template> <template>
<button class="rounded-full p-1 hover:bg-slate-200 dark:hover:bg-slate-700" :onclick="changeTheme"> <button class="rounded-full p-1 hover:bg-slate-200 dark:hover:bg-slate-700" @click="changeTheme">
<div class="w-5 h-5 flex items-center justify-center"> <div class="w-5 h-5 flex items-center justify-center">
<div v-if="current_theme === 'light'"> <div v-if="current_theme === 'light'">
<img :src="moon_icon" alt="Dark theme" class="h-full w-full" /> <img src="~/assets/images/icons/moon.svg" alt="Dark theme" class="h-full w-full" />
</div> </div>
<div v-if="current_theme === 'dark'"> <div v-if="current_theme === 'dark'">
<img :src="sun_icon" alt="Light theme" class="invert h-full w-full" /> <img src="~/assets/images/icons/sun.svg" alt="Light theme" class="invert h-full w-full" />
</div> </div>
</div> </div>
</button> </button>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { getTheme, flipTheme, applyTheme } from "@/utilities/theme_handler";
import { ref } from "vue"; import { ref } from "vue";
import moon_icon from "@/assets/images/icons/moon.svg"; const current_theme = ref("");
import sun_icon from "@/assets/images/icons/sun.svg";
const current_theme = ref(getTheme()); onMounted(() => {
current_theme.value = getTheme();
})
function changeTheme() { function changeTheme() {
flipTheme(); flipTheme();

View File

@ -1,4 +1,8 @@
<template> <template>
<div v-if="loading" class="flex w-full justify-center">
<span class="animate-ping absolute inline-flex h-5 w-5 rounded-full bg-slate-800 dark:bg-slate-200 opacity-75"></span>
</div>
<div class="w-full h-full" ref="container_timeline"> <div class="w-full h-full" ref="container_timeline">
<div class="flex justify-center w-full h-full" v-if="month_offset > 0 && min_date && max_date"> <div class="flex justify-center w-full h-full" v-if="month_offset > 0 && min_date && max_date">
@ -54,16 +58,15 @@
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from "vue";
import type { PropType, Ref } from "vue";
interface Event { interface Event {
title: string, description: string, time_label: string, title: string, description: string, time_label: string,
start: Date, end: Date, current?: boolean start: Date, end: Date, current?: boolean
}; };
const props = defineProps({ const props = defineProps({
right: { right: {
type: Object as PropType<Event[]>, type: Object as PropType<Event[]>,
@ -75,7 +78,7 @@
}, },
}); });
const loading = ref(true);
const container_timeline = ref(); const container_timeline = ref();
const month_offset = ref(-1); const month_offset = ref(-1);
const right_events:Ref<{ index: number, offset: number }[]> = ref([]); const right_events:Ref<{ index: number, offset: number }[]> = ref([]);
@ -106,6 +109,7 @@
} }
onMounted(() => { onMounted(() => {
loading.value = false;
updateTimelineSize(); updateTimelineSize();
// Computes the offset w.r.t. the end of the time interval // Computes the offset w.r.t. the end of the time interval
@ -118,6 +122,6 @@
index: index index: index
})); }));
new ResizeObserver(updateTimelineSize).observe(document.querySelector("#app") as Element) new ResizeObserver(updateTimelineSize).observe(document.querySelector("html") as Element)
}) })
</script> </script>

View File

@ -1,4 +1,5 @@
<template> <template>
<ClientOnly>
<div v-if="show_banner" class="fixed top-0 left-0 w-full pointer-events-none z-50"> <div v-if="show_banner" class="fixed top-0 left-0 w-full pointer-events-none z-50">
<div :class="`border rounded-sm mx-auto w-fit max-w-xs md:max-w-md pointer-events-auto <div :class="`border rounded-sm mx-auto w-fit max-w-xs md:max-w-md pointer-events-auto
@ -15,8 +16,8 @@
<PictureNoLightEgg v-if="easteregg === 'picture-nolights'" /> <PictureNoLightEgg v-if="easteregg === 'picture-nolights'" />
<div class="mt-1 text-center"> <div class="mt-1 text-center">
<p v-if="found_eastereggs != total_eastereggs">{{ found_eastereggs }}/{{ total_eastereggs }} {{ t("easter eggs found") }}</p> <p v-if="found_eastereggs != total_eastereggs">{{ found_eastereggs }}/{{ total_eastereggs }} {{ $t("easter eggs found") }}</p>
<p v-if="found_eastereggs === total_eastereggs">{{ t("all easter eggs found") }}</p> <p v-if="found_eastereggs === total_eastereggs">{{ $t("all easter eggs found") }}</p>
</div> </div>
</div> </div>
</div> </div>
@ -24,29 +25,22 @@
</div> </div>
</div> </div>
</ClientOnly>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from "vue";
import { useI18n } from "vue-i18n";
import { getFoundEasterEggsCount, getTotalEasterEggsCount } from "@/utilities/easteregg_handler";
import CookieEgg from "./eggs/Cookie.vue";
import FutureEgg from "./eggs/Future.vue";
import SomethingEgg from "./eggs/Something.vue";
import PictureBrightEgg from "./eggs/PictureBright.vue";
import PictureNoLightEgg from "./eggs/PictureNoLight.vue";
import locale from "@/locales/easteregg";
const show_banner = ref(false); const show_banner = ref(false);
const easteregg = ref(""); const easteregg = ref("");
const total_eastereggs = ref(getTotalEasterEggsCount()); const total_eastereggs = ref(getTotalEasterEggsCount());
const found_eastereggs = ref(getFoundEasterEggsCount()); const found_eastereggs = ref(0);
const { t } = useI18n({ messages: locale });
let current_dismiss_timeout:number|null = null; onMounted(() => {
found_eastereggs.value = getFoundEasterEggsCount();
})
let current_dismiss_timeout:NodeJS.Timeout|null = null;
function show(easteregg_name:string) { function show(easteregg_name:string) {
easteregg.value = easteregg_name; easteregg.value = easteregg_name;

View File

@ -0,0 +1,16 @@
<template>
<div class="flex justify-center text-sm">
<div class="flex items-center justify-center">
<div class="w-10 h-10 flex items-center justify-center overflow-hidden">
<img src="~/assets/images/cookie.png" alt="" class="h-full w-full" />
</div>
</div>
<div class="flex-1 ml-2">
<p class="font-bold text-base">{{ $t("cookie.title") }}</p>
<p>{{ $t("cookie.description") }}</p>
</div>
</div>
</template>

View File

@ -0,0 +1,16 @@
<template>
<div class="flex text-sm">
<div class="flex items-center justify-center">
<div class="w-10 h-10 flex items-center justify-center overflow-hidden">
<img src="~/assets/images/future.png" alt="" class="h-full w-full" />
</div>
</div>
<div class="flex-1 ml-2">
<p class="font-bold text-base">{{ $t("future.title") }}</p>
<p>{{ $t("future.description") }}</p>
</div>
</div>
</template>

View File

@ -0,0 +1,25 @@
<template>
<ClientOnly>
<div class="flex text-sm">
<div class="flex items-center justify-center">
<div class="w-10 h-10 flex items-center justify-center overflow-hidden">
<img src="~/assets/images/sun.png" alt="" class="h-full w-full" />
</div>
</div>
<div class="flex-1 ml-2">
<p class="font-bold text-base">{{ $t("bright.title") }}</p>
<p v-if="!dark_unlocked">{{ $t("bright.description") }}</p>
<p v-if="dark_unlocked">{{ $t("bright_either.description") }}</p>
</div>
</div>
</ClientOnly>
</template>
<script setup lang="ts">
const dark_unlocked = ref(false);
onMounted(() => {
dark_unlocked.value = getFoundEasterEggs().includes("picture-nolights");
});
</script>

View File

@ -0,0 +1,25 @@
<template>
<ClientOnly>
<div class="flex text-sm">
<div class="flex items-center justify-center">
<div class="w-10 h-10 flex items-center justify-center overflow-hidden">
<img src="~/assets/images/moon.png" alt="" class="h-full w-full" />
</div>
</div>
<div class="flex-1 ml-2">
<p class="font-bold text-base">{{ $t("dark.title") }}</p>
<p v-if="!light_unlocked">{{ $t("dark.description") }}</p>
<p v-if="light_unlocked">{{ $t("dark_either.description") }}</p>
</div>
</div>
</ClientOnly>
</template>
<script setup lang="ts">
const light_unlocked = ref(false);
onMounted(() => {
light_unlocked.value = getFoundEasterEggs().includes("picture-bright");
});
</script>

View File

@ -0,0 +1,16 @@
<template>
<div class="flex text-sm">
<div class="flex items-center justify-center">
<div class="w-10 h-10 flex items-center justify-center overflow-hidden">
<img src="~/assets/images/sad.svg" alt="" class="h-full w-full dark:invert" />
</div>
</div>
<div class="flex-1 ml-2">
<p class="font-bold text-base">{{ $t("something.title") }}</p>
<p>{{ $t("something.description") }}</p>
</div>
</div>
</template>

View File

@ -1,22 +1,25 @@
<template> <template>
<li> <li>
<router-link :to="props.to" :aria-current="is_active_page ? 'page' : null" <NuxtLink :to="localePath(props.to)" :aria-current="is_active_page ? 'page' : null"
:class="`block py-2 md:p-0 text-right md:text-center :class="`block py-2 md:p-0 text-right md:text-center
${is_active_page ? 'font-bold text-zinc-900 dark:text-zinc-400' : 'font-normal hover:underline text-gray-700 dark:text-slate-50'}`"> ${is_active_page ? 'font-bold text-zinc-900 dark:text-zinc-400' : 'font-normal hover:underline text-gray-700 dark:text-slate-50'}`">
{{ props.label }} {{ props.label }}
</router-link> </NuxtLink>
</li> </li>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from "vue"; import { ref } from "vue";
import { useRoute } from "vue-router"; const localePath = useLocalePath()
const route = useRoute(); const router = useRouter();
const props = defineProps({ const props = defineProps({
to: { type: String, required: true }, to: { type: String, required: true },
label: String label: String
}) })
const is_active_page = ref(route.path === props.to); let current_path = router.currentRoute.value.fullPath.replace(/\/$/, "");
if (current_path === "") { current_path = "/"; }
const is_active_page = ref(current_path === localePath(props.to) || current_path === props.to);
</script> </script>

View File

@ -4,14 +4,14 @@
<div class="block md:flex w-full"> <div class="block md:flex w-full">
<div class="flex justify-end items-center order-2 md:w-1/2"> <div class="flex justify-end items-center order-2 md:w-1/2">
<a href="https://github.com/NotXia" class="rounded-full p-1 mx-1 hover:bg-slate-200 dark:hover:bg-slate-700"> <a href="https://github.com/NotXia" class="rounded-full p-1 mx-1 hover:bg-slate-200 dark:hover:bg-slate-700">
<img :src="github_icon" alt="Github" class="h-5 dark:invert" /> <img src="~/assets/images/icons/github.svg" alt="Github" class="h-5 dark:invert" />
</a> </a>
<ThemeSwitch class="mx-1" /> <ThemeSwitch class="mx-1" />
<LanguageSelector class="mx-1" /> <LanguageSelector class="mx-1" />
<button class="inline-flex items-center mx-3 text-sm text-gray-500 md:hidden dark:text-gray-400" <button class="inline-flex items-center mx-3 text-sm text-gray-500 md:hidden dark:text-gray-400"
data-collapse-toggle="navbar-main" type="button" aria-controls="navbar-main" aria-expanded="false"> data-collapse-toggle="navbar-main" type="button" aria-controls="navbar-main" aria-expanded="false">
<span class="sr-only">{{ t("open nav") }}</span> <span class="sr-only">{{ $t("open nav") }}</span>
<svg class="w-6 h-6" aria-hidden="true" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"> <svg class="w-6 h-6" aria-hidden="true" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" clip-rule="evenodd"></path> <path fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" clip-rule="evenodd"></path>
</svg> </svg>
@ -21,11 +21,11 @@
<div class="flex items-center order-1 md:w-full"> <div class="flex items-center order-1 md:w-full">
<div class="hidden w-full md:block md:w-auto" id="navbar-main"> <div class="hidden w-full md:block md:w-auto" id="navbar-main">
<ul class="flex flex-col py-4 pr-4 mt-0 md:flex-row md:space-x-8 md:text-sm md:font-medium bg-transparent"> <ul class="flex flex-col py-4 pr-4 mt-0 md:flex-row md:space-x-8 md:text-sm md:font-medium bg-transparent">
<NavLink to="/" :label="t('home')"/> <NavLink to="/" :label="$t('home')"/>
<NavLink to="/about" :label="t('about')"/> <NavLink to="/about" :label="$t('about')"/>
<NavLink to="/projects" :label="t('projects')"/> <NavLink to="/projects" :label="$t('projects')"/>
<NavLink to="/resume" :label="t('resume')"/> <NavLink to="/resume" :label="$t('resume')"/>
<NavLink to="/contacts" :label="t('contacts')"/> <NavLink to="/contacts" :label="$t('contacts')"/>
</ul> </ul>
</div> </div>
</div> </div>
@ -36,18 +36,10 @@
<script setup lang="ts"> <script setup lang="ts">
import NavLink from "./NavLink.vue"; import NavLink from "./NavLink.vue"
import ThemeSwitch from "../theme-switch/ThemeSwitch.vue";
import LanguageSelector from "../language-selector/LanguageSelector.vue";
import { initCollapses } from "flowbite"; import { initCollapses } from "flowbite";
import { useI18n } from "vue-i18n";
import locale from "@/locales/navbar";
import { onMounted } from "vue"; import { onMounted } from "vue";
import github_icon from "@/assets/images/icons/github.svg";
const { t } = useI18n({ messages: locale });
onMounted(() => { onMounted(() => {
initCollapses(); initCollapses();
}) })

View File

@ -0,0 +1,21 @@
<template>
<ProjectCard
title="Animal House" :image="image"
:links="[
{ label: 'Repository', url: 'https://github.com/NotXia/animal-house' }
]">
<p class="text-center">{{ $t("unibo_21-22") }}</p>
<p>{{ $t('animalhouse.description') }}</p>
<ul class="list-inside list-['-_']">
<li>{{ $t('animalhouse.description.game') }}</li>
<li>{{ $t('animalhouse.description.frontoffice') }}</li>
<li>{{ $t('animalhouse.description.backoffice') }}</li>
</ul>
</ProjectCard>
</template>
<script setup lang="ts">
import image from "@/assets/images/projects/animal-house.png";
</script>

View File

@ -0,0 +1,16 @@
<template>
<ProjectCard
title="Image deblur" :image="image"
:links="[
{ label: 'Repository', url: 'https://github.com/NotXia/imaging' }
]">
<p class="text-center">{{ $t("unibo_21-22") }}</p>
<p>{{ $t('imaging.description') }}</p>
</ProjectCard>
</template>
<script setup lang="ts">
import image from "@/assets/images/projects/imaging.png";
</script>

View File

@ -0,0 +1,16 @@
<template>
<ProjectCard
title="MNK Game" :image="image"
:links="[
{ label: 'Repository', url: 'https://github.com/NotXia/MNKGame' }
]">
<p class="text-center">{{ $t("unibo_20-21") }}</p>
<p>{{ $t('mnk.description') }}</p>
</ProjectCard>
</template>
<script setup lang="ts">
import image from "@/assets/images/projects/mnkgame.png";
</script>

View File

@ -5,7 +5,7 @@
{ label: 'Repository', url: 'https://github.com/NotXia/notxia.github.io' }, { label: 'Repository', url: 'https://github.com/NotXia/notxia.github.io' },
]"> ]">
<p v-if="!show_recursive_message">{{ t('notxia.github.io.description') }}</p> <p v-if="!show_recursive_message">{{ $t('notxia.github.io.description') }}</p>
<div class="w-full h-72 relative"> <div class="w-full h-72 relative">
<div role="status" v-if="!iframe_loaded && !show_recursive_message" class="absolute top-0 left-0 w-full h-full flex justify-center items-center"> <div role="status" v-if="!iframe_loaded && !show_recursive_message" class="absolute top-0 left-0 w-full h-full flex justify-center items-center">
@ -16,21 +16,16 @@
</div> </div>
<div v-if="show_recursive_message" class="flex justify-center items-center w-full h-full"> <div v-if="show_recursive_message" class="flex justify-center items-center w-full h-full">
<p>{{ t("no recursion") }}</p> <p>{{ $t("no recursion") }}</p>
</div> </div>
<iframe src="/" frameborder="0" width="100%" height="100%" @load="iframe_loaded = true"></iframe> <iframe v-if="!show_recursive_message" src="/" frameborder="0" width="100%" height="100%" @load="iframe_loaded=true"></iframe>
</div> </div>
</ProjectCard> </ProjectCard>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import ProjectCard from "../ProjectCard.vue";
import { useI18n } from "vue-i18n";
import { ref, onMounted } from "vue";
import locale from "@/locales/projects";
const iframe_loaded = ref(false); const iframe_loaded = ref(false);
const show_recursive_message = ref(false); const show_recursive_message = ref(false);
@ -42,6 +37,4 @@
} }
catch (err) { show_recursive_message.value = false; } catch (err) { show_recursive_message.value = false; }
}); });
const { t } = useI18n({ messages: locale });
</script> </script>

View File

@ -0,0 +1,11 @@
<template>
<ProjectCard
title="PandOS+"
:links="[
{ label: 'Repository', url: 'https://github.com/NotXia/pandos-plus' }
]">
<p class="text-center">{{ $t("unibo_21-22") }}</p>
<p>{{ $t('pandos+.description') }}</p>
</ProjectCard>
</template>

View File

@ -1,22 +1,16 @@
<template> <template>
<ProjectCard <ProjectCard
title="Pathfinding visualizer" :image="image" title="Pathfinding visualizer" :image="image"
:description="t('description')"
:links="[ :links="[
{ label: 'Repository', url: 'https://github.com/NotXia/pathfinding-visualizer' }, { label: 'Repository', url: 'https://github.com/NotXia/pathfinding-visualizer' },
{ label: 'Demo', url: 'https://notxia.github.io/pathfinding-visualizer/' } { label: 'Demo', url: 'https://notxia.github.io/pathfinding-visualizer/' }
]"> ]">
{{ t('pathfinding_visualizer.description') }} {{ $t('pathfinding_visualizer.description') }}
</ProjectCard> </ProjectCard>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import ProjectCard from "../ProjectCard.vue";
import { useI18n } from "vue-i18n";
import image from "@/assets/images/projects/pathfinding-visualizer.png"; import image from "@/assets/images/projects/pathfinding-visualizer.png";
import locale from "@/locales/projects";
const { t } = useI18n({ messages: locale });
</script> </script>

View File

@ -0,0 +1,16 @@
<template>
<ProjectCard
title="Platform game" :image="image"
:links="[
{ label: 'Repository', url: 'https://github.com/NotXia/platform-game' }
]">
<p class="text-center">{{ $t("unibo_20-21") }}</p>
<p>{{ $t('platform.description') }}</p>
</ProjectCard>
</template>
<script setup lang="ts">
import image from "@/assets/images/projects/platform.png";
</script>

View File

@ -1,22 +1,16 @@
<template> <template>
<ProjectCard <ProjectCard
title="Sorting visualizer" :image="image" title="Sorting visualizer" :image="image"
:description="t('description')"
:links="[ :links="[
{ label: 'Repository', url: 'https://github.com/NotXia/sorting-visualizer' }, { label: 'Repository', url: 'https://github.com/NotXia/sorting-visualizer' },
{ label: 'Demo', url: 'https://notxia.github.io/sorting-visualizer/' } { label: 'Demo', url: 'https://notxia.github.io/sorting-visualizer/' }
]"> ]">
{{ t('sort_visualizer.description') }} {{ $t('sort_visualizer.description') }}
</ProjectCard> </ProjectCard>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import ProjectCard from "../ProjectCard.vue";
import { useI18n } from "vue-i18n";
import image from "@/assets/images/projects/sorting-visualizer.png"; import image from "@/assets/images/projects/sorting-visualizer.png";
import locale from "@/locales/projects";
const { t } = useI18n({ messages: locale });
</script> </script>

View File

@ -0,0 +1,16 @@
<template>
<ProjectCard
title="Tweet Analysis" :image="image"
:links="[
{ label: 'Repository', url: 'https://github.com/NotXia/tweet-analysis' }
]">
<p class="text-center">{{ $t("unibo_22-23") }}</p>
<p>{{ $t('tweet_analysis.description') }}</p>
</ProjectCard>
</template>
<script setup lang="ts">
import image from "@/assets/images/projects/tweet-analysis.png";
</script>

View File

@ -0,0 +1,12 @@
<template>
<ProjectCard
title="Wirefilter"
:links="[
{ label: 'Repository', url: 'https://github.com/NotXia/vdeplug_wirefilter' },
{ label: 'VirtualSquare', url: 'http://wiki.virtualsquare.org/#!index.md' }
]">
<p class="text-center">{{ $t("unibo_22-23") }}</p>
<p>{{ $t("wirefilter.description") }}</p>
</ProjectCard>
</template>

View File

@ -5,7 +5,7 @@
</div> </div>
<div id="tooltip-future" role="tooltip" <div id="tooltip-future" role="tooltip"
class="absolute z-10 invisible inline-block px-2 py-1 text-xs font-medium transition-opacity duration-1000 rounded-lg opacity-0 tooltip"> class="absolute z-10 invisible inline-block px-2 py-1 text-xs font-medium transition-opacity duration-1000 rounded-lg opacity-0 tooltip">
{{ t("future") }} {{ $t("future") }}
</div> </div>
</div> </div>
@ -13,19 +13,19 @@
<Timeline <Timeline
:right="[ :right="[
{ {
title: t('diploma'), time_label: '2015 - 2020', title: $t('diploma'), time_label: '2015 - 2020',
description: t('aldini'), description: $t('aldini'),
start: new Date(2015, september, 1), end: new Date(2020, june, 1) start: new Date(2015, september, 1), end: new Date(2020, june, 1)
}, },
{ {
title: t('bs in cs'), time_label: '2020 - 2023', title: $t('bs in cs'), time_label: '2020 - 2023',
description: t('unibo'), description: $t('unibo'),
start: new Date(2020, september, 1), end: new Date(), current: true start: new Date(2020, september, 1), end: new Date(), current: true
} }
]" ]"
:left="[ :left="[
{ {
title: t('pcto toyota'), time_label: `${t('m_12')} 2019 | ${t('m_7')} 2019 | ${t('m_2')} 2019`, title: $t('pcto toyota'), time_label: `${$t('m_12')} 2019 | ${$t('m_7')} 2019 | ${$t('m_2')} 2019`,
description: 'Toyota Material Handling Manufacturing Italy', description: 'Toyota Material Handling Manufacturing Italy',
start: new Date(2019, february, 1), end: new Date(2019, december, 1) start: new Date(2019, february, 1), end: new Date(2019, december, 1)
}, },
@ -42,19 +42,12 @@
]" /> ]" />
</div> </div>
</div> </div>
<p class="text-center text-xs text-gray-400 dark:text-slate-600">{{ t("like timelines") }}</p> <p class="text-center text-xs text-gray-400 dark:text-slate-600">{{ $t("like timelines") }}</p>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import Timeline from "@/components/timeline/Timeline.vue";
import { useI18n } from "vue-i18n";
import { initTooltips } from "flowbite"; import { initTooltips } from "flowbite";
import { onMounted } from "vue";
import { addFoundEasterEgg } from "@/utilities/easteregg_handler";
import locale from "@/locales/resume";
const { t } = useI18n({ messages: locale });
const january = 0, february = 1, march = 2, april = 3, may = 4, june = 5, july = 6, august = 7, september = 8, october = 9, november = 10, december = 11; const january = 0, february = 1, march = 2, april = 3, may = 4, june = 5, july = 6, august = 7, september = 8, october = 9, november = 10, december = 11;
@ -63,7 +56,7 @@
}); });
let achievement_timer:number|null = null; let achievement_timer:NodeJS.Timeout|null = null;
function startArchievementTimer() { function startArchievementTimer() {
achievement_timer = setTimeout(() => { achievement_timer = setTimeout(() => {

Some files were not shown because too many files have changed in this diff Show More