Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ jobs:
run: pnpm generate

- name: Deploy via FTP
# Full sync: deletes remote files not present in local build to ensure
# a clean deployment matching the generated output exactly.
uses: SamKirkland/FTP-Deploy-Action@v4.3.6
with:
server: ${{ secrets.HOST }}
Expand Down
9 changes: 1 addition & 8 deletions app/components/landing/LabsTeaser.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,7 @@
type LabsSection = {
title: string
description: string
link: {
label: string
icon?: string
to?: string
color?: 'primary' | 'neutral' | 'success' | 'warning' | 'error' | 'info'
variant?: 'solid' | 'outline' | 'subtle' | 'soft' | 'ghost' | 'link'
target?: '_blank' | '_self'
}
link: ContentButton
}

defineProps<{
Expand Down
9 changes: 1 addition & 8 deletions app/components/landing/SpeakingTeaser.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,7 @@ type SpeakingSection = {
title: string
description: string
note?: string
link: {
label: string
icon?: string
to?: string
color?: 'primary' | 'neutral' | 'success' | 'warning' | 'error' | 'info'
variant?: 'solid' | 'outline' | 'subtle' | 'soft' | 'ghost' | 'link'
target?: '_blank' | '_self'
}
link: ContentButton
}

defineProps<{
Expand Down
20 changes: 20 additions & 0 deletions app/composables/usePageSeo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
type SeoInput = {
title?: string
description?: string
seo?: {
title?: string
description?: string
} | null
}

export function usePageSeo(page: SeoInput) {
const title = page.seo?.title ?? page.title
const description = page.seo?.description ?? page.description

useSeoMeta({
title,
ogTitle: title,
description,
ogDescription: description
})
}
9 changes: 3 additions & 6 deletions app/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,9 @@ if (!page.value) {
})
}

useSeoMeta({
title: page.value?.seo.title || page.value?.title,
ogTitle: page.value?.seo.title || page.value?.title,
description: page.value?.seo.description || page.value?.description,
ogDescription: page.value?.seo.description || page.value?.description
})
usePageSeo(page.value)

defineOgImage()
</script>

<template>
Expand Down
18 changes: 7 additions & 11 deletions app/pages/labs/[slug].vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
<script setup lang="ts">
import { formatLabDate, labStatusIconMap, labStatusMap } from '../../utils/labs'

const route = useRoute()
const slug = Array.isArray(route.params.slug) ? route.params.slug[0] : route.params.slug

const { data: lab } = await useAsyncData(`lab-${slug}`, async () => {
const entries = await queryCollection('labs').all()
const sortedEntries = sortLabs(entries)

return sortedEntries.find(entry => getLabSlug(entry) === slug) ?? null
const { data: lab } = await useAsyncData(`lab-${slug}`, () => {
return queryCollection('labs').where('stem', '=', `labs/${slug}`).first()
})

if (!lab.value) {
Expand All @@ -17,12 +16,9 @@ if (!lab.value) {
})
}

useSeoMeta({
title: lab.value.title,
ogTitle: lab.value.title,
description: lab.value.description,
ogDescription: lab.value.description
})
usePageSeo(lab.value)

defineOgImage()

const formattedDate = computed(() => formatLabDate(lab.value!.date))
const hasImage = computed(() => Boolean(lab.value?.image))
Expand Down
9 changes: 3 additions & 6 deletions app/pages/labs/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,9 @@ if (!page.value) {
})
}

useSeoMeta({
title: page.value?.seo?.title || page.value?.title,
ogTitle: page.value?.seo?.title || page.value?.title,
description: page.value?.seo?.description || page.value?.description,
ogDescription: page.value?.seo?.description || page.value?.description
})
usePageSeo(page.value)

defineOgImage()
</script>

<template>
Expand Down
14 changes: 7 additions & 7 deletions app/pages/speaking/[slug].vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<script setup lang="ts">
import { resolveTalkEntry, type ResolvedTalkResource } from '../../utils/speaking'

const route = useRoute()
const slug = Array.isArray(route.params.slug) ? route.params.slug[0] : route.params.slug

const { data: talk } = await useAsyncData(`talk-${slug}`, async () => {
const entries = await queryCollection('talks').all()
const sortedEntries = sortTalks(entries)
const entry = sortedEntries.find(entry => getTalkSlug(entry) === slug) ?? null
const entry = await queryCollection('talks').where('stem', '=', `speaking/${slug}`).first()

return entry ? resolveTalkEntry(entry) : null
})
Expand All @@ -18,13 +18,13 @@ if (!talk.value) {
})
}

useSeoMeta({
usePageSeo({
title: talk.value.title,
ogTitle: talk.value.title,
description: talk.value.summary || talk.value.description,
ogDescription: talk.value.summary || talk.value.description
description: talk.value.summary ?? talk.value.description
})

defineOgImage()

const organizerSubtitle = computed(() => {
if (!talk.value?.organizerTitle) {
return null
Expand Down
9 changes: 3 additions & 6 deletions app/pages/speaking/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,9 @@ if (!page.value) {
})
}

useSeoMeta({
title: page.value?.seo?.title || page.value?.title,
ogTitle: page.value?.seo?.title || page.value?.title,
description: page.value?.seo?.description || page.value?.description,
ogDescription: page.value?.seo?.description || page.value?.description
})
usePageSeo(page.value)

defineOgImage()
</script>

<template>
Expand Down
8 changes: 8 additions & 0 deletions app/utils/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export type ContentButton = {
label: string
icon?: string
to?: string
color?: 'primary' | 'neutral' | 'success' | 'warning' | 'error' | 'info'
variant?: 'solid' | 'outline' | 'subtle' | 'soft' | 'ghost' | 'link'
target?: '_blank' | '_self'
}
6 changes: 1 addition & 5 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
// @ts-check
import withNuxt from './.nuxt/eslint.config.mjs'

export default withNuxt({
rules: {
'@typescript-eslint/no-explicit-any': 'off'
}
})
export default withNuxt()
3 changes: 1 addition & 2 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ export default defineNuxtConfig({
'@nuxt/image',
'@nuxt/ui',
'@nuxt/content',
'@vueuse/nuxt',
'nuxt-og-image',
'motion-v/nuxt'
],
Expand All @@ -27,7 +26,7 @@ export default defineNuxtConfig({
}
},

compatibilityDate: '2024-11-01',
compatibilityDate: '2025-03-01',

nitro: {
prerender: {
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
"@nuxt/content": "^3.12.0",
"@nuxt/image": "^2.0.0",
"@nuxt/ui": "^4.6.0",
"@vueuse/nuxt": "^14.2.1",
"better-sqlite3": "^12.6.2",
"motion-v": "^2.2.0",
"nuxt": "^4.4.2",
Expand Down
20 changes: 0 additions & 20 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.