Compare commits

...

12 Commits

Author SHA1 Message Date
4ed7228b60 chore: update package.json to include trustedDependencies and update favicon 2025-11-12 19:10:01 +01:00
d88fd80aee feat: update nuxt configuration and add nuxt-studio module
- Refactored nuxt.config.ts to include 'nuxt-studio' module.
- Removed preview API configuration from content settings.
- Added GitHub repository configuration for Nuxt Studio.
- Introduced prerender settings for the application.

chore: update package dependencies

- Bumped versions of several @iconify-json packages.
- Updated @pinia/nuxt to version 0.11.3.
- Updated vue-router to version 4.6.3 and wrangler to 4.45.4.
- Updated vue-tsc to version 3.1.3.
- Added nuxt-studio as a dependency.

fix: update binary PDF resumes

- Updated English and French resume PDFs in the public/resumes directory.
2025-11-12 19:00:09 +01:00
8856e77ae1 feat: mettre à jour les fichiers PDF de CV en anglais et en français 2025-10-23 12:44:52 +02:00
48e6043205 chore: add wrangler dependency to package.json 2025-10-03 20:32:05 +02:00
22c93c509d chore: update dependencies to latest versions
- Updated @nuxt/ui from ^4.0.0 to 4.0.1
- Updated typescript from ^5.9.2 to 5.9.3
- Updated @types/node from ^24.6.0 to 24.6.2
2025-10-03 17:41:34 +02:00
6c5b561d49 Refactor code structure for improved readability and maintainability 2025-10-03 17:37:34 +02:00
dbebcd23a5 Merge branch 'master' of https://github.com/ArthurDanjou/artchat 2025-10-03 17:05:27 +02:00
fa0421c51d feat: add @nuxtjs/seo module for improved SEO capabilities 2025-10-03 17:05:03 +02:00
6e648526d4 Revise ArtStudies project details and technologies
Updated project description and added M2 section with new technologies.
2025-10-03 11:33:25 +02:00
nuxthub-admin[bot]
f586394f80 ci: add nuxthub workflow 2025-09-30 10:21:07 +00:00
506152a986 feat: add config.json for deployment and update dependencies in bun.lock and nuxt.config.ts 2025-09-30 11:37:24 +02:00
f73276df8f refactor: remove Vercel references and update dependencies
- Removed Vercel from skills and theme descriptions.
- Updated theme description to only mention JetBrains Mono.
- Changed deployment references from Vercel to Cloudflare in multiple locales.
- Updated package dependencies:
  - @iconify-json/devicon from 1.2.44 to 1.2.45
  - @tailwindcss/typography from 0.5.18 to 0.5.19
  - vue from 3.5.21 to 3.5.22
  - @types/node from 24.5.2 to 24.6.0
  - vue-tsc from 3.0.8 to 3.1.0
2025-09-30 11:16:32 +02:00
47 changed files with 993 additions and 862 deletions

34
.github/workflows/nuxthub.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
name: Deploy to NuxtHub
on: push
jobs:
deploy:
name: "Deploy to NuxtHub"
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- name: Install Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 22
- name: Install dependencies
run: bun install
- name: Ensure NuxtHub module is installed
run: bunx nuxthub@latest ensure
- name: Build & Deploy to NuxtHub
uses: nuxt-hub/action@v2
with:
project-key: artchat-vuju

3
.gitignore vendored
View File

@@ -24,4 +24,5 @@ logs
.env
.env.*
!.env.example
.vercel
.wrangler

View File

@@ -1,7 +1,4 @@
<script lang="ts" setup>
import { Analytics } from '@vercel/analytics/nuxt'
import { SpeedInsights } from '@vercel/speed-insights/nuxt'
useHead({
link: [{ rel: 'icon', type: 'image/webp', href: '/favicon.webp' }],
})
@@ -31,7 +28,7 @@ const head = useLocaleHead()
<ChatCommandPalette
v-motion
: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') ? 'work' : 'chat'"
:initial="{
opacity: 0,
y: 200,
@@ -48,8 +45,6 @@ const head = useLocaleHead()
}"
/>
<NuxtPage />
<SpeedInsights />
<Analytics />
</UApp>
</template>

View File

@@ -15,7 +15,7 @@ const searchTerm = ref('')
const openMessageModal = ref(false)
const openClearModal = ref(false)
const { t, locale } = useI18n()
const { t, locale } = useI18n({ useScope: 'global' })
const { messages, submitMessage } = useChat()
const { clearMessages, messages: storeMessages } = useChatStore()
@@ -72,7 +72,7 @@ const toolTipContent = {
align: 'center',
side: 'top',
sideOffset: 0,
}
} as any
const router = useRouter()
function goHome() {
@@ -134,7 +134,7 @@ function isRoute(name: string): boolean {
<div class="absolute inset-0 -m-1" />
<div class="flex items-center gap-2.5">
<UIcon :name="item.icon!" size="20" />
<span>{{ t(item.label) }}</span>
<span>{{ t(item.label || '') }}</span>
</div>
<div class="text-dimmed text-xs font-medium text-start">
{{ t(item.prompt) }}

View File

@@ -5,7 +5,7 @@ import { ChatState } from '~~/types'
const props = defineProps<{ messageId: number, fetchStates: ChatFetchState[] }>()
const currentState = ref<ChatFetchState | undefined>(props.fetchStates[0] ?? undefined)
const { setLoadingState } = useChatStore()
const { t } = useI18n()
const { t } = useI18n({ useScope: 'global' })
onMounted(() => {
let index = 0

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
const { t } = useI18n()
const { t } = useI18n({ useScope: 'global' })
</script>
<template>

View File

@@ -8,7 +8,7 @@ const props = defineProps<{
const isArthur = computed(() => props.message.sender === ChatSender.ARTHUR)
const { t, locale } = useI18n()
const { t, locale } = useI18n({ useScope: 'global' })
const formatDate = computed(() => useDateFormat(props.message.createdAt, 'D MMMM YYYY, HH:mm', { locales: locale.value ?? 'en' }).value)
</script>

View File

@@ -23,7 +23,7 @@ const props = defineProps<{
message: ChatMessage
}>()
const { locale, t } = useI18n()
const { locale, t } = useI18n({ useScope: 'global' })
const formatDate = computed(() => useDateFormat(props.message.createdAt, 'D MMMM YYYY, HH:mm', { locales: locale.value ?? 'en' }).value)
const componentMap: Record<ChatType, Component | undefined> = {
@@ -66,9 +66,19 @@ const dynamicComponent = computed(() => componentMap[props.message.type])
</UCard>
<UCard
v-else
v-motion
variant="soft"
class="mt-1 w-full max-w-none bg-transparent"
:ui="{ body: 'p-0 sm:p-0', header: 'p-0 sm:p-0', footer: 'p-0 sm:p-0' }"
:initial="{
opacity: 0,
y: 20,
}"
:enter="{
opacity: 1,
y: 0,
transition: { ease: 'easeInOut', duration: 300, delay: 500 },
}"
>
<component
:is="dynamicComponent"

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
const { t, locale } = useI18n()
const { t, locale } = useI18n({ useScope: 'global' })
</script>
<template>

View File

@@ -1,11 +1,11 @@
<script lang="ts" setup>
const { t } = useI18n()
const { t } = useI18n({ useScope: 'global' })
</script>
<template>
<UCard class="mt-8 shadow-sm bg-white dark:bg-neutral-900">
<NuxtImg
src="/arthur pro.webp"
src="/arthur-pro.webp"
alt="Arthur Danjou"
class="w-24 h-24 rounded-full float-left mr-4 mb-4"
/>

View File

@@ -3,7 +3,7 @@ import type { UseTimeAgoMessages } from '@vueuse/core'
import type { Activity } from '~~/types'
import { activityMessages, IDEs } from '~~/types'
const { locale, t } = useI18n()
const { locale, t } = useI18n({ useScope: 'global' })
const { data: activity, refresh } = await useAsyncData<Activity>('activity', () => $fetch<Activity>('/api/activity'))
useIntervalFn(async () => await refresh(), 5000)

View File

@@ -1,7 +1,7 @@
<script lang="ts" setup>
import { socials } from '~~/types'
const { t } = useI18n()
const { t } = useI18n({ useScope: 'global' })
</script>
<template>

View File

@@ -1,5 +1,5 @@
<script lang="ts" setup>
const { t } = useI18n()
const { t } = useI18n({ useScope: 'global' })
const year = ref(useNow().value.getFullYear())
</script>
@@ -17,12 +17,12 @@ const year = ref(useNow().value.getFullYear())
class="px-0"
/>
</template>
<template #vercel>
<template #cloudflare>
<UButton
label="Vercel"
trailing-icon="i-logos-vercel-icon"
label="Cloudflare"
trailing-icon="i-logos-cloudflare-icon"
variant="link"
to="https://vercel.com"
to="https://cloudflare.com"
target="_blank"
class="px-0"
/>

View File

@@ -1,5 +1,5 @@
<script lang="ts" setup>
const { t } = useI18n()
const { t } = useI18n({ useScope: 'global' })
</script>
<template>

View File

@@ -1,7 +1,7 @@
<script lang="ts" setup>
const { data: experiences } = await useAsyncData('experiences', async () => await queryCollection('experiences').all())
const { t, locale } = useI18n()
const { t, locale } = useI18n({ useScope: 'global' })
const formatDate = (date: string) => useDateFormat(new Date(date), 'MMM YYYY', { locales: locale.value ?? 'en' }).value
function getLanguageForText(text: { en: string, es: string, fr: string }) {
return locale.value === 'en' ? text.en : locale.value === 'es' ? text.es : text.fr

View File

@@ -1,5 +1,5 @@
<script lang="ts" setup>
const { t } = useI18n()
const { t } = useI18n({ useScope: 'global' })
</script>
<template>

View File

@@ -1,7 +1,7 @@
<script lang="ts" setup>
import { en, es, fr } from '@nuxt/ui/locale'
const { locale, t } = useI18n()
const { locale, t } = useI18n({ useScope: 'global' })
const { changeLocale } = useLanguage()
</script>

View File

@@ -10,7 +10,7 @@
<div class="m-1 md:max-w-2/3 shadow-sm rounded-xl border border-gray-200 dark:border-gray-700 overflow-hidden relative z-10">
<NuxtImg class="rounded-xl" src="/location.png" />
<div class="size-12 rounded-full border-2 border-sky-500 absolute z-50 top-2/5 -translate-y-1/2 left-1/5 -translate-x-1/2 animate-bounce">
<NuxtImg src="/arthur pro.webp" class="rounded-full" alt="Location of Arthur" />
<NuxtImg src="/arthur-pro.webp" class="rounded-full" alt="Location of Arthur" />
</div>
</div>
</section>

View File

@@ -1,5 +1,5 @@
<script lang="ts" setup>
const { locale, t } = useI18n()
const { locale, t } = useI18n({ useScope: 'global' })
const { data: projects } = await useAsyncData('projects-index', async () => await queryCollection('projects').where('favorite', '=', true).select('title', 'description', 'id', 'publishedAt', 'tags', 'slug').all())
const date = (date: string) => useDateFormat(new Date(date), 'DD MMMM YYYY', { locales: locale.value ?? 'en' })

View File

@@ -1,5 +1,5 @@
<script lang="ts" setup>
const { t } = useI18n()
const { t } = useI18n({ useScope: 'global' })
interface ResumeFile {
name: string

View File

@@ -1,7 +1,7 @@
<script lang="ts" setup>
const { data: skills } = await useAsyncData('skills', async () => await queryCollection('skills').first())
const { t, locale } = useI18n()
const { t, locale } = useI18n({ useScope: 'global' })
</script>
<template>

View File

@@ -3,7 +3,7 @@ import type { Stats } from '~~/types'
const { data: stats } = await useAsyncData<Stats>('stats', () => $fetch('/api/stats'))
const { locale, t } = useI18n()
const { locale, t } = useI18n({ useScope: 'global' })
const time = useTimeAgo(new Date(stats.value!.coding.data.range.start) ?? new Date()).value.split(' ')[0]
const date = useDateFormat(new Date(stats.value!.coding.data.range.start ?? new Date()), 'DD MMMM YYYY', { locales: locale.value ?? 'en' })

View File

@@ -1,5 +1,5 @@
<script lang="ts" setup>
const { t } = useI18n()
const { t } = useI18n({ useScope: 'global' })
const { dark, toggleDark } = useTheme()
</script>

View File

@@ -6,7 +6,7 @@ const props = defineProps({
},
})
const { locale } = useI18n()
const { locale } = useI18n({ useScope: 'global' })
const { data: items } = await useAsyncData(`uses-${props.category}`, async () => await queryCollection('uses').where('category', '=', props.category).all())
const { data: categoryData } = await useAsyncData(`category-${props.category}`, async () => await queryCollection('usesCategories').where('slug', '=', props.category).first())

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import type { Weather } from '~~/types'
const { t } = useI18n()
const { t } = useI18n({ useScope: 'global' })
const { data: weather } = await useAsyncData<Weather>('weather', () =>
$fetch('/api/weather'))
</script>

View File

@@ -1,5 +1,5 @@
<script lang="ts" setup>
const { locale, t } = useI18n()
const { locale, t } = useI18n({ useScope: 'global' })
const { data: writings } = await useAsyncData('writings-index', async () => await queryCollection('writings').order('publishedAt', 'DESC').select('title', 'description', 'id', 'publishedAt', 'tags', 'slug').limit(2).all())
const formatDate = (date: string) => useDateFormat(new Date(date), 'DD MMMM YYYY', { locales: locale.value ?? 'en' })

View File

@@ -1,5 +1,5 @@
export function useLanguage() {
const { setLocale } = useI18n()
const { setLocale } = useI18n({ useScope: 'global' })
async function changeLocale(newLocale: string) {
await setLocale(newLocale as 'en' | 'fr' | 'es')

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
const { t } = useI18n()
const { t } = useI18n({ useScope: 'global' })
</script>
<template>

View File

@@ -10,7 +10,7 @@ const { messages } = useChatStore()
const parents = useTemplateRef('parents')
const { height } = useElementBounding(parents)
const { locale } = useI18n()
const { locale } = useI18n({ useScope: 'global' })
const lastLang = ref(locale.value)
watch(
height,

View File

@@ -1,4 +1,6 @@
<script lang="ts" setup>
import { useDateFormat } from '#imports'
const route = useRoute()
const { data: project } = await useAsyncData(`projects/${route.params.slug}`, () =>
queryCollection('projects').path(`/projects/${route.params.slug}`).first())
@@ -16,7 +18,9 @@ useSeoMeta({
author: 'Arthur Danjou',
})
const { t } = useI18n()
const { t } = useI18n({ useScope: 'global' })
useSeoMeta(project.value.seo || {})
</script>
<template>

View File

@@ -1,5 +1,5 @@
<script lang="ts" setup>
const { t } = useI18n()
const { t } = useI18n({ useScope: 'global' })
useSeoMeta({
title: 'My Projects',
description: t('projects.description'),

View File

@@ -1,4 +1,6 @@
<script lang="ts" setup>
import { useDateFormat } from '#imports'
const route = useRoute()
const { data: writing } = await useAsyncData(`writings/${route.params.slug}`, () =>
queryCollection('writings').path(`/writings/${route.params.slug}`).first())
@@ -10,13 +12,9 @@ if (!writing.value) {
})
}
useSeoMeta({
title: writing.value?.title,
description: writing.value?.description,
author: 'Arthur Danjou',
})
const { t } = useI18n({ useScope: 'global' })
const { t } = useI18n()
useSeoMeta(writing.value.seo || {})
</script>
<template>

View File

@@ -1,5 +1,5 @@
<script lang="ts" setup>
const { t } = useI18n()
const { t } = useI18n({ useScope: 'global' })
useSeoMeta({
title: 'My Shelf - Arthur DANJOU',
description: t('writings.description'),

1359
bun.lock

File diff suppressed because it is too large Load Diff

View File

@@ -1,104 +1,111 @@
import { defineCollection, z } from '@nuxt/content'
import { defineCollection, defineContentConfig, z } from '@nuxt/content'
import { asSeoCollection } from '@nuxtjs/seo/content'
export const collections = {
projects: defineCollection({
type: 'page',
source: 'projects/*.md',
schema: z.object({
slug: z.string(),
title: z.string(),
description: z.string(),
publishedAt: z.string(),
readingTime: z.number().optional(),
tags: z.array(z.string()),
cover: z.string(),
favorite: z.boolean().optional(),
canva: z.object({
height: z.number().default(270),
width: z.number().default(480),
}),
}),
}),
writings: defineCollection({
type: 'page',
source: 'writings/*.md',
schema: z.object({
slug: z.string(),
title: z.string(),
description: z.string(),
publishedAt: z.string(),
readingTime: z.number(),
cover: z.string().optional(),
tags: z.array(z.string()),
canva: z.object({
height: z.number().default(270),
width: z.number().default(480),
}),
}),
}),
usesCategories: defineCollection({
type: 'data',
source: 'uses/categories/*.json',
schema: z.object({
slug: z.string(),
name: z.object({
en: z.string(),
fr: z.string(),
es: z.string(),
}),
}),
}),
uses: defineCollection({
type: 'data',
source: 'uses/*.json',
schema: z.object({
name: z.string(),
description: z.object({
en: z.string(),
fr: z.string(),
es: z.string(),
}),
category: z.string(),
}),
}),
skills: defineCollection({
type: 'data',
source: 'skills.json',
schema: z.object({
body: z.array(z.object({
id: z.string(),
name: z.object({
en: z.string(),
fr: z.string(),
es: z.string(),
export default defineContentConfig({
collections: {
projects: defineCollection(
asSeoCollection({
type: 'page',
source: 'projects/*.md',
schema: z.object({
slug: z.string(),
title: z.string(),
description: z.string(),
publishedAt: z.string(),
readingTime: z.number().optional(),
tags: z.array(z.string()),
cover: z.string(),
favorite: z.boolean().optional(),
}),
items: z.array(z.object({
}),
),
writings: defineCollection(
asSeoCollection({
type: 'page',
source: 'writings/*.md',
schema: z.object({
slug: z.string(),
title: z.string(),
description: z.string(),
publishedAt: z.string(),
readingTime: z.number(),
cover: z.string().optional(),
tags: z.array(z.string()),
}),
}),
),
usesCategories: defineCollection(
asSeoCollection({
type: 'data',
source: 'uses/categories/*.json',
schema: z.object({
slug: z.string(),
name: z.object({
en: z.string(),
fr: z.string(),
es: z.string(),
}),
}),
}),
),
uses: defineCollection(
asSeoCollection({
type: 'data',
source: 'uses/*.json',
schema: z.object({
name: z.string(),
icon: z.string(),
})),
})),
}),
}),
experiences: defineCollection({
type: 'data',
source: 'experiences/*.json',
schema: z.object({
title: z.object({
en: z.string(),
fr: z.string(),
es: z.string(),
description: z.object({
en: z.string(),
fr: z.string(),
es: z.string(),
}),
category: z.string(),
}),
}),
company: z.string(),
companyUrl: z.string().url().optional(),
startDate: z.string(),
endDate: z.string().optional(),
location: z.string(),
description: z.object({
en: z.string(),
fr: z.string(),
es: z.string(),
),
skills: defineCollection(
asSeoCollection({
type: 'data',
source: 'skills.json',
schema: z.object({
body: z.array(z.object({
id: z.string(),
name: z.object({
en: z.string(),
fr: z.string(),
es: z.string(),
}),
items: z.array(z.object({
name: z.string(),
icon: z.string(),
})),
})),
}),
}),
tags: z.array(z.string()),
}),
}),
}
),
experiences: defineCollection(
asSeoCollection({
type: 'data',
source: 'experiences/*.json',
schema: z.object({
title: z.object({
en: z.string(),
fr: z.string(),
es: z.string(),
}),
company: z.string(),
companyUrl: z.string().url().optional(),
startDate: z.string(),
endDate: z.string().optional(),
location: z.string(),
description: z.object({
en: z.string(),
fr: z.string(),
es: z.string(),
}),
tags: z.array(z.string()),
}),
}),
),
},
})

View File

@@ -11,11 +11,14 @@ tags:
- r
---
[ArtStudies](https://go.arthurdanjou.fr/artstudies) is a curated collection of academic projects completed throughout my mathematics studies. The repository showcases work in both _Python_ and _R_, focusing on mathematical modeling, data analysis, and numerical methods.
# ArtStudies
[ArtStudies Projects](https://github.com/ArthurDanjou/artstudies) is a curated collection of academic projects completed throughout my mathematics studies. The repository showcases work in both _Python_ and _R_, focusing on mathematical modeling, data analysis, and numerical methods.
The projects are organized into two main sections:
- **L3** Third year of the Bachelor's degree in Mathematics
- **M1** First year of the Master's degree in Mathematics
- **M2** Second year of the Master's degree in Mathematics
## 📁 File Structure
@@ -38,6 +41,10 @@ The projects are organized into two main sections:
- `Portfolio Management`
- `Statistical Learning`
- `M2`
- `Machine Learning`
- `SQL`
## 🛠️ Technologies & Tools
- [Python](https://www.python.org): A high-level, interpreted programming language, widely used for data science, machine learning, and scientific computing.
@@ -49,6 +56,8 @@ The projects are organized into two main sections:
- [Scikit-learn](https://scikit-learn.org): A robust library offering simple and efficient tools for machine learning and statistical modeling, including classification, regression, and clustering.
- [TensorFlow](https://www.tensorflow.org): A comprehensive open-source framework for building and deploying machine learning and deep learning models.
- [Matplotlib](https://matplotlib.org): A versatile plotting library for creating high-quality static, animated, and interactive visualizations in Python.
- [Plotly](https://plotly.com): An interactive graphing library for creating dynamic visualizations in Python and R.
- [Seaborn](https://seaborn.pydata.org): A statistical data visualization library built on top of Matplotlib, providing a high-level interface for drawing attractive and informative graphics.
- [RMarkdown](https://rmarkdown.rstudio.com): A dynamic tool for combining code, results, and narrative into high-quality documents and presentations.
- [FactoMineR](https://factominer.free.fr/): An R package focused on multivariate exploratory data analysis (e.g., PCA, MCA, CA).
- [ggplot2](https://ggplot2.tidyverse.org): A grammar-based graphics package for creating complex and elegant visualizations in R.

View File

@@ -96,10 +96,6 @@
"name": "Linux",
"icon": "i-logos-linux-tux"
},
{
"name": "Vercel",
"icon": "i-logos-vercel-icon"
},
{
"name": "CloudFlare",
"icon": "i-logos-cloudflare-icon"
@@ -141,7 +137,7 @@
"name": {
"en": "Python Frameworks",
"fr": "Frameworks Python",
"es": "Frameworks de Python"
"es": "Frameworks Python"
},
"items": [
{

View File

@@ -1,9 +1,9 @@
{
"name": "Theme and Font",
"description": {
"en": "My theme is Catppuccin Macchiato, a community-driven pastel theme that aims to be the middle ground between low and high contrast themes. My main fonts are Vercel Geist and JetBrains Mono",
"fr": "Mon thème est Catppuccin Macchiato, un thème pastel piloté par la communauté qui vise à être le juste milieu entre les thèmes à faible et à fort contraste. Mes polices principales sont Vercel Geist et JetBrains Mono",
"es": "Mi tema es Catppuccin Macchiato, un tema pastel impulsado por la comunidad que tiene como objetivo ser el punto intermedio entre los temas de bajo y alto contraste. Mis fuentes principales son Vercel Geist y JetBrains Mono"
"en": "My theme is Catppuccin Macchiato, a community-driven pastel theme that aims to be the middle ground between low and high contrast themes. My main font is JetBrains Mono",
"fr": "Mon thème est Catppuccin Macchiato, un thème pastel piloté par la communauté qui vise à être le juste milieu entre les thèmes à faible et à fort contraste. Ma police principale est JetBrains Mono",
"es": "Mi tema es Catppuccin Macchiato, un tema pastel impulsado por la comunidad que tiene como objetivo ser el punto intermedio entre los temas de bajo y alto contraste. Mi fuente principal es JetBrains Mono"
},
"category": "ide"
}

View File

@@ -190,7 +190,7 @@
},
"hobbies": "Outside of programming and my technical projects, I dedicate much of my free time to my passions: sports, music, traveling, and spending time with friends. Sports teach me discipline and perseverance, music fuels my creativity, and traveling opens me up to new cultures and ways of thinking, which also nurtures my intellectual curiosity.\nThese passions help me maintain balance and strengthen the qualities I bring to both my studies and my career: curiosity, commitment, autonomy, and a constant desire to improve. They make me someone who is motivated, adaptable, and always ready to take on new challenges.",
"credits": {
"made": "This site was designed with {nuxt} and then deployed via {vercel}",
"made": "This site was designed with {nuxt} and then deployed via {cloudflare}",
"heart": "Made with ❤️ and a lot of reflection.",
"chat": "A big thank you to {chat}, my personal assistant, always ready to answer your questions with clarity and speed.",
"copyrights": "© {year} Arthur Danjou - All rights reserved.",
@@ -230,9 +230,6 @@
},
"top": "Go to top"
},
"canva": {
"title": "Loading the canva ..."
},
"writings": {
"description": "All my reflections on programming, mathematics, the conception of artificial intelligence, etc., are put in chronological order.",
"title": "Writings on math, artificial intelligence, development, and my passions.",

View File

@@ -182,7 +182,7 @@
"wind": "Viento"
},
"credits": {
"made": "Este sitio fue diseñado con {nuxt} y luego se implementó a través de {vercel}",
"made": "Este sitio fue diseñado con {nuxt} y luego se implementó a través de {cloudflare}",
"heart": "Hecho con ❤️ y mucho reflexión.",
"chat": "Muchas gracias a {chat}, mi asistente personal, siempre listo para responder a sus preguntas con claridad y velocidad.",
"copyrights": "© {year} Arthur Danjou - Todos los derechos reservados.",
@@ -233,9 +233,6 @@
"top": "Ir arriba"
},
"alert": "Por falta de tiempo, no tuve tiempo para traducir este contenido al francés. Gracias por su comprensión.",
"canva": {
"title": "Cargando el lienzo ..."
},
"writings": {
"description": "Todas mis reflexiones sobre programación, matemáticas, la concepción de la inteligencia artificial, etc., están organizadas en orden cronológico.",
"title": "Escritos sobre matemáticas, inteligencia artificial, desarrollo y mis pasiones.",

View File

@@ -191,7 +191,7 @@
},
"hobbies": "En dehors de la programmation et de mes projets techniques, je consacre une grande partie de mon temps libre à mes passions : le sport, la musique, les voyages et les moments partagés entre amis. Le sport m'apporte rigueur et persévérance, la musique stimule ma créativité, et voyager m'ouvre à d'autres cultures, à d'autres façons de penser, ce qui nourrit aussi ma curiosité intellectuelle. Ces passions m'aident à garder un bon équilibre et renforcent les qualités que je mobilise dans mes études et ma carrière: curiosité, engagement, autonomie et volonté constante de progresser. Elles font de moi quelqu'un de motivé, adaptable, et toujours prêt à relever de nouveaux défis.",
"credits": {
"made": "Ce site a été conçu avec {nuxt} et puis déployé via {vercel}",
"made": "Ce site a été conçu avec {nuxt} et puis déployé via {cloudflare}",
"heart": "Réalisé avec ❤️ et beaucoup de réflexion.",
"chat": "Un grand merci à {chat}, mon assistant personnel, toujours prêt à répondre à vos questions avec clarté et rapidité.",
"copyrights": "© {year} Arthur DANJOU — Tous droits réservés.",
@@ -231,9 +231,6 @@
},
"top": "Remonter en haut"
},
"canva": {
"title": "Chargement du canva..."
},
"writings": {
"description": "Toutes mes réflexions sur la programmation, les mathématiques, la conception de l'intelligence artificielle, etc., sont mises en ordre chronologique.",
"title": "Écrits sur les maths, l'intelligence artificielle, le développement et mes passions.",

View File

@@ -1,3 +1,5 @@
import { definePerson } from 'nuxt-schema-org/schema'
export default defineNuxtConfig({
compatibilityDate: '2025-07-20',
@@ -17,22 +19,45 @@ export default defineNuxtConfig({
css: ['~/assets/css/main.css'],
// Nuxt Modules
modules: [
'@nuxt/ui',
'@nuxt/content',
'@vueuse/nuxt',
'@nuxtjs/google-fonts',
'@nuxt/image',
'@vueuse/motion/nuxt',
'@pinia/nuxt',
'@nuxtjs/i18n',
],
modules: ['@nuxt/ui', '@nuxtjs/seo', '@nuxt/content', '@vueuse/nuxt', '@nuxtjs/google-fonts', '@nuxt/image', '@vueuse/motion/nuxt', '@pinia/nuxt', '@nuxtjs/i18n', 'nuxt-studio'],
ogImage: {
enabled: false,
},
linkChecker: {
enabled: false,
},
site: {
url: 'https://arthurdanjou.fr',
name: 'Developer enjoying Artificial Intelligence and Machine Learning. Mathematics Student at Paris Dauphine-PSL University specialised in Statistics and Data Science.',
},
schemaOrg: {
identity: definePerson({
// Basic Information, if applicable
name: 'Arthur Danjou',
givenName: 'Arthur',
familyName: 'Danjou',
// Profile Information, if applicable
image: '/arthur-pro.webp',
description: 'AI researcher and technical author specializing in machine learning and neural networks',
jobTitle: 'Principal AI Researcher',
// Contact & Social, if applicable
email: 'arthurdanjou@outlook.fr',
url: 'https://go.arthurdanjou.fr/website',
sameAs: [
'https://go.arthurdanjou.fr/twitter',
'https://go.arthurdanjou.fr/github',
'https://go.arthurdanjou.fr/linkedin',
],
}),
},
// Nuxt Content
content: {
preview: {
api: 'https://api.nuxt.studio',
},
build: {
markdown: {
highlight: {
@@ -51,6 +76,12 @@ export default defineNuxtConfig({
},
},
vite: {
build: {
sourcemap: false,
},
},
// Nuxt Color Mode
colorMode: {
preference: 'system',
@@ -63,6 +94,17 @@ export default defineNuxtConfig({
timeline: { enabled: true },
},
// Nuxt Studio
studio: {
// GitHub repository configuration (owner and repo are required)
repository: {
provider: 'github', // only GitHub is currently supported
owner: 'arthurdanjou', // your GitHub username or organization
repo: 'artchat', // your repository name
branch: 'main', // the branch to commit to (default: main)
},
},
// Nuxt I18N
i18n: {
strategy: 'no_prefix',
@@ -108,9 +150,17 @@ export default defineNuxtConfig({
// Nitro
nitro: {
experimental: {
websocket: true,
openAPI: true,
},
preset: 'cloudflare-module',
cloudflare: {
deployConfig: true,
nodeCompat: true,
},
prerender: {
routes: ['/'],
crawlLinks: true,
},
},
// Nuxt Env

View File

@@ -13,38 +13,43 @@
},
"dependencies": {
"@antfu/eslint-config": "^5.4.1",
"@iconify-json/devicon": "^1.2.44",
"@iconify-json/logos": "^1.2.9",
"@iconify-json/devicon": "^1.2.46",
"@iconify-json/logos": "^1.2.10",
"@iconify-json/ph": "^1.2.2",
"@iconify-json/simple-icons": "^1.2.53",
"@iconify-json/simple-icons": "^1.2.57",
"@iconify-json/twemoji": "^1.2.4",
"@iconify-json/vscode-icons": "^1.2.30",
"@iconify-json/vscode-icons": "^1.2.33",
"@nuxt/content": "3.7.1",
"@nuxt/eslint": "1.9.0",
"@nuxt/image": "^1.11.0",
"@nuxt/ui": "^4.0.0",
"@nuxt/ui": "4.0.1",
"@nuxtjs/google-fonts": "^3.2.0",
"@nuxtjs/i18n": "10.1.0",
"@pinia/nuxt": "^0.11.2",
"@tailwindcss/typography": "^0.5.18",
"@vercel/analytics": "^1.5.0",
"@vercel/speed-insights": "^1.2.0",
"@nuxtjs/seo": "^3.2.2",
"@pinia/nuxt": "^0.11.3",
"@tailwindcss/typography": "^0.5.19",
"@vueuse/math": "13.9.0",
"@vueuse/motion": "^3.0.3",
"better-sqlite3": "^12.4.1",
"eslint": "9.36.0",
"nuxt": "4.1.2",
"nuxt-studio": "1.0.0-alpha.1",
"rehype-katex": "^7.0.1",
"remark-math": "^6.0.0",
"remark-parse": "^11.0.0",
"remark-rehype": "^11.1.2",
"typescript": "^5.9.2",
"vue": "^3.5.21",
"vue-router": "^4.5.1"
"typescript": "5.9.3",
"vue": "^3.5.22",
"vue-router": "^4.6.3",
"wrangler": "^4.45.4"
},
"devDependencies": {
"@types/node": "^24.5.2",
"@types/node": "24.6.2",
"@vueuse/nuxt": "^13.9.0",
"vue-tsc": "^3.0.8"
}
"vue-tsc": "^3.1.3"
},
"trustedDependencies": [
"@parcel/watcher",
"unrs-resolver"
]
}

View File

Before

Width:  |  Height:  |  Size: 155 KiB

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

Binary file not shown.