mirror of
https://github.com/ArthurDanjou/artchat.git
synced 2026-01-14 15:54:03 +01:00
refactor: streamline component structure and enhance accessibility features
This commit is contained in:
42
app/app.vue
42
app/app.vue
@@ -28,28 +28,26 @@ const head = useLocaleHead()
|
|||||||
</Html>
|
</Html>
|
||||||
<NuxtLoadingIndicator color="#808080" />
|
<NuxtLoadingIndicator color="#808080" />
|
||||||
<AppBackground />
|
<AppBackground />
|
||||||
<UContainer>
|
<ChatCommandPalette
|
||||||
<ChatCommandPalette
|
v-motion
|
||||||
v-motion
|
:active="messages.length > 0"
|
||||||
:active="messages.length > 0"
|
:mode="route.path.includes('/projects') || route.path.includes('/writings') || route.path.includes('/canva') ? 'work' : 'chat'"
|
||||||
:mode="route.path.includes('/projects') || route.path.includes('/writings') || route.path.includes('/canva') ? 'work' : 'chat'"
|
:initial="{
|
||||||
:initial="{
|
opacity: 0,
|
||||||
opacity: 0,
|
y: 200,
|
||||||
y: 200,
|
scale: 0.6,
|
||||||
scale: 0.6,
|
}"
|
||||||
}"
|
:enter="{
|
||||||
:enter="{
|
opacity: 1,
|
||||||
opacity: 1,
|
y: 0,
|
||||||
y: 0,
|
scale: 1,
|
||||||
scale: 1,
|
transition: {
|
||||||
transition: {
|
delay: route.path === '/' ? 1800 : 0,
|
||||||
delay: route.path === '/' ? 1800 : 0,
|
ease: 'easeIn',
|
||||||
ease: 'easeIn',
|
},
|
||||||
},
|
}"
|
||||||
}"
|
/>
|
||||||
/>
|
<NuxtPage />
|
||||||
<NuxtPage />
|
|
||||||
</UContainer>
|
|
||||||
<SpeedInsights />
|
<SpeedInsights />
|
||||||
<Analytics />
|
<Analytics />
|
||||||
</UApp>
|
</UApp>
|
||||||
|
|||||||
@@ -46,8 +46,9 @@ defineShortcuts({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const activeElement = useActiveElement()
|
const activeElement = useActiveElement()
|
||||||
watch(activeElement, () => {
|
watch(openMessageModal, async () => {
|
||||||
if (activeElement.value instanceof HTMLElement && ['INPUT', 'TEXTAREA'].includes(activeElement.value.tagName)) {
|
await nextTick()
|
||||||
|
if (activeElement.value instanceof HTMLElement) {
|
||||||
activeElement.value.blur()
|
activeElement.value.blur()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -201,6 +202,7 @@ function goHome() {
|
|||||||
/>
|
/>
|
||||||
</UTooltip>
|
</UTooltip>
|
||||||
<UTooltip
|
<UTooltip
|
||||||
|
v-if="router.currentRoute.value.name !== 'canva'"
|
||||||
:text="t('palette.tooltip.canva')"
|
:text="t('palette.tooltip.canva')"
|
||||||
arrow
|
arrow
|
||||||
:content="toolTipContent"
|
:content="toolTipContent"
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const generate = computed(() => props.id && ((typeof headings?.anchorLinks === '
|
|||||||
<a
|
<a
|
||||||
v-if="id && generate"
|
v-if="id && generate"
|
||||||
:href="`#${id}`"
|
:href="`#${id}`"
|
||||||
class="text-xl font-bold border-transparent border-b-2 hover:border-black dark:hover:border-white duration-300"
|
class="text-xl font-bold decoration-neutral-300 dark:decoration-neutral-700 underline-offset-2 hover:decoration-black dark:hover:decoration-white duration-300"
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const generate = computed(() => props.id && ((typeof headings?.anchorLinks === '
|
|||||||
<a
|
<a
|
||||||
v-if="id && generate"
|
v-if="id && generate"
|
||||||
:href="`#${id}`"
|
:href="`#${id}`"
|
||||||
class="text-lg font-semibold text-neutral-800 dark:text-neutral-200"
|
class="text-lg font-semibold text-neutral-800 dark:text-neutral-200 decoration-neutral-300 dark:decoration-neutral-700 underline-offset-2 hover:decoration-black dark:hover:decoration-white duration-300"
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ const { t } = useI18n()
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="mt-8 p-8 border bg-white/70 dark:bg-black/70 border-gray-200 dark:border-neutral-700 rounded-md">
|
<UCard class="mt-8 shadow-sm bg-white dark:bg-neutral-900">
|
||||||
<NuxtImg
|
<NuxtImg
|
||||||
src="/arthur pro.webp"
|
src="/arthur pro.webp"
|
||||||
alt="Arthur Danjou"
|
alt="Arthur Danjou"
|
||||||
@@ -42,5 +42,5 @@ const { t } = useI18n()
|
|||||||
<br> <br>
|
<br> <br>
|
||||||
</template>
|
</template>
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
</div>
|
</UCard>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ watch(
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main>
|
<UContainer>
|
||||||
<ChatMain />
|
<ChatMain />
|
||||||
<div ref="parents" class="space-y-4 md:my-32 mb-16">
|
<div ref="parents" class="space-y-4 md:my-32 mb-16">
|
||||||
<ChatMessageContainer
|
<ChatMessageContainer
|
||||||
@@ -95,5 +95,5 @@ watch(
|
|||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</UContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ const route = useRoute()
|
|||||||
const { data: project } = await useAsyncData(`projects/${route.params.slug}`, () =>
|
const { data: project } = await useAsyncData(`projects/${route.params.slug}`, () =>
|
||||||
queryCollection('projects').path(`/projects/${route.params.slug}`).first())
|
queryCollection('projects').path(`/projects/${route.params.slug}`).first())
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
|
|
||||||
useSeoMeta({
|
useSeoMeta({
|
||||||
title: project.value?.title,
|
title: project.value?.title,
|
||||||
description: project.value?.description,
|
description: project.value?.description,
|
||||||
@@ -13,20 +11,7 @@ useSeoMeta({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main v-if="project" class="mt-8 md:mt-16 md:mb-32 mb-20">
|
<main v-if="project" class="mt-8 md:mt-16 md:mb-36 mb-20">
|
||||||
<div class="flex">
|
|
||||||
<NuxtLinkLocale
|
|
||||||
class="flex items-center gap-2 mb-8 group text-sm hover:text-black dark:hover:text-white duration-300"
|
|
||||||
to="/canva"
|
|
||||||
>
|
|
||||||
<UIcon
|
|
||||||
class="group-hover:-translate-x-1 transform duration-300"
|
|
||||||
name="i-ph-arrow-left-duotone"
|
|
||||||
size="20"
|
|
||||||
/>
|
|
||||||
{{ t('post.back') }}
|
|
||||||
</NuxtLinkLocale>
|
|
||||||
</div>
|
|
||||||
<PostAlert class="mb-8" />
|
<PostAlert class="mb-8" />
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-end justify-between gap-2 flex-wrap">
|
<div class="flex items-end justify-between gap-2 flex-wrap">
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ const route = useRoute()
|
|||||||
const { data: writing } = await useAsyncData(`writings/${route.params.slug}`, () =>
|
const { data: writing } = await useAsyncData(`writings/${route.params.slug}`, () =>
|
||||||
queryCollection('writings').path(`/writings/${route.params.slug}`).first())
|
queryCollection('writings').path(`/writings/${route.params.slug}`).first())
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
|
|
||||||
useSeoMeta({
|
useSeoMeta({
|
||||||
title: writing.value?.title,
|
title: writing.value?.title,
|
||||||
description: writing.value?.description,
|
description: writing.value?.description,
|
||||||
@@ -13,20 +11,7 @@ useSeoMeta({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main v-if="writing" class="mt-8 md:mt-16 md:mb-32 mb-20">
|
<UContainer v-if="writing" class="mt-16 md:mt-16 md:mb-36 mb-22">
|
||||||
<div class="flex">
|
|
||||||
<NuxtLinkLocale
|
|
||||||
class="flex items-center gap-2 mb-8 group text-sm hover:text-black dark:hover:text-white duration-300"
|
|
||||||
to="/canva"
|
|
||||||
>
|
|
||||||
<UIcon
|
|
||||||
class="group-hover:-translate-x-1 transform duration-300"
|
|
||||||
name="i-ph-arrow-left-duotone"
|
|
||||||
size="20"
|
|
||||||
/>
|
|
||||||
{{ t('post.back') }}
|
|
||||||
</NuxtLinkLocale>
|
|
||||||
</div>
|
|
||||||
<PostAlert class="mb-8" />
|
<PostAlert class="mb-8" />
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-end justify-between gap-2 flex-wrap">
|
<div class="flex items-end justify-between gap-2 flex-wrap">
|
||||||
@@ -51,7 +36,7 @@ useSeoMeta({
|
|||||||
class="w-full rounded-md my-8"
|
class="w-full rounded-md my-8"
|
||||||
>
|
>
|
||||||
<ProseImg
|
<ProseImg
|
||||||
:src="`/projects/${writing.cover}`"
|
:src="`/writings/${writing.cover}`"
|
||||||
label="Project cover"
|
label="Project cover"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -66,7 +51,7 @@ useSeoMeta({
|
|||||||
/>
|
/>
|
||||||
</ClientOnly>
|
</ClientOnly>
|
||||||
<PostFooter />
|
<PostFooter />
|
||||||
</main>
|
</UContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ tags:
|
|||||||
|
|
||||||
Create categories and tabs to group your shortcuts, personalize them with icons and colors, and make the page private if you want to keep your links just for yourself. The interface is clean, responsive, and works across all modern browsers.
|
Create categories and tabs to group your shortcuts, personalize them with icons and colors, and make the page private if you want to keep your links just for yourself. The interface is clean, responsive, and works across all modern browsers.
|
||||||
|
|
||||||
### 🛠️ Built with
|
## 🛠️ Built with
|
||||||
|
|
||||||
- [Nuxt](https://nuxt.com): An open-source framework for building performant, full-stack web applications with Vue.
|
- [Nuxt](https://nuxt.com): An open-source framework for building performant, full-stack web applications with Vue.
|
||||||
- [NuxtHub](https://hub.nuxt.com): A Cloudflare-powered platform to deploy and scale Nuxt apps globally with minimal latency and full-stack capabilities.
|
- [NuxtHub](https://hub.nuxt.com): A Cloudflare-powered platform to deploy and scale Nuxt apps globally with minimal latency and full-stack capabilities.
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ tags:
|
|||||||
My homelab is a self-hosted environment where I deploy, test, and maintain personal services. Everything is securely exposed **only through a private VPN** using [Tailscale](https://tailscale.com/), ensuring encrypted, access-controlled connections across all devices.
|
My homelab is a self-hosted environment where I deploy, test, and maintain personal services. Everything is securely exposed **only through a private VPN** using [Tailscale](https://tailscale.com/), ensuring encrypted, access-controlled connections across all devices.
|
||||||
For selected services, I also use **Cloudflare Tunnels** to enable secure external access without opening ports or exposing my public IP.
|
For selected services, I also use **Cloudflare Tunnels** to enable secure external access without opening ports or exposing my public IP.
|
||||||
|
|
||||||
### 🛠️ Running Services
|
## 🛠️ Running Services
|
||||||
|
|
||||||
- **MinIO**: S3-compatible object storage for static files and backups.
|
- **MinIO**: S3-compatible object storage for static files and backups.
|
||||||
- **Immich**: Self-hosted photo management platform — a private alternative to Google Photos.
|
- **Immich**: Self-hosted photo management platform — a private alternative to Google Photos.
|
||||||
@@ -31,7 +31,7 @@ For selected services, I also use **Cloudflare Tunnels** to enable secure extern
|
|||||||
- **Beszel**: Self-hosted, lightweight alternative to Notion for notes and knowledge management.
|
- **Beszel**: Self-hosted, lightweight alternative to Notion for notes and knowledge management.
|
||||||
- **Palmr**: Personal logging and journaling tool.
|
- **Palmr**: Personal logging and journaling tool.
|
||||||
|
|
||||||
### 🖥️ Hardware
|
## 🖥️ Hardware
|
||||||
|
|
||||||
- **Beelink EQR6**: AMD Ryzen mini PC, main server host.
|
- **Beelink EQR6**: AMD Ryzen mini PC, main server host.
|
||||||
- **TP-Link 5-port Switch**: Network connectivity for all devices.
|
- **TP-Link 5-port Switch**: Network connectivity for all devices.
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ tags:
|
|||||||
|
|
||||||
It's designed to be fast, accessible, and fully responsive. The site also serves as a playground to explore and test modern frontend tools.
|
It's designed to be fast, accessible, and fully responsive. The site also serves as a playground to explore and test modern frontend tools.
|
||||||
|
|
||||||
### ⚒️ Tech Stack
|
## ⚒️ Tech Stack
|
||||||
|
|
||||||
- **UI** → [Vue.js](https://vuejs.org/): A progressive JavaScript framework for building interactive interfaces.
|
- **UI** → [Vue.js](https://vuejs.org/): A progressive JavaScript framework for building interactive interfaces.
|
||||||
- **Framework** → [Nuxt](https://nuxt.com/): A powerful full-stack framework built on Vue, perfect for modern web apps.
|
- **Framework** → [Nuxt](https://nuxt.com/): A powerful full-stack framework built on Vue, perfect for modern web apps.
|
||||||
|
|||||||
@@ -198,7 +198,6 @@
|
|||||||
"thanks": "Thanks for reading! My name is {name}, and I love writing about AI, data science, and the intersection between mathematics and programming. {jump} I've been coding and exploring math for years, and I'm always learning something new—whether it's self-hosting tools in my homelab, experimenting with machine learning models, or diving into statistical methods. {jump} I share my knowledge here because I know how valuable clear, hands-on resources can be, especially when you're just getting started or exploring something deeply technical. {jump} If you have any questions or just want to chat, feel free to reach out to me on {linkedin} or {github }. {jump} I hope you enjoyed this post and learned something useful. If you did, {comment}—it really helps and means a lot!",
|
"thanks": "Thanks for reading! My name is {name}, and I love writing about AI, data science, and the intersection between mathematics and programming. {jump} I've been coding and exploring math for years, and I'm always learning something new—whether it's self-hosting tools in my homelab, experimenting with machine learning models, or diving into statistical methods. {jump} I share my knowledge here because I know how valuable clear, hands-on resources can be, especially when you're just getting started or exploring something deeply technical. {jump} If you have any questions or just want to chat, feel free to reach out to me on {linkedin} or {github }. {jump} I hope you enjoyed this post and learned something useful. If you did, {comment}—it really helps and means a lot!",
|
||||||
"comment": "consider sharing it"
|
"comment": "consider sharing it"
|
||||||
},
|
},
|
||||||
"back": "Return to the canva",
|
|
||||||
"link": {
|
"link": {
|
||||||
"copied": "Link copied",
|
"copied": "Link copied",
|
||||||
"copy": "Copy link"
|
"copy": "Copy link"
|
||||||
|
|||||||
@@ -197,7 +197,6 @@
|
|||||||
"thanks": "¡Gracias por leer! Me llamo {name} y me encanta escribir sobre inteligencia artificial, ciencia de datos y todo lo que se encuentra en la intersección entre las matemáticas y la programación. {jump} Llevo años programando y explorando las matemáticas, y cada día aprendo algo nuevo — ya sea autoalojando herramientas en mi homelab, experimentando con modelos de aprendizaje automático o profundizando en métodos estadísticos. {jump} Comparto mis conocimientos aquí porque sé lo valiosos que pueden ser los recursos claros, prácticos y accesibles, especialmente cuando uno está empezando o explorando temas técnicos en profundidad. {jump} Si tienes alguna pregunta o simplemente quieres charlar, no dudes en dejar un comentario abajo o contactarme por {linkedin} o {github}. {jump} Espero que este artículo te haya gustado y que hayas aprendido algo útil. Si es así, {comment} — ¡me ayuda mucho y significa mucho para mí!",
|
"thanks": "¡Gracias por leer! Me llamo {name} y me encanta escribir sobre inteligencia artificial, ciencia de datos y todo lo que se encuentra en la intersección entre las matemáticas y la programación. {jump} Llevo años programando y explorando las matemáticas, y cada día aprendo algo nuevo — ya sea autoalojando herramientas en mi homelab, experimentando con modelos de aprendizaje automático o profundizando en métodos estadísticos. {jump} Comparto mis conocimientos aquí porque sé lo valiosos que pueden ser los recursos claros, prácticos y accesibles, especialmente cuando uno está empezando o explorando temas técnicos en profundidad. {jump} Si tienes alguna pregunta o simplemente quieres charlar, no dudes en dejar un comentario abajo o contactarme por {linkedin} o {github}. {jump} Espero que este artículo te haya gustado y que hayas aprendido algo útil. Si es así, {comment} — ¡me ayuda mucho y significa mucho para mí!",
|
||||||
"comment": "considera compartirlo"
|
"comment": "considera compartirlo"
|
||||||
},
|
},
|
||||||
"back": "Volver al lienzo",
|
|
||||||
"link": {
|
"link": {
|
||||||
"copied": "Link copiado",
|
"copied": "Link copiado",
|
||||||
"copy": "Copiar link"
|
"copy": "Copiar link"
|
||||||
|
|||||||
@@ -198,7 +198,6 @@
|
|||||||
"thanks": "Merci de votre lecture ! Je m'appelle {name}, et j'adore écrire sur l'intelligence artificielle, la data science, et tout ce qui se situe à l'intersection entre les mathématiques et la programmation. {jump} Je code et j'explore les maths depuis des années, et j'apprends encore de nouvelles choses chaque jour — que ce soit en auto-hébergeant des outils dans mon homelab, en expérimentant des modèles de machine learning ou en approfondissant des méthodes statistiques. {jump} Je partage mes connaissances ici parce que je sais à quel point des ressources claires, pratiques et accessibles peuvent être précieuses, surtout quand on débute ou qu'on explore un sujet technique en profondeur. {jump} Si vous avez des questions ou simplement envie d'échanger, n'hésitez pas à laisser un commentaire ci-dessous ou à me contacter sur {linkedin} ou {github}. {jump} J'espère que cet article vous a plu et qu'il vous a appris quelque chose d'utile. Si c'est le cas, {comment} — ça m'aide beaucoup et ça me fait vraiment plaisir !",
|
"thanks": "Merci de votre lecture ! Je m'appelle {name}, et j'adore écrire sur l'intelligence artificielle, la data science, et tout ce qui se situe à l'intersection entre les mathématiques et la programmation. {jump} Je code et j'explore les maths depuis des années, et j'apprends encore de nouvelles choses chaque jour — que ce soit en auto-hébergeant des outils dans mon homelab, en expérimentant des modèles de machine learning ou en approfondissant des méthodes statistiques. {jump} Je partage mes connaissances ici parce que je sais à quel point des ressources claires, pratiques et accessibles peuvent être précieuses, surtout quand on débute ou qu'on explore un sujet technique en profondeur. {jump} Si vous avez des questions ou simplement envie d'échanger, n'hésitez pas à laisser un commentaire ci-dessous ou à me contacter sur {linkedin} ou {github}. {jump} J'espère que cet article vous a plu et qu'il vous a appris quelque chose d'utile. Si c'est le cas, {comment} — ça m'aide beaucoup et ça me fait vraiment plaisir !",
|
||||||
"comment": "pensez à le partager"
|
"comment": "pensez à le partager"
|
||||||
},
|
},
|
||||||
"back": "Retourner sur le canva",
|
|
||||||
"link": {
|
"link": {
|
||||||
"copied": "Lien copié",
|
"copied": "Lien copié",
|
||||||
"copy": "Copier le lien"
|
"copy": "Copier le lien"
|
||||||
|
|||||||
Reference in New Issue
Block a user