mirror of
https://github.com/ArthurDanjou/artchat.git
synced 2026-01-29 16:57:47 +01:00
Compare commits
11 Commits
f73276df8f
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 4ed7228b60 | |||
| d88fd80aee | |||
| 8856e77ae1 | |||
| 48e6043205 | |||
| 22c93c509d | |||
| 6c5b561d49 | |||
| dbebcd23a5 | |||
| fa0421c51d | |||
| 6e648526d4 | |||
|
|
f586394f80 | ||
| 506152a986 |
34
.github/workflows/nuxthub.yml
vendored
Normal file
34
.github/workflows/nuxthub.yml
vendored
Normal 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
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -24,3 +24,5 @@ logs
|
|||||||
.env
|
.env
|
||||||
.env.*
|
.env.*
|
||||||
!.env.example
|
!.env.example
|
||||||
|
|
||||||
|
.wrangler
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ const head = useLocaleHead()
|
|||||||
<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') ? 'work' : 'chat'"
|
||||||
:initial="{
|
:initial="{
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
y: 200,
|
y: 200,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const searchTerm = ref('')
|
|||||||
const openMessageModal = ref(false)
|
const openMessageModal = ref(false)
|
||||||
const openClearModal = ref(false)
|
const openClearModal = ref(false)
|
||||||
|
|
||||||
const { t, locale } = useI18n()
|
const { t, locale } = useI18n({ useScope: 'global' })
|
||||||
const { messages, submitMessage } = useChat()
|
const { messages, submitMessage } = useChat()
|
||||||
const { clearMessages, messages: storeMessages } = useChatStore()
|
const { clearMessages, messages: storeMessages } = useChatStore()
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ const toolTipContent = {
|
|||||||
align: 'center',
|
align: 'center',
|
||||||
side: 'top',
|
side: 'top',
|
||||||
sideOffset: 0,
|
sideOffset: 0,
|
||||||
}
|
} as any
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
function goHome() {
|
function goHome() {
|
||||||
@@ -134,7 +134,7 @@ function isRoute(name: string): boolean {
|
|||||||
<div class="absolute inset-0 -m-1" />
|
<div class="absolute inset-0 -m-1" />
|
||||||
<div class="flex items-center gap-2.5">
|
<div class="flex items-center gap-2.5">
|
||||||
<UIcon :name="item.icon!" size="20" />
|
<UIcon :name="item.icon!" size="20" />
|
||||||
<span>{{ t(item.label) }}</span>
|
<span>{{ t(item.label || '') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-dimmed text-xs font-medium text-start">
|
<div class="text-dimmed text-xs font-medium text-start">
|
||||||
{{ t(item.prompt) }}
|
{{ t(item.prompt) }}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { ChatState } from '~~/types'
|
|||||||
const props = defineProps<{ messageId: number, fetchStates: ChatFetchState[] }>()
|
const props = defineProps<{ messageId: number, fetchStates: ChatFetchState[] }>()
|
||||||
const currentState = ref<ChatFetchState | undefined>(props.fetchStates[0] ?? undefined)
|
const currentState = ref<ChatFetchState | undefined>(props.fetchStates[0] ?? undefined)
|
||||||
const { setLoadingState } = useChatStore()
|
const { setLoadingState } = useChatStore()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n({ useScope: 'global' })
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
let index = 0
|
let index = 0
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const { t } = useI18n()
|
const { t } = useI18n({ useScope: 'global' })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ const props = defineProps<{
|
|||||||
|
|
||||||
const isArthur = computed(() => props.message.sender === ChatSender.ARTHUR)
|
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)
|
const formatDate = computed(() => useDateFormat(props.message.createdAt, 'D MMMM YYYY, HH:mm', { locales: locale.value ?? 'en' }).value)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const props = defineProps<{
|
|||||||
message: ChatMessage
|
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 formatDate = computed(() => useDateFormat(props.message.createdAt, 'D MMMM YYYY, HH:mm', { locales: locale.value ?? 'en' }).value)
|
||||||
|
|
||||||
const componentMap: Record<ChatType, Component | undefined> = {
|
const componentMap: Record<ChatType, Component | undefined> = {
|
||||||
@@ -66,9 +66,19 @@ const dynamicComponent = computed(() => componentMap[props.message.type])
|
|||||||
</UCard>
|
</UCard>
|
||||||
<UCard
|
<UCard
|
||||||
v-else
|
v-else
|
||||||
|
v-motion
|
||||||
variant="soft"
|
variant="soft"
|
||||||
class="mt-1 w-full max-w-none bg-transparent"
|
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' }"
|
: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
|
<component
|
||||||
:is="dynamicComponent"
|
:is="dynamicComponent"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const { t, locale } = useI18n()
|
const { t, locale } = useI18n({ useScope: 'global' })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const { t } = useI18n()
|
const { t } = useI18n({ useScope: 'global' })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UCard class="mt-8 shadow-sm bg-white dark:bg-neutral-900">
|
<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"
|
||||||
class="w-24 h-24 rounded-full float-left mr-4 mb-4"
|
class="w-24 h-24 rounded-full float-left mr-4 mb-4"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { UseTimeAgoMessages } from '@vueuse/core'
|
|||||||
import type { Activity } from '~~/types'
|
import type { Activity } from '~~/types'
|
||||||
import { activityMessages, IDEs } 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'))
|
const { data: activity, refresh } = await useAsyncData<Activity>('activity', () => $fetch<Activity>('/api/activity'))
|
||||||
|
|
||||||
useIntervalFn(async () => await refresh(), 5000)
|
useIntervalFn(async () => await refresh(), 5000)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { socials } from '~~/types'
|
import { socials } from '~~/types'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n({ useScope: 'global' })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const { t } = useI18n()
|
const { t } = useI18n({ useScope: 'global' })
|
||||||
|
|
||||||
const year = ref(useNow().value.getFullYear())
|
const year = ref(useNow().value.getFullYear())
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const { t } = useI18n()
|
const { t } = useI18n({ useScope: 'global' })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const { data: experiences } = await useAsyncData('experiences', async () => await queryCollection('experiences').all())
|
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
|
const formatDate = (date: string) => useDateFormat(new Date(date), 'MMM YYYY', { locales: locale.value ?? 'en' }).value
|
||||||
function getLanguageForText(text: { en: string, es: string, fr: string }) {
|
function getLanguageForText(text: { en: string, es: string, fr: string }) {
|
||||||
return locale.value === 'en' ? text.en : locale.value === 'es' ? text.es : text.fr
|
return locale.value === 'en' ? text.en : locale.value === 'es' ? text.es : text.fr
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const { t } = useI18n()
|
const { t } = useI18n({ useScope: 'global' })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { en, es, fr } from '@nuxt/ui/locale'
|
import { en, es, fr } from '@nuxt/ui/locale'
|
||||||
|
|
||||||
const { locale, t } = useI18n()
|
const { locale, t } = useI18n({ useScope: 'global' })
|
||||||
const { changeLocale } = useLanguage()
|
const { changeLocale } = useLanguage()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -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">
|
<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" />
|
<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">
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<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 { 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' })
|
const date = (date: string) => useDateFormat(new Date(date), 'DD MMMM YYYY', { locales: locale.value ?? 'en' })
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const { t } = useI18n()
|
const { t } = useI18n({ useScope: 'global' })
|
||||||
|
|
||||||
interface ResumeFile {
|
interface ResumeFile {
|
||||||
name: string
|
name: string
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const { data: skills } = await useAsyncData('skills', async () => await queryCollection('skills').first())
|
const { data: skills } = await useAsyncData('skills', async () => await queryCollection('skills').first())
|
||||||
|
|
||||||
const { t, locale } = useI18n()
|
const { t, locale } = useI18n({ useScope: 'global' })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { Stats } from '~~/types'
|
|||||||
|
|
||||||
const { data: stats } = await useAsyncData<Stats>('stats', () => $fetch('/api/stats'))
|
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 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' })
|
const date = useDateFormat(new Date(stats.value!.coding.data.range.start ?? new Date()), 'DD MMMM YYYY', { locales: locale.value ?? 'en' })
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const { t } = useI18n()
|
const { t } = useI18n({ useScope: 'global' })
|
||||||
const { dark, toggleDark } = useTheme()
|
const { dark, toggleDark } = useTheme()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -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: 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())
|
const { data: categoryData } = await useAsyncData(`category-${props.category}`, async () => await queryCollection('usesCategories').where('slug', '=', props.category).first())
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Weather } from '~~/types'
|
import type { Weather } from '~~/types'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n({ useScope: 'global' })
|
||||||
const { data: weather } = await useAsyncData<Weather>('weather', () =>
|
const { data: weather } = await useAsyncData<Weather>('weather', () =>
|
||||||
$fetch('/api/weather'))
|
$fetch('/api/weather'))
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<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 { 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' })
|
const formatDate = (date: string) => useDateFormat(new Date(date), 'DD MMMM YYYY', { locales: locale.value ?? 'en' })
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export function useLanguage() {
|
export function useLanguage() {
|
||||||
const { setLocale } = useI18n()
|
const { setLocale } = useI18n({ useScope: 'global' })
|
||||||
|
|
||||||
async function changeLocale(newLocale: string) {
|
async function changeLocale(newLocale: string) {
|
||||||
await setLocale(newLocale as 'en' | 'fr' | 'es')
|
await setLocale(newLocale as 'en' | 'fr' | 'es')
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const { t } = useI18n()
|
const { t } = useI18n({ useScope: 'global' })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const { messages } = useChatStore()
|
|||||||
const parents = useTemplateRef('parents')
|
const parents = useTemplateRef('parents')
|
||||||
const { height } = useElementBounding(parents)
|
const { height } = useElementBounding(parents)
|
||||||
|
|
||||||
const { locale } = useI18n()
|
const { locale } = useI18n({ useScope: 'global' })
|
||||||
const lastLang = ref(locale.value)
|
const lastLang = ref(locale.value)
|
||||||
watch(
|
watch(
|
||||||
height,
|
height,
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { useDateFormat } from '#imports'
|
||||||
|
|
||||||
const route = useRoute()
|
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())
|
||||||
@@ -16,7 +18,9 @@ useSeoMeta({
|
|||||||
author: 'Arthur Danjou',
|
author: 'Arthur Danjou',
|
||||||
})
|
})
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n({ useScope: 'global' })
|
||||||
|
|
||||||
|
useSeoMeta(project.value.seo || {})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const { t } = useI18n()
|
const { t } = useI18n({ useScope: 'global' })
|
||||||
useSeoMeta({
|
useSeoMeta({
|
||||||
title: 'My Projects',
|
title: 'My Projects',
|
||||||
description: t('projects.description'),
|
description: t('projects.description'),
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { useDateFormat } from '#imports'
|
||||||
|
|
||||||
const route = useRoute()
|
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())
|
||||||
@@ -10,13 +12,9 @@ if (!writing.value) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
useSeoMeta({
|
const { t } = useI18n({ useScope: 'global' })
|
||||||
title: writing.value?.title,
|
|
||||||
description: writing.value?.description,
|
|
||||||
author: 'Arthur Danjou',
|
|
||||||
})
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
useSeoMeta(writing.value.seo || {})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const { t } = useI18n()
|
const { t } = useI18n({ useScope: 'global' })
|
||||||
useSeoMeta({
|
useSeoMeta({
|
||||||
title: 'My Shelf - Arthur DANJOU',
|
title: 'My Shelf - Arthur DANJOU',
|
||||||
description: t('writings.description'),
|
description: t('writings.description'),
|
||||||
|
|||||||
@@ -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 = {
|
export default defineContentConfig({
|
||||||
projects: defineCollection({
|
collections: {
|
||||||
type: 'page',
|
projects: defineCollection(
|
||||||
source: 'projects/*.md',
|
asSeoCollection({
|
||||||
schema: z.object({
|
type: 'page',
|
||||||
slug: z.string(),
|
source: 'projects/*.md',
|
||||||
title: z.string(),
|
schema: z.object({
|
||||||
description: z.string(),
|
slug: z.string(),
|
||||||
publishedAt: z.string(),
|
title: z.string(),
|
||||||
readingTime: z.number().optional(),
|
description: z.string(),
|
||||||
tags: z.array(z.string()),
|
publishedAt: z.string(),
|
||||||
cover: z.string(),
|
readingTime: z.number().optional(),
|
||||||
favorite: z.boolean().optional(),
|
tags: z.array(z.string()),
|
||||||
canva: z.object({
|
cover: z.string(),
|
||||||
height: z.number().default(270),
|
favorite: z.boolean().optional(),
|
||||||
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(),
|
|
||||||
}),
|
}),
|
||||||
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(),
|
name: z.string(),
|
||||||
icon: z.string(),
|
description: z.object({
|
||||||
})),
|
en: z.string(),
|
||||||
})),
|
fr: z.string(),
|
||||||
}),
|
es: z.string(),
|
||||||
}),
|
}),
|
||||||
experiences: defineCollection({
|
category: z.string(),
|
||||||
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(),
|
skills: defineCollection(
|
||||||
startDate: z.string(),
|
asSeoCollection({
|
||||||
endDate: z.string().optional(),
|
type: 'data',
|
||||||
location: z.string(),
|
source: 'skills.json',
|
||||||
description: z.object({
|
schema: z.object({
|
||||||
en: z.string(),
|
body: z.array(z.object({
|
||||||
fr: z.string(),
|
id: z.string(),
|
||||||
es: 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()),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|||||||
@@ -11,11 +11,14 @@ tags:
|
|||||||
- r
|
- 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:
|
The projects are organized into two main sections:
|
||||||
- **L3** – Third year of the Bachelor's degree in Mathematics
|
- **L3** – Third year of the Bachelor's degree in Mathematics
|
||||||
- **M1** – First year of the Master'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
|
## 📁 File Structure
|
||||||
|
|
||||||
@@ -38,6 +41,10 @@ The projects are organized into two main sections:
|
|||||||
- `Portfolio Management`
|
- `Portfolio Management`
|
||||||
- `Statistical Learning`
|
- `Statistical Learning`
|
||||||
|
|
||||||
|
- `M2`
|
||||||
|
- `Machine Learning`
|
||||||
|
- `SQL`
|
||||||
|
|
||||||
## 🛠️ Technologies & Tools
|
## 🛠️ Technologies & Tools
|
||||||
|
|
||||||
- [Python](https://www.python.org): A high-level, interpreted programming language, widely used for data science, machine learning, and scientific computing.
|
- [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.
|
- [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.
|
- [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.
|
- [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.
|
- [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).
|
- [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.
|
- [ggplot2](https://ggplot2.tidyverse.org): A grammar-based graphics package for creating complex and elegant visualizations in R.
|
||||||
|
|||||||
@@ -230,9 +230,6 @@
|
|||||||
},
|
},
|
||||||
"top": "Go to top"
|
"top": "Go to top"
|
||||||
},
|
},
|
||||||
"canva": {
|
|
||||||
"title": "Loading the canva ..."
|
|
||||||
},
|
|
||||||
"writings": {
|
"writings": {
|
||||||
"description": "All my reflections on programming, mathematics, the conception of artificial intelligence, etc., are put in chronological order.",
|
"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.",
|
"title": "Writings on math, artificial intelligence, development, and my passions.",
|
||||||
|
|||||||
@@ -233,9 +233,6 @@
|
|||||||
"top": "Ir arriba"
|
"top": "Ir arriba"
|
||||||
},
|
},
|
||||||
"alert": "Por falta de tiempo, no tuve tiempo para traducir este contenido al francés. Gracias por su comprensión.",
|
"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": {
|
"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.",
|
"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.",
|
"title": "Escritos sobre matemáticas, inteligencia artificial, desarrollo y mis pasiones.",
|
||||||
|
|||||||
@@ -231,9 +231,6 @@
|
|||||||
},
|
},
|
||||||
"top": "Remonter en haut"
|
"top": "Remonter en haut"
|
||||||
},
|
},
|
||||||
"canva": {
|
|
||||||
"title": "Chargement du canva..."
|
|
||||||
},
|
|
||||||
"writings": {
|
"writings": {
|
||||||
"description": "Toutes mes réflexions sur la programmation, les mathématiques, la conception de l'intelligence artificielle, etc., sont mises en ordre chronologique.",
|
"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.",
|
"title": "Écrits sur les maths, l'intelligence artificielle, le développement et mes passions.",
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { definePerson } from 'nuxt-schema-org/schema'
|
||||||
|
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
compatibilityDate: '2025-07-20',
|
compatibilityDate: '2025-07-20',
|
||||||
|
|
||||||
@@ -17,22 +19,45 @@ export default defineNuxtConfig({
|
|||||||
css: ['~/assets/css/main.css'],
|
css: ['~/assets/css/main.css'],
|
||||||
|
|
||||||
// Nuxt Modules
|
// Nuxt Modules
|
||||||
modules: [
|
modules: ['@nuxt/ui', '@nuxtjs/seo', '@nuxt/content', '@vueuse/nuxt', '@nuxtjs/google-fonts', '@nuxt/image', '@vueuse/motion/nuxt', '@pinia/nuxt', '@nuxtjs/i18n', 'nuxt-studio'],
|
||||||
'@nuxt/ui',
|
|
||||||
'@nuxt/content',
|
ogImage: {
|
||||||
'@vueuse/nuxt',
|
enabled: false,
|
||||||
'@nuxtjs/google-fonts',
|
},
|
||||||
'@nuxt/image',
|
linkChecker: {
|
||||||
'@vueuse/motion/nuxt',
|
enabled: false,
|
||||||
'@pinia/nuxt',
|
},
|
||||||
'@nuxtjs/i18n',
|
|
||||||
],
|
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
|
// Nuxt Content
|
||||||
content: {
|
content: {
|
||||||
preview: {
|
|
||||||
api: 'https://api.nuxt.studio',
|
|
||||||
},
|
|
||||||
build: {
|
build: {
|
||||||
markdown: {
|
markdown: {
|
||||||
highlight: {
|
highlight: {
|
||||||
@@ -51,6 +76,12 @@ export default defineNuxtConfig({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
vite: {
|
||||||
|
build: {
|
||||||
|
sourcemap: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// Nuxt Color Mode
|
// Nuxt Color Mode
|
||||||
colorMode: {
|
colorMode: {
|
||||||
preference: 'system',
|
preference: 'system',
|
||||||
@@ -63,6 +94,17 @@ export default defineNuxtConfig({
|
|||||||
timeline: { enabled: true },
|
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
|
// Nuxt I18N
|
||||||
i18n: {
|
i18n: {
|
||||||
strategy: 'no_prefix',
|
strategy: 'no_prefix',
|
||||||
@@ -108,9 +150,17 @@ export default defineNuxtConfig({
|
|||||||
// Nitro
|
// Nitro
|
||||||
nitro: {
|
nitro: {
|
||||||
experimental: {
|
experimental: {
|
||||||
websocket: true,
|
|
||||||
openAPI: true,
|
openAPI: true,
|
||||||
},
|
},
|
||||||
|
preset: 'cloudflare-module',
|
||||||
|
cloudflare: {
|
||||||
|
deployConfig: true,
|
||||||
|
nodeCompat: true,
|
||||||
|
},
|
||||||
|
prerender: {
|
||||||
|
routes: ['/'],
|
||||||
|
crawlLinks: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// Nuxt Env
|
// Nuxt Env
|
||||||
|
|||||||
29
package.json
29
package.json
@@ -13,36 +13,43 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@antfu/eslint-config": "^5.4.1",
|
"@antfu/eslint-config": "^5.4.1",
|
||||||
"@iconify-json/devicon": "^1.2.45",
|
"@iconify-json/devicon": "^1.2.46",
|
||||||
"@iconify-json/logos": "^1.2.9",
|
"@iconify-json/logos": "^1.2.10",
|
||||||
"@iconify-json/ph": "^1.2.2",
|
"@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/twemoji": "^1.2.4",
|
||||||
"@iconify-json/vscode-icons": "^1.2.30",
|
"@iconify-json/vscode-icons": "^1.2.33",
|
||||||
"@nuxt/content": "3.7.1",
|
"@nuxt/content": "3.7.1",
|
||||||
"@nuxt/eslint": "1.9.0",
|
"@nuxt/eslint": "1.9.0",
|
||||||
"@nuxt/image": "^1.11.0",
|
"@nuxt/image": "^1.11.0",
|
||||||
"@nuxt/ui": "^4.0.0",
|
"@nuxt/ui": "4.0.1",
|
||||||
"@nuxtjs/google-fonts": "^3.2.0",
|
"@nuxtjs/google-fonts": "^3.2.0",
|
||||||
"@nuxtjs/i18n": "10.1.0",
|
"@nuxtjs/i18n": "10.1.0",
|
||||||
"@pinia/nuxt": "^0.11.2",
|
"@nuxtjs/seo": "^3.2.2",
|
||||||
|
"@pinia/nuxt": "^0.11.3",
|
||||||
"@tailwindcss/typography": "^0.5.19",
|
"@tailwindcss/typography": "^0.5.19",
|
||||||
"@vueuse/math": "13.9.0",
|
"@vueuse/math": "13.9.0",
|
||||||
"@vueuse/motion": "^3.0.3",
|
"@vueuse/motion": "^3.0.3",
|
||||||
"better-sqlite3": "^12.4.1",
|
"better-sqlite3": "^12.4.1",
|
||||||
"eslint": "9.36.0",
|
"eslint": "9.36.0",
|
||||||
"nuxt": "4.1.2",
|
"nuxt": "4.1.2",
|
||||||
|
"nuxt-studio": "1.0.0-alpha.1",
|
||||||
"rehype-katex": "^7.0.1",
|
"rehype-katex": "^7.0.1",
|
||||||
"remark-math": "^6.0.0",
|
"remark-math": "^6.0.0",
|
||||||
"remark-parse": "^11.0.0",
|
"remark-parse": "^11.0.0",
|
||||||
"remark-rehype": "^11.1.2",
|
"remark-rehype": "^11.1.2",
|
||||||
"typescript": "^5.9.2",
|
"typescript": "5.9.3",
|
||||||
"vue": "^3.5.22",
|
"vue": "^3.5.22",
|
||||||
"vue-router": "^4.5.1"
|
"vue-router": "^4.6.3",
|
||||||
|
"wrangler": "^4.45.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^24.6.0",
|
"@types/node": "24.6.2",
|
||||||
"@vueuse/nuxt": "^13.9.0",
|
"@vueuse/nuxt": "^13.9.0",
|
||||||
"vue-tsc": "^3.1.0"
|
"vue-tsc": "^3.1.3"
|
||||||
}
|
},
|
||||||
|
"trustedDependencies": [
|
||||||
|
"@parcel/watcher",
|
||||||
|
"unrs-resolver"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
|
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.
Reference in New Issue
Block a user