diff --git a/app/app.vue b/app/app.vue index d0af7f8..40a139f 100644 --- a/app/app.vue +++ b/app/app.vue @@ -1,6 +1,6 @@ diff --git a/app/components/AppHeader.vue b/app/components/AppHeader.vue index 464a90c..5e82d49 100644 --- a/app/components/AppHeader.vue +++ b/app/components/AppHeader.vue @@ -84,6 +84,7 @@ const socialsList = [ /> + diff --git a/app/components/home/Education.vue b/app/components/home/Education.vue new file mode 100644 index 0000000..6d1ab4f --- /dev/null +++ b/app/components/home/Education.vue @@ -0,0 +1,61 @@ + + + diff --git a/app/components/home/Experiences.vue b/app/components/home/Experiences.vue new file mode 100644 index 0000000..809b11b --- /dev/null +++ b/app/components/home/Experiences.vue @@ -0,0 +1,100 @@ + + + diff --git a/app/components/home/Quote.vue b/app/components/home/Quote.vue index 7837b52..1622c7d 100644 --- a/app/components/home/Quote.vue +++ b/app/components/home/Quote.vue @@ -7,7 +7,7 @@ alt="Avatar" class="hover:rotate-360 duration-500 transform-gpu rounded-full" size="xl" - src="/favicon.webp" + src="/arthur.webp" /> diff --git a/app/components/home/Skills.vue b/app/components/home/Skills.vue new file mode 100644 index 0000000..e16daea --- /dev/null +++ b/app/components/home/Skills.vue @@ -0,0 +1,43 @@ + + + diff --git a/app/composables/content.ts b/app/composables/content.ts new file mode 100644 index 0000000..9f46199 --- /dev/null +++ b/app/composables/content.ts @@ -0,0 +1,8 @@ +export async function useContent() { + const skills = await queryCollection('skills').where('extension', '=', 'json').first() + const projects = await queryCollection('projects').where('extension', '=', 'md').order('publishedAt', 'DESC').all() + const education = await queryCollection('education').where('extension', '=', 'md').order('startDate', 'DESC').all() + const experiences = await queryCollection('experiences').where('extension', '=', 'md').order('startDate', 'DESC').all() + + return { skills, projects, education, experiences } +} diff --git a/app/pages/activity.vue b/app/pages/activity.vue new file mode 100644 index 0000000..b139ec6 --- /dev/null +++ b/app/pages/activity.vue @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/app/pages/ecosystem.vue b/app/pages/ecosystem.vue new file mode 100644 index 0000000..b139ec6 --- /dev/null +++ b/app/pages/ecosystem.vue @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/app/pages/hobbies.vue b/app/pages/hobbies.vue new file mode 100644 index 0000000..a75fbb9 --- /dev/null +++ b/app/pages/hobbies.vue @@ -0,0 +1,15 @@ + + + diff --git a/app/pages/index.vue b/app/pages/index.vue index c899412..c5850b3 100644 --- a/app/pages/index.vue +++ b/app/pages/index.vue @@ -16,6 +16,9 @@ const { data: page } = await useAsyncData('index', () => { :value="page" class="mt-8 md:mt-16" /> + + + diff --git a/app/pages/projects.vue b/app/pages/projects.vue index 2a0f008..c4f6f8a 100644 --- a/app/pages/projects.vue +++ b/app/pages/projects.vue @@ -1,13 +1,12 @@ - - \ No newline at end of file diff --git a/app/pages/uses.vue b/app/pages/uses.vue new file mode 100644 index 0000000..dcb9d1f --- /dev/null +++ b/app/pages/uses.vue @@ -0,0 +1,15 @@ + + + diff --git a/bun.lock b/bun.lock index ac54975..1363057 100644 --- a/bun.lock +++ b/bun.lock @@ -24,6 +24,7 @@ }, "devDependencies": { "@iconify-json/devicon": "1.2.54", + "@iconify-json/file-icons": "^1.2.2", "@iconify-json/logos": "^1.2.10", "@iconify-json/ph": "^1.2.2", "@iconify-json/twemoji": "1.2.5", @@ -236,6 +237,8 @@ "@iconify-json/devicon": ["@iconify-json/devicon@1.2.54", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-I188YZ/t+SF2bfZnrRjwmdr/nzUNXuc/S3uvsZF0LG6atOuGtKk7KcmK/NUaTB2JAIeM1hsD+7wieYBObI8O4Q=="], + "@iconify-json/file-icons": ["@iconify-json/file-icons@1.2.2", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-ajk44wYGTiu79EAyrfNHNaxZ1/2Z1xuSOAvYUT/pAPWHc+P6n9TUZP/ccnPG18kx0WMBxmkHHQ9/zkm6ENhk+Q=="], + "@iconify-json/logos": ["@iconify-json/logos@1.2.10", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-qxaXKJ6fu8jzTMPQdHtNxlfx6tBQ0jXRbHZIYy5Ilh8Lx9US9FsAdzZWUR8MXV8PnWTKGDFO4ZZee9VwerCyMA=="], "@iconify-json/lucide": ["@iconify-json/lucide@1.2.73", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-++HFkqDNu4jqG5+vYT+OcVj9OiuPCw9wQuh8G5QWQnBRSJ9eKwSStiU8ORgOoK07xJsm/0VIHySMniXUUXP9Gw=="], diff --git a/content.config.ts b/content.config.ts index 719c5d8..9578d9c 100644 --- a/content.config.ts +++ b/content.config.ts @@ -30,8 +30,7 @@ export const collections = { type: 'data', source: 'skills.json', schema: z.object({ - description: z.string(), - skills: z.array(z.object({ + body: z.array(z.object({ id: z.string(), name: z.string(), description: z.string().optional(), @@ -80,10 +79,9 @@ export const collections = { type: 'data', source: 'contact.json', schema: z.object({ - contact: z.array(z.object({ + body: z.array(z.object({ id: z.string(), name: z.string(), - description: z.string().optional(), category: z.string().optional(), icon: z.string().optional(), value: z.string().url(), @@ -100,7 +98,7 @@ export const collections = { type: 'data', source: 'languages.json', schema: z.object({ - languages: z.array(z.object({ + body: z.array(z.object({ name: z.string(), level: z.string(), proficiency: z.string() diff --git a/content/contact.json b/content/contact.json index 21f21c9..60e2622 100644 --- a/content/contact.json +++ b/content/contact.json @@ -1,77 +1,69 @@ { - "contact": [ - { - "id": "personal-email", - "name": "Email Personnel", - "description": "Contactez-moi pour des questions personnelles", - "category": "communication", - "icon": "i-ph-envelope-simple-duotone", - "value": "https://go.arthurdanjou.fr/mail-perso", - "priority": 1 - }, - { - "id": "professional-email", - "name": "Email Professionnel", - "description": "Pour les opportunités professionnelles et collaborations", - "category": "communication", - "icon": "i-ph-envelope-simple-duotone", - "value": "https://go.arthurdanjou.fr/mail-pro", - "priority": 1 - }, - { - "id": "linkedin", - "name": "LinkedIn", - "description": "Profil professionnel et réseau", - "category": "social", - "icon": "i-ph:linkedin-logo-duotone", - "value": "https://go.arthurdanjou.fr/linkedin", - "priority": 2 - }, - { - "id": "github", - "name": "GitHub", - "description": "Projets open-source et portefeuille technique", - "category": "social", - "icon": "i-ph:github-logo-duotone", - "value": "https://go.arthurdanjou.fr/github", - "username": "ArthurDanjou", - "priority": 1 - }, - { - "id": "twitter", - "name": "Twitter / X", - "description": "Actualités tech et partages d'idées", - "category": "social", - "icon": "i-ph:x-logo-duotone", - "value": "https://go.arthurdanjou.fr/twitter", - "priority": 3 - }, - { - "id": "discord", - "name": "Discord", - "description": "Discussions en temps réel et communauté", - "category": "communication", - "icon": "i-ph:discord-logo-duotone", - "value": "https://go.arthurdanjou.fr/discord", - "priority": 2 - }, - { - "id": "personal-website", - "name": "Site Personnel", - "description": "Accueil et portefeuille complet", - "category": "web", - "icon": "i-ph:globe-duotone", - "value": "https://arthurdanjou.fr", - "priority": 1 - }, - { - "id": "status-page", - "name": "Statut des Services", - "description": "État et disponibilité des services", - "category": "infrastructure", - "icon": "i-ph:fire-duotone", - "value": "https://go.arthurdanjou.fr/status", - "priority": 3 - } - ] + "body": [ + { + "id": "personal-email", + "name": "Email Personnel", + "category": "communication", + "icon": "i-ph-envelope-simple-duotone", + "value": "https://go.arthurdanjou.fr/mail-perso", + "priority": 1 + }, + { + "id": "professional-email", + "name": "Email Professionnel", + "category": "communication", + "icon": "i-ph-envelope-simple-duotone", + "value": "https://go.arthurdanjou.fr/mail-pro", + "priority": 1 + }, + { + "id": "linkedin", + "name": "LinkedIn", + "category": "social", + "icon": "i-ph:linkedin-logo-duotone", + "value": "https://go.arthurdanjou.fr/linkedin", + "priority": 2 + }, + { + "id": "github", + "name": "GitHub", + "category": "social", + "icon": "i-ph:github-logo-duotone", + "value": "https://go.arthurdanjou.fr/github", + "username": "ArthurDanjou", + "priority": 1 + }, + { + "id": "twitter", + "name": "Twitter / X", + "category": "social", + "icon": "i-ph:x-logo-duotone", + "value": "https://go.arthurdanjou.fr/twitter", + "priority": 3 + }, + { + "id": "discord", + "name": "Discord", + "category": "communication", + "icon": "i-ph:discord-logo-duotone", + "value": "https://go.arthurdanjou.fr/discord", + "priority": 2 + }, + { + "id": "personal-website", + "name": "Site Personnel", + "category": "web", + "icon": "i-ph:globe-duotone", + "value": "https://arthurdanjou.fr", + "priority": 1 + }, + { + "id": "status-page", + "name": "Statut des Services", + "category": "infrastructure", + "icon": "i-ph:fire-duotone", + "value": "https://go.arthurdanjou.fr/status", + "priority": 3 + } + ] } \ No newline at end of file diff --git a/content/experiences/hackathon-cnd.md b/content/experiences/hackathon-cnd.md new file mode 100644 index 0000000..d9cf609 --- /dev/null +++ b/content/experiences/hackathon-cnd.md @@ -0,0 +1,19 @@ +--- +title: Hackathon CND - Machine Learning for Cybersecurity +type: Hackathon +company: Commissariat au numérique de défense (CND), French Armies ministry +companyUrl: https://www.defense.gouv.fr/cnd +location: Fort du Mont-Valérien, Suresnes, France +startDate: 2025-11 +endDate: 2025-11 +duration: 3 days +description: Developed a Python ML pipeline during the CND hackathon to classify system logs for bug and attack detection. Implemented feature extraction and preprocessing, trained and evaluated models (tree-based and lightweight neural), tuned thresholds to favor recall, and delivered a realtime prototype with visualization and reproducible code in collaboration with CND engineers. Implemented a Streamlit application to test the classifier interactively and used an LLM to generate contextual help explaining the likely origin and indicators of detected bugs or attacks for end users. +tags: + - Python + - Machine Learning + - AI + - Cybersecurity + - Streamlit + - LLM +emoji: 🔒 +--- diff --git a/content/experiences/hackathon-dirisi.md b/content/experiences/hackathon-dirisi.md deleted file mode 100644 index a019092..0000000 --- a/content/experiences/hackathon-dirisi.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -title: Hackathon CND - Machine Learning for Cybersecurity -type: Hackathon -company: Commissariat au numérique de défense (CND), French Armies ministry -companyUrl: https://www.defense.gouv.fr/cnd -location: Fort du Mont-Valérien, Suresnes, France -startDate: 2025-11 -endDate: 2025-11 -duration: 3 days -description: Developed a Python ML pipeline during the Dirisi hackathon to classify system logs for bug and attack detection. Implemented feature extraction and preprocessing, trained and evaluated models (tree-based and lightweight neural), tuned thresholds to favor recall, and delivered a realtime prototype with visualization and reproducible code in collaboration with CND engineers. Implemented a Streamlit application to test the classifier interactively and used an LLM to generate contextual help explaining the likely origin and indicators of detected bugs or attacks for end users. -tags: - - Python - - Machine Learning - - AI - - Cybersecurity - - Streamlit - - LLM -emoji: 🔒 ---- diff --git a/content/experiences/sevetys.md b/content/experiences/sevetys.md index 7963040..ea12b9d 100644 --- a/content/experiences/sevetys.md +++ b/content/experiences/sevetys.md @@ -1,5 +1,5 @@ --- -title: Data Analyst Intern +title: Data Engineer Intern type: Internship company: Sevetys companyUrl: https://sevetys.fr @@ -7,7 +7,7 @@ location: Paris, France startDate: 2025-06 endDate: 2025-07 duration: 2 months -description: At Sevetys, I worked as a Data Analyst on topics related to client and patient data. My responsibilities included Python development using PySpark on Microsoft Azure, data modeling based on business needs, and ensuring data quality. This experience allowed me to deepen my data engineering skills while working autonomously in a demanding cloud-based environment. +description: At Sevetys, I worked as a Data Engineer on topics related to client and patient data. My responsibilities included Python development using PySpark on Microsoft Azure, data modeling based on business needs, and ensuring data quality. This experience allowed me to deepen my data engineering skills while working autonomously in a demanding cloud-based environment. tags: - Python - PySpark diff --git a/content/index.md b/content/index.md index ec913b0..1604433 100644 --- a/content/index.md +++ b/content/index.md @@ -32,4 +32,4 @@ These tools allow me to go from :hover-text{hover="Exploration, cleaning, reshap I'm :hover-text{hover="As tech is always evolving, I need to be up-to-date 🖥️" position="top" text="constantly"} learning new things, from technology to finance and entrepreneurship. I love :hover-text{hover="I love sharing my knowledge and helping others 🫂" text="sharing"} my knowledge and learning new theorems and technologies. I'm a :hover-text{hover="I'm constantly looking to discover new things" text="curious"} person and eager to continue learning and growing throughout my life. -As well as programming, I enjoy :hover-text{hover="Sport allows me to burn off energy 🏋️‍♂️" text="sport"} and :hover-text{hover="Travelling frees me and gets me away from it all ✈️" text="travelling"} . My passion, commitment and eagerness to learn and progress are the qualities that enable me to succeed in my :hover-text{hover="Career already begun and far from over 😎" text="career"} and :hover-text{hover="Only 2 years of study left 💪" text="studies"} . +As well as programming, I enjoy :hover-text{hover="Sport allows me to burn off energy 🏋️‍♂️" text="sport"} and :hover-text{hover="Travelling frees me and gets me away from it all ✈️" text="travelling"} . My passion, commitment and eagerness to learn and progress are the qualities that enable me to succeed in my :hover-text{hover="Career already begun and far from over 😎" text="career"} and :hover-text{hover="Only 2 years of study left 💪" text="studies"} . \ No newline at end of file diff --git a/content/languages.json b/content/languages.json index 4b79dd2..f068fa1 100644 --- a/content/languages.json +++ b/content/languages.json @@ -1,5 +1,5 @@ { - "languages": [ + "body": [ { "name": "French", "level": "Native", diff --git a/content/skills.json b/content/skills.json index fd4c8f8..1555356 100644 --- a/content/skills.json +++ b/content/skills.json @@ -1,6 +1,5 @@ { - "description": "Master's student in Applied Mathematics (M280 - Paris Dauphine) specializing in Data Science and AI. My profile sits at the intersection of theoretical research and software engineering. I leverage my strong background in probability, statistics, and optimization to design robust Deep Learning architectures, while using my engineering skills (MLOps, Infrastructure) to deploy them efficiently. Currently looking for a research-oriented final year internship (April 2026) leading to a PhD.", - "skills": [ + "body": [ { "id": "scientific-computing", "name": "Scientific Computing & AI", @@ -31,9 +30,13 @@ "icon": "i-devicon-scikitlearn" }, { - "name": "Pandas & Numpy", + "name": "Pandas", "icon": "i-devicon-pandas" }, + { + "name": "NumPy", + "icon": "i-logos-numpy" + }, { "name": "MatPlotLib", "icon": "i-devicon-matplotlib" @@ -76,6 +79,10 @@ { "name": "Apache Spark (PySpark)", "icon": "i-logos-apache-spark" + }, + { + "name": "Cloudflare", + "icon": "i-logos-cloudflare-icon" } ] }, @@ -103,6 +110,10 @@ { "name": "AdonisJs", "icon": "i-logos-adonisjs-icon" + }, + { + "name": "Gradio", + "icon": "i-logos-gradio-icon" } ] } diff --git a/content/uses.md b/content/uses.md index 961575d..e9aecfd 100644 --- a/content/uses.md +++ b/content/uses.md @@ -33,6 +33,7 @@ This page documents all the tools, equipment and services I use daily for my wor ### IDEs - **Visual Studio Code** - My main development environment. Flexible, performant and lightweight. Supports Python, JavaScript, TypeScript, SQL and much more. I especially appreciate the extensions and AI integrations +- **Cursor** - A VSCode fork with AI-powered code completions and suggestions to boost productivity - **JetBrains Suite** (IntelliJ IDEA Ultimate, PyCharm Professional, WebStorm, DataGrip) - Which I've been using for 7 years. The best IDEs for Java, Python, JavaScript, SQL and other languages ### Theme and Fonts @@ -68,12 +69,11 @@ This page documents all the tools, equipment and services I use daily for my wor ### Self-Hosted Services I maintain several services: + - **Monitoring & Infrastructure**: Uptime Kuma, Beszel, Traefik, Portainer - **Security & Privacy**: Cloudflare, AdGuard Home, Vaultwarden, Tailscale - **Storage & Media**: Minio, Immich - **Smart Home**: Home Assistant - **Other Utilities**: MySpeed, Palmr, Cap.so ---- - *This list is constantly updated as I experiment with new tools and equipment.* diff --git a/nuxt.config.ts b/nuxt.config.ts index 42216bc..a574972 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -33,6 +33,13 @@ export default defineNuxtConfig({ fallback: 'light' }, + content: { + database: { + type: 'd1', + bindingName: 'DB' + } + }, + mdc: { headings: { anchorLinks: false @@ -83,9 +90,11 @@ export default defineNuxtConfig({ nitro: { preset: 'cloudflare_module', + experimental: { + openAPI: true + }, prerender: { - routes: ['/'], crawlLinks: true } }, diff --git a/package.json b/package.json index e5c0c1b..7861e44 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ }, "devDependencies": { "@iconify-json/devicon": "1.2.54", + "@iconify-json/file-icons": "^1.2.2", "@iconify-json/logos": "^1.2.10", "@iconify-json/ph": "^1.2.2", "@iconify-json/twemoji": "1.2.5", diff --git a/public/favicon.webp b/public/favicon.ico similarity index 100% rename from public/favicon.webp rename to public/favicon.ico diff --git a/server/api/contact.get.ts b/server/api/contact.get.ts index e4546cd..b536f13 100644 --- a/server/api/contact.get.ts +++ b/server/api/contact.get.ts @@ -5,6 +5,8 @@ export default defineCachedEventHandler(async (event) => { .where('extension', '=', 'json') .first() + console.log(await queryCollection(event, 'contact').all()) + if (!result) { throw createError({ statusCode: 404, statusMessage: 'Contact information not found' }) } diff --git a/server/api/hobbies.get.ts b/server/api/hobbies.get.ts deleted file mode 100644 index 5f03a9b..0000000 --- a/server/api/hobbies.get.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { queryCollection } from '@nuxt/content/server' - -export default defineCachedEventHandler(async (event) => { - const result = await queryCollection(event, 'hobbies') - .where('extension', '=', 'md') - .first() - if (!result) { - throw createError({ statusCode: 404, statusMessage: 'Hobbies not found' }) - } - - return result.body -}, { - maxAge: 60 * 60 * 24, - name: 'hobbies' -}) diff --git a/server/api/profile.get.ts b/server/api/profile.get.ts deleted file mode 100644 index 633053d..0000000 --- a/server/api/profile.get.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { queryCollection } from '@nuxt/content/server' - -export default defineCachedEventHandler(async (event) => { - const result = await queryCollection(event, 'profile') - .where('extension', '=', 'md') - .first() - - if (!result) { - throw createError({ statusCode: 404, statusMessage: 'Profile not found' }) - } - - return result -}, { - maxAge: 60 * 60 * 24, - name: 'profile' -}) diff --git a/server/api/uses.get.ts b/server/api/uses.get.ts deleted file mode 100644 index fa0dff2..0000000 --- a/server/api/uses.get.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { queryCollection } from '@nuxt/content/server' - -export default defineCachedEventHandler(async (event) => { - const categories = await queryCollection(event, 'usesCategories') - .where('extension', '=', 'md') - .all() - - if (categories.length === 0) { - throw createError({ statusCode: 404, statusMessage: 'Uses categories not found' }) - } - - const uses = await queryCollection(event, 'uses') - .where('extension', '=', 'md') - .all() - - if (uses.length === 0) { - throw createError({ statusCode: 404, statusMessage: 'Uses not found' }) - } - - const uses_by_categories = [] - for (const category of categories) { - uses_by_categories.push({ - category: category, - uses: uses.filter((use: { category: unknown }) => use.category === category.slug) - }) - } - - return uses_by_categories -}, -{ - maxAge: 60 * 60 * 24, - name: 'uses' -}) diff --git a/types/index.ts b/types/index.ts index 7fe8079..fb4ca4a 100644 --- a/types/index.ts +++ b/types/index.ts @@ -59,8 +59,12 @@ interface Nav { export const navs: readonly Nav[] = [ { label: 'home', to: '/', icon: 'house-duotone' }, + { label: 'uses', to: '/uses', icon: 'tree-evergreen-duotone' }, { label: 'projects', to: '/projects', icon: 'folder-duotone' }, + { label: 'hobbies', to: '/hobbies', icon: 'game-controller-duotone' }, { label: 'stats', to: '/stats', icon: 'chart-bar-duotone' }, + { label: 'activity', to: '/activity', icon: 'activity-duotone' }, + { label: 'ecosystem', to: '/ecosystem', icon: 'graph-duotone' }, { label: 'resume', icon: 'address-book-duotone',