mirror of
https://github.com/ArthurDanjou/artsite.git
synced 2026-01-29 23:28:14 +01:00
Refactor: Split portfolio to projects and writings sections, and update content structure
- Renamed 'portfolio' collection to 'projects' in content configuration. - Introduced a new 'writings' collection with corresponding schema. - Updated README to reflect changes in content structure and navigation. - Removed the old portfolio page and added new pages for projects and writings. - Added multiple new project and writing markdown files with relevant content. - Updated license year to 2025. - Enhanced AppHeader for new navigation links. - Improved ProseImg component styling.
This commit is contained in:
@@ -28,13 +28,22 @@ const navs = [
|
||||
},
|
||||
{
|
||||
label: {
|
||||
en: 'portfolio',
|
||||
fr: 'portfolio',
|
||||
en: 'writings',
|
||||
fr: 'écrits',
|
||||
es: 'escritos',
|
||||
},
|
||||
to: '/portfolio',
|
||||
to: '/writings',
|
||||
icon: 'books-duotone',
|
||||
},
|
||||
{
|
||||
label: {
|
||||
en: 'projects',
|
||||
fr: 'projets',
|
||||
es: 'proyectos',
|
||||
},
|
||||
to: '/projects',
|
||||
icon: 'code-duotone',
|
||||
},
|
||||
{
|
||||
label: {
|
||||
en: 'resume',
|
||||
|
||||
@@ -5,7 +5,7 @@ defineProps<{ src: string, label: string, caption?: string }>()
|
||||
<template>
|
||||
<div class="flex flex-col justify-center items-center prose-none my-8">
|
||||
<img :src="src" :alt="label" class="w-full h-auto m-0 prose-none">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-300 prose-none">
|
||||
<p class="mt-2 text-sm text-gray-500 dark:text-gray-300 prose-none">
|
||||
{{ caption }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { Tag } from '~~/types'
|
||||
import { TAGS } from '~~/types'
|
||||
|
||||
const { t, locale } = useI18n({
|
||||
useScope: 'local',
|
||||
})
|
||||
useSeoMeta({
|
||||
title: 'My Shelf',
|
||||
description: t('description'),
|
||||
})
|
||||
|
||||
const tagFilter = ref<string[]>([])
|
||||
|
||||
const { data: writings, refresh } = await useAsyncData('all-portfolio', async () => {
|
||||
const writings = await queryCollection('portfolio')
|
||||
.order('publishedAt', 'DESC')
|
||||
.all()
|
||||
return writings.filter((writing) => {
|
||||
if (tagFilter.value.length === 0)
|
||||
return true
|
||||
return writing.tags.some(tag => tagFilter.value.includes(tag.toLowerCase()))
|
||||
})
|
||||
})
|
||||
|
||||
watch(tagFilter, async () => await refresh())
|
||||
|
||||
const tags: Array<Tag> = [
|
||||
...TAGS.sort((a, b) => a.label.localeCompare(b.label)),
|
||||
]
|
||||
|
||||
function updateTag(payload: Tag[]) {
|
||||
tagFilter.value = payload.map(tag => tag.label.toLowerCase())
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="space-y-12">
|
||||
<AppTitle
|
||||
:description="t('description')"
|
||||
:title="t('title')"
|
||||
/>
|
||||
<UAlert
|
||||
v-if="locale !== 'en'"
|
||||
:description="t('alert.description')"
|
||||
:title="t('alert.title')"
|
||||
color="red"
|
||||
icon="i-ph-warning-duotone"
|
||||
variant="outline"
|
||||
/>
|
||||
<div class="flex justify-end sticky top-4 z-50">
|
||||
<USelectMenu
|
||||
:placeholder="t('tags')"
|
||||
:items="tags"
|
||||
multiple
|
||||
color="neutral"
|
||||
:highlight-on-hover="true"
|
||||
class="w-full md:w-1/3"
|
||||
@update:model-value="updateTag"
|
||||
/>
|
||||
</div>
|
||||
<ul class="grid grid-cols-1 gap-4">
|
||||
<NuxtLink
|
||||
v-for="(writing, id) in writings"
|
||||
:key="id"
|
||||
:to="writing.path"
|
||||
>
|
||||
<li
|
||||
class=" h-full border p-4 border-neutral-200 rounded-md hover:border-neutral-500 dark:border-neutral-800 dark:hover:border-neutral-600 duration-300"
|
||||
>
|
||||
<article class="space-y-2">
|
||||
<h1
|
||||
class="font-bold text-lg duration-300 text-black dark:text-white"
|
||||
>
|
||||
{{ writing.title }}
|
||||
</h1>
|
||||
<div
|
||||
class="text-sm text-neutral-500 duration-300 flex items-center gap-1"
|
||||
>
|
||||
<UIcon name="ph:calendar-duotone" size="16" />
|
||||
<p>{{ useDateFormat(writing.publishedAt, 'DD MMMM YYYY').value }} </p>·
|
||||
<UIcon name="ph:timer-duotone" size="16" />
|
||||
<p>{{ writing.readingTime }}min long</p>
|
||||
</div>
|
||||
<h3>
|
||||
{{ writing.description }}
|
||||
</h3>
|
||||
</article>
|
||||
<div class="flex gap-2 mt-4 flex-wrap">
|
||||
<ClientOnly>
|
||||
<UBadge
|
||||
v-for="tag in writing.tags.sort((a: any, b: any) => a.localeCompare(b))"
|
||||
:key="tag"
|
||||
:color="TAGS.find(color => color.label.toLowerCase() === tag)?.color"
|
||||
variant="soft"
|
||||
size="sm"
|
||||
class="rounded-full"
|
||||
>
|
||||
<div class="flex gap-1 items-center">
|
||||
<UIcon :name="TAGS.find(icon => icon.label.toLowerCase() === tag)?.icon || ''" size="16" />
|
||||
<p>{{ TAGS.find(color => color.label.toLowerCase() === tag)?.label }}</p>
|
||||
</div>
|
||||
</UBadge>
|
||||
</ClientOnly>
|
||||
</div>
|
||||
</li>
|
||||
</NuxtLink>
|
||||
</ul>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<i18n lang="json">
|
||||
{
|
||||
"en": {
|
||||
"title": "Writing on my life, development, academic and personal projects and passions",
|
||||
"description": "All my thoughts on programming, mathematics, artificial intelligence design, etc., are put together in chronological order. I also write about my projects, my discoveries, and my thoughts.",
|
||||
"alert": {
|
||||
"title": "Translations alert!",
|
||||
"description": "Due to time constraints, all article translations will be available only in English. Thank you for your understanding."
|
||||
},
|
||||
"tags": "Select tags to filter"
|
||||
},
|
||||
"fr": {
|
||||
"title": "Écrits sur ma vie, le développement, mes projets et mes passions.",
|
||||
"description": "Toutes mes réflexions sur la programmation, les mathématiques, la conception de l'intelligence artificielle, etc., sont mises en ordre chronologique. J'écris aussi sur mes projets, mes découvertes et mes pensées.",
|
||||
"alert": {
|
||||
"title": "Attentions aux traductions!",
|
||||
"description": "Par soucis de temps, toutes les traductions des articles seront disponibles uniquement en anglais. Merci de votre compréhension."
|
||||
},
|
||||
"tags": "Sélectionner des tags pour filtrer"
|
||||
},
|
||||
"es": {
|
||||
"title": "Escritos sobre mi vida, el desarrollo, mis proyectos y mis pasiones.",
|
||||
"description": " Todas mis reflexiones sobre la programación, las matemáticas, la conception de la inteligencia artificial, etc. están puestas en orden cronológico. También escribo sobre mis proyectos, mis descubrimientos y mis pensamientos.",
|
||||
"alert": {
|
||||
"title": "Cuidado con las traducciones !",
|
||||
"description": "Por problema de tiempo, los artículos están solo disponibles en ingles. Gracias por vuestra comprensión."
|
||||
},
|
||||
"tags": "Seleccionar etiquetas para filtrar"
|
||||
}
|
||||
}
|
||||
</i18n>
|
||||
|
||||
<style scoped>
|
||||
.tablist > button {
|
||||
cursor: pointer !important;
|
||||
}
|
||||
</style>
|
||||
124
app/pages/projects/[slug].vue
Normal file
124
app/pages/projects/[slug].vue
Normal file
@@ -0,0 +1,124 @@
|
||||
<script lang="ts" setup>
|
||||
const route = useRoute()
|
||||
const { data: project } = await useAsyncData(`projects/${route.params.slug}`, () =>
|
||||
queryCollection('projects').path(`/projects/${route.params.slug}`).first())
|
||||
|
||||
const { locale } = useI18n()
|
||||
const { t } = useI18n({
|
||||
useScope: 'local',
|
||||
})
|
||||
|
||||
useSeoMeta({
|
||||
title: project.value?.title,
|
||||
description: project.value?.description,
|
||||
author: 'Arthur Danjou',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main v-if="project">
|
||||
<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="/projects"
|
||||
>
|
||||
<UIcon
|
||||
class="group-hover:-translate-x-1 transform duration-300"
|
||||
name="i-ph-arrow-left-duotone"
|
||||
size="20"
|
||||
/>
|
||||
{{ t('back') }}
|
||||
</NuxtLinkLocale>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<div class="flex items-end justify-between gap-2 flex-wrap">
|
||||
<h1
|
||||
class="font-bold text-3xl text-black dark:text-white"
|
||||
>
|
||||
{{ project.title }}
|
||||
</h1>
|
||||
<div
|
||||
class="text-sm text-neutral-500 duration-300 flex items-center gap-1"
|
||||
>
|
||||
<UIcon name="ph:calendar-duotone" size="16" />
|
||||
<p>{{ useDateFormat(project.publishedAt, 'DD MMMM YYYY').value }} </p>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mt-2 text-base">
|
||||
{{ project.description }}
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
v-if="project.cover"
|
||||
class="w-full rounded-md my-8"
|
||||
>
|
||||
<ProseImg
|
||||
:src="`/projects/${project.cover}`"
|
||||
label="Project cover"
|
||||
/>
|
||||
</div>
|
||||
<USeparator
|
||||
class="my-4"
|
||||
icon="i-ph-pencil-line-duotone"
|
||||
/>
|
||||
<UAlert
|
||||
v-if="locale !== 'en'"
|
||||
:description="t('alert.description')"
|
||||
:title="t('alert.title')"
|
||||
class="mb-8"
|
||||
color="red"
|
||||
icon="i-ph-warning-duotone"
|
||||
variant="outline"
|
||||
/>
|
||||
<ContentRenderer
|
||||
:value="project"
|
||||
class="!max-w-none prose dark:prose-invert"
|
||||
/>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.prose h2 a,
|
||||
.prose h3 a,
|
||||
.prose h4 a {
|
||||
@apply no-underline;
|
||||
}
|
||||
|
||||
.prose img {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.katex-html {
|
||||
display: none;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n lang="json">
|
||||
{
|
||||
"en": {
|
||||
"alert": {
|
||||
"title": "Translations alert!",
|
||||
"description": "Due to time constraints, all article translations will be available only in English. Thank you for your understanding."
|
||||
},
|
||||
"back": "Go back"
|
||||
},
|
||||
"fr": {
|
||||
"alert": {
|
||||
"title": "Attentions aux traductions!",
|
||||
"description": "Par soucis de temps, toutes les traductions des articles seront disponibles uniquement en anglais. Merci de votre compréhension."
|
||||
},
|
||||
"back": "Retourner en arrière"
|
||||
},
|
||||
"es": {
|
||||
"alert": {
|
||||
"title": "Cuidado con las traducciones!",
|
||||
"description": " Por problemas de tiempo, los artículos solo están disponibles en inglés. Gracias por vuestra comprensión.ug ñeóicula."
|
||||
},
|
||||
"back": "Volver atrás"
|
||||
}
|
||||
}
|
||||
</i18n>
|
||||
92
app/pages/projects/index.vue
Normal file
92
app/pages/projects/index.vue
Normal file
@@ -0,0 +1,92 @@
|
||||
<script setup lang="ts">
|
||||
import { TAGS } from '~~/types'
|
||||
|
||||
const { t } = useI18n({
|
||||
useScope: 'local',
|
||||
})
|
||||
useSeoMeta({
|
||||
title: 'My Projects',
|
||||
description: t('description'),
|
||||
})
|
||||
|
||||
const { data: projects } = await useAsyncData('all-projects', () => {
|
||||
return queryCollection('projects')
|
||||
.order('publishedAt', 'DESC')
|
||||
.all()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="space-y-12">
|
||||
<AppTitle
|
||||
:description="t('description')"
|
||||
:title="t('title')"
|
||||
/>
|
||||
<ul class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<NuxtLink
|
||||
v-for="(project, id) in projects"
|
||||
:key="id"
|
||||
:to="project.path"
|
||||
>
|
||||
<li
|
||||
class="flex flex-col justify-between h-full border p-4 border-neutral-200 rounded-md hover:border-neutral-500 dark:border-neutral-800 dark:hover:border-neutral-600 duration-300"
|
||||
>
|
||||
<article class="space-y-2">
|
||||
<div
|
||||
class="flex flex-col gap-2"
|
||||
>
|
||||
<h1 class="font-bold text-lg text-black dark:text-white">
|
||||
{{ project.title }}
|
||||
</h1>
|
||||
<h3 class="text-md text-neutral-500 dark:text-neutral-400">
|
||||
{{ project.description }}
|
||||
</h3>
|
||||
</div>
|
||||
</article>
|
||||
<div class="flex items-center justify-between gap-2 mt-4">
|
||||
<div
|
||||
class="text-sm text-neutral-500 flex items-center gap-1"
|
||||
>
|
||||
<UIcon name="ph:calendar-duotone" size="16" />
|
||||
<p>{{ useDateFormat(project.publishedAt, 'DD MMMM YYYY').value }} </p>
|
||||
</div>
|
||||
<div class="flex gap-2 flex-wrap">
|
||||
<ClientOnly>
|
||||
<UBadge
|
||||
v-for="tag in project.tags.sort((a: any, b: any) => a.localeCompare(b))"
|
||||
:key="tag"
|
||||
:color="TAGS.find(color => color.label.toLowerCase() === tag)?.color as any"
|
||||
variant="soft"
|
||||
size="sm"
|
||||
class="rounded-full"
|
||||
>
|
||||
<div class="flex gap-1 items-center">
|
||||
<UIcon :name="TAGS.find(icon => icon.label.toLowerCase() === tag)?.icon || ''" size="16" />
|
||||
<p>{{ TAGS.find(color => color.label.toLowerCase() === tag)?.label }}</p>
|
||||
</div>
|
||||
</UBadge>
|
||||
</ClientOnly>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</NuxtLink>
|
||||
</ul>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<i18n lang="json">
|
||||
{
|
||||
"en": {
|
||||
"title": "All my projects I have worked on, both academic and personal",
|
||||
"description": "A collection of my projects using R, Python, or web development technologies. These projects span various domains, including data analysis, machine learning, and web applications, showcasing my skills in coding, problem-solving, and project development."
|
||||
},
|
||||
"fr": {
|
||||
"title": "Tous mes projets auxquels j'ai travaillé, académiques et personnels",
|
||||
"description": "Une collection de mes projets réalisés en R, Python, ou en développement web. Ces projets couvrent divers domaines, y compris l'analyse de données, l'apprentissage automatique et les applications web, mettant en avant mes compétences en codage, résolution de problèmes et développement de projets."
|
||||
},
|
||||
"es": {
|
||||
"title": "Todos mis proyectos en los que he trabajado, académicos y personales",
|
||||
"description": "Una colección de mis proyectos realizados en R, Python o tecnologías de desarrollo web. Estos proyectos abarcan diversos campos, como análisis de datos, aprendizaje automático y aplicaciones web, mostrando mis habilidades en programación, resolución de problemas y desarrollo de proyectos."
|
||||
}
|
||||
}
|
||||
</i18n>
|
||||
@@ -1,12 +1,12 @@
|
||||
<script lang="ts" setup>
|
||||
const route = useRoute()
|
||||
const { data: post } = await useAsyncData(`portfolio/${route.params.slug}`, () =>
|
||||
queryCollection('portfolio').path(`/portfolio/${route.params.slug}`).first())
|
||||
const { data: post } = await useAsyncData(`writings/${route.params.slug}`, () =>
|
||||
queryCollection('writings').path(`/writings/${route.params.slug}`).first())
|
||||
|
||||
const {
|
||||
data: postDB,
|
||||
refresh,
|
||||
} = await useAsyncData(`portfolio/${route.params.slug}/db`, () => $fetch(`/api/posts/${route.params.slug}`, { method: 'POST' }))
|
||||
} = await useAsyncData(`writings/${route.params.slug}/db`, () => $fetch(`/api/posts/${route.params.slug}`, { method: 'POST' }))
|
||||
|
||||
const { locale } = useI18n()
|
||||
const { t } = useI18n({
|
||||
@@ -22,7 +22,7 @@ function top() {
|
||||
}
|
||||
|
||||
const { copy, copied } = useClipboard({
|
||||
source: `https://arthurdanjou.fr/portfolio/${route.params.slug}`,
|
||||
source: `https://arthurdanjou.fr/writings/${route.params.slug}`,
|
||||
copiedDuring: 4000,
|
||||
})
|
||||
|
||||
@@ -70,7 +70,7 @@ function scrollToSection(id: string) {
|
||||
<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="/portfolio"
|
||||
to="/writings"
|
||||
>
|
||||
<UIcon
|
||||
class="group-hover:-translate-x-1 transform duration-300"
|
||||
@@ -115,7 +115,7 @@ function scrollToSection(id: string) {
|
||||
{{ post.description }}
|
||||
</p>
|
||||
</div>
|
||||
<div v-if="post.body.toc && post.body.toc.links.length > 0" class="flex justify-end sticky top-0 z-50">
|
||||
<div v-if="post.body.toc && post.body.toc.links.length > 0" class="flex justify-end sticky top-0 z-50 !cursor-pointer">
|
||||
<UPopover
|
||||
mode="click"
|
||||
:content="{
|
||||
@@ -128,11 +128,10 @@ function scrollToSection(id: string) {
|
||||
:label="t('toc')"
|
||||
color="neutral"
|
||||
variant="solid"
|
||||
class="mt-2"
|
||||
/>
|
||||
|
||||
<template #content>
|
||||
<div class="text-neutral-500 p-2">
|
||||
<div class="text-neutral-500">
|
||||
<div
|
||||
v-for="link in post!.body!.toc!.links"
|
||||
:key="link.id"
|
||||
@@ -156,9 +155,9 @@ function scrollToSection(id: string) {
|
||||
v-if="post.cover"
|
||||
class="w-full rounded-md mb-8"
|
||||
>
|
||||
<NuxtImg
|
||||
:src="`/portfolio/${post.cover}`"
|
||||
alt="Writing cover"
|
||||
<ProseImg
|
||||
:src="`/writings/${post.cover}`"
|
||||
label="Writing cover"
|
||||
/>
|
||||
</div>
|
||||
<USeparator
|
||||
@@ -185,7 +184,7 @@ function scrollToSection(id: string) {
|
||||
</i18n-t>
|
||||
<div class="flex gap-4 items-center flex-wrap">
|
||||
<UButton
|
||||
:label="postDB?.likes > 1 ? `${postDB?.likes} likes` : `${postDB?.likes} like`"
|
||||
:label="(postDB?.likes ?? 0) > 1 ? `${postDB?.likes ?? 0} likes` : `${postDB?.likes ?? 0} like`"
|
||||
:color="likeCookie ? 'red' : 'neutral'"
|
||||
icon="i-ph-heart-duotone"
|
||||
size="lg"
|
||||
118
app/pages/writings/index.vue
Normal file
118
app/pages/writings/index.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<script setup lang="ts">
|
||||
import { TAGS } from '~~/types'
|
||||
|
||||
const { t, locale } = useI18n({
|
||||
useScope: 'local',
|
||||
})
|
||||
useSeoMeta({
|
||||
title: 'My Shelf',
|
||||
description: t('description'),
|
||||
})
|
||||
|
||||
const { data: writings } = await useAsyncData('all-writings', () => {
|
||||
return queryCollection('writings')
|
||||
.order('publishedAt', 'DESC')
|
||||
.all()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="space-y-12">
|
||||
<AppTitle
|
||||
:description="t('description')"
|
||||
:title="t('title')"
|
||||
/>
|
||||
<UAlert
|
||||
v-if="locale !== 'en'"
|
||||
:description="t('alert.description')"
|
||||
:title="t('alert.title')"
|
||||
color="red"
|
||||
icon="i-ph-warning-duotone"
|
||||
variant="outline"
|
||||
/>
|
||||
<ul class="grid grid-cols-1 gap-4">
|
||||
<NuxtLink
|
||||
v-for="(writing, id) in writings"
|
||||
:key="id"
|
||||
:to="writing.path"
|
||||
>
|
||||
<li
|
||||
class=" h-full border p-4 border-neutral-200 rounded-md hover:border-neutral-500 dark:border-neutral-800 dark:hover:border-neutral-600 duration-300"
|
||||
>
|
||||
<article class="space-y-2">
|
||||
<h1
|
||||
class="font-bold text-lg duration-300 text-black dark:text-white"
|
||||
>
|
||||
{{ writing.title }}
|
||||
</h1>
|
||||
<h3>
|
||||
{{ writing.description }}
|
||||
</h3>
|
||||
</article>
|
||||
<div class="flex justify-between items-center mt-2">
|
||||
<div
|
||||
class="text-sm text-neutral-500 duration-300 flex items-center gap-1"
|
||||
>
|
||||
<UIcon name="ph:calendar-duotone" size="16" />
|
||||
<p>{{ useDateFormat(writing.publishedAt, 'DD MMMM YYYY').value }} </p>·
|
||||
<UIcon name="ph:timer-duotone" size="16" />
|
||||
<p>{{ writing.readingTime }}min long</p>
|
||||
</div>
|
||||
<div class="flex gap-2 flex-wrap">
|
||||
<ClientOnly>
|
||||
<UBadge
|
||||
v-for="tag in writing.tags.sort((a: any, b: any) => a.localeCompare(b))"
|
||||
:key="tag"
|
||||
:color="TAGS.find(color => color.label.toLowerCase() === tag)?.color as any"
|
||||
variant="soft"
|
||||
size="sm"
|
||||
class="rounded-full"
|
||||
>
|
||||
<div class="flex gap-1 items-center">
|
||||
<UIcon :name="TAGS.find(icon => icon.label.toLowerCase() === tag)?.icon || ''" size="16" />
|
||||
<p>{{ TAGS.find(color => color.label.toLowerCase() === tag)?.label }}</p>
|
||||
</div>
|
||||
</UBadge>
|
||||
</ClientOnly>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</NuxtLink>
|
||||
</ul>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<i18n lang="json">
|
||||
{
|
||||
"en": {
|
||||
"title": "Writings on math, artificial intelligence, development, and my passions.",
|
||||
"description": "All my reflections on programming, mathematics, artificial intelligence design, etc., are organized chronologically.",
|
||||
"alert": {
|
||||
"title": "Attention to translations!",
|
||||
"description": "For time reasons, all article translations will only be available in English. Thank you for your understanding."
|
||||
}
|
||||
},
|
||||
"fr": {
|
||||
"title": "Écrits sur les maths, l'intelligence artificielle, le développement et mes passions.",
|
||||
"description": "Toutes mes réflexions sur la programmation, les mathématiques, la conception de l'intelligence artificielle, etc., sont mises en ordre chronologique.",
|
||||
"alert": {
|
||||
"title": "Attentions aux traductions!",
|
||||
"description": "Par soucis de temps, toutes les traductions des articles seront disponibles uniquement en anglais. Merci de votre compréhension."
|
||||
}
|
||||
},
|
||||
"es": {
|
||||
"title": "Escritos sobre matemáticas, inteligencia artificial, desarrollo y mis pasiones.",
|
||||
"description": "Todas mis reflexiones sobre programación, matemáticas, diseño de inteligencia artificial, etc., están organizadas cronológicamente.",
|
||||
"alert": {
|
||||
"title": "¡Atención a las traducciones!",
|
||||
"description": "Por razones de tiempo, todas las traducciones de los artículos estarán disponibles solo en inglés. Gracias por su comprensión."
|
||||
}
|
||||
}
|
||||
}
|
||||
</i18n>
|
||||
|
||||
<style scoped>
|
||||
.tablist > button {
|
||||
cursor: pointer !important;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user