Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support multiple og images #305

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
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
210 changes: 91 additions & 119 deletions client/app.vue

Large diffs are not rendered by default.

10 changes: 4 additions & 6 deletions client/components/ImageLoader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const props = defineProps<{
src: string
aspectRatio: number
maxHeight?: number
minHeight?: number
maxWidth?: number
}>()
// emits a load event
Expand Down Expand Up @@ -52,22 +53,19 @@ onMounted(() => {
</script>

<template>
<div ref="image" :style="{ aspectRatio }">
<div ref="image" :style="{ aspectRatio, minHeight }">
<NLoading v-if="loading" />
</div>
</template>

<style scoped>
div {
cursor: pointer;
max-height: 600px;
height: auto;
width: auto;
height: 100%;
margin: 0 auto;
max-width: 1200px;
width: 100%;
transition: 0.4s ease-in-out;
background-color: white;
background-size: contain;
aspect-ratio: 2 / 1
}
</style>
6 changes: 4 additions & 2 deletions client/components/SlackCardRenderer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
word-wrap: break-word;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
height: 300px;
min-height: 300px;
}
.siteName {
font-weight: bold;
Expand All @@ -51,7 +51,9 @@
word-wrap: break-word;
}
.description {
color: #2c2d30;
color: white;
opacity: 0.7;
margin-bottom: 8px;
}
.title {
color: #0576b9;
Expand Down
4 changes: 3 additions & 1 deletion client/components/TemplateComponentPreview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ const props = defineProps<{
component: OgImageComponent
active: boolean
imageFormat: string
width: string
height: string
}>()

function openComponent() {
Expand Down Expand Up @@ -39,7 +41,7 @@ const loadStats = ref<{ timeTaken: string, sizeKb: string }>()
</div>
<div class="border-2 group-hover:shadow-sm rounded-[0.35rem] border-transparent hover:border-yellow-500 transition-all">
<VTooltip>
<div class="w-[300px] h-[150px] relative">
<div class="h-[150px] relative" :style="{ aspectRatio }">
<NIcon v-if="active" icon="carbon:checkmark-filled" class="absolute top-2 right-2 text-green-500" />
<ImageLoader
v-if="!isHtml"
Expand Down
4 changes: 2 additions & 2 deletions client/components/TwitterCardRenderer.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
<script setup lang="ts">
defineProps<{
aspectRatio: number
title?: string
}>()
</script>

<template>
<div class="root max-h-full relative flex">
<div class="max-h-full border-1 border-solid border-[#cfd9de] rounded-[16px] overflow-hidden">
<div class="image-wrap">
<div class="image-wrap" :style="{ aspectRatio }">
<slot />
</div>
</div>
Expand All @@ -24,7 +25,6 @@ defineProps<{

<style scoped>
.image-wrap {
aspect-ratio: 2 / 1;
background-color: rgba(0, 0, 0, 0.1);
opacity: 1;
height: 300px;
Expand Down
26 changes: 26 additions & 0 deletions client/components/WhatsAppRenderer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<script setup lang="ts">
</script>

<template>
<div style="max-width: 600px; background-color: #00574B; font-size: 14.2px; line-height: 19px; color: white; padding: 6px; border-radius: 7.5px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
<div class="flex items-center" style="background-color: #025144; border-radius: 8px; overflow: hidden;">
<div style="aspect-ratio: 1/1;">
<slot />
</div>
<div style="padding: 6px 10px;">
<div class="font-bold" style="-webkit-line-clamp: 2; color: rgba(233, 237, 239, 0.88)">
<slot name="title" />
</div>
<div style="font-size: .75rem; -webkit-line-clamp: 2; color: rgba(233, 237, 239, 0.88)">
<slot name="description" />
</div>
<div style="font-size: .75rem; color: rgba(233, 237, 239, 0.3)">
<slot name="url" />
</div>
</div>
</div>
<div style="padding: 6px 10px;">
When someone sends quite a long message with a link that has an og:image, the og:image is made into a square. For example <span style="color: #53bdeb"><slot name="url" /></span>
</div>
</div>
</template>
44 changes: 11 additions & 33 deletions client/composables/fetch.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,21 @@
import type { V as VueHeadClient } from '@unhead/vue/dist/shared/vue.71760da0'
import type { OgImageComponent, OgImageOptions, OgImageRuntimeConfig } from '../../src/runtime/types'
import { appFetch, devtoolsClient, useAsyncData } from '#imports'
import type {
DevToolsMetaDataExtraction,
OgImageComponent,
OgImageRuntimeConfig,
} from '../../src/runtime/types'
import { appFetch, useAsyncData } from '#imports'
import { joinURL } from 'ufo'
import { globalRefreshTime, optionsOverrides, path, refreshTime } from '~/util/logic'
import { globalRefreshTime, ogImageKey, optionsOverrides, path, refreshTime } from '~/util/logic'

export function fetchPathDebug() {
return useAsyncData<{ siteConfig: { url?: string }, options: OgImageOptions, vnodes: Record<string, any> }>(async () => {
return useAsyncData<DevToolsMetaDataExtraction[]>(async () => {
if (!appFetch.value)
return { siteCofig: {}, options: {}, vnodes: {} }

const clientHead = devtoolsClient.value?.host.nuxt.vueApp.config?.globalProperties?.$head as VueHeadClient
const tags = await clientHead?.resolveTags() || []
const ogImageSrc = tags.find(d => d._d === 'meta:property:og:image')?.props.content
if (ogImageSrc && !ogImageSrc.startsWith('/__og-image__/image')) {
// generate the social
return {
siteConfig: {},
options: {
url: ogImageSrc,
socialPreview: {
og: {
title: tags.find(d => d._d === 'meta:property:og:title')?.props.content,
description: tags.find(d => d._d === 'meta:property:og:description')?.props.content,
},
twitter: {
title: tags.find(d => d._d === 'meta:name:twitter:title')?.props.content,
description: tags.find(d => d._d === 'meta:name:twitter:description')?.props.content,
},
},
},
vnodes: {},
custom: true,
}
}
return appFetch.value(joinURL('/__og-image__/image', path.value, 'og.json'), {
return []
return appFetch.value(joinURL('/__og-image__/image', path.value, `${ogImageKey.value || 'og'}.json`), {
query: optionsOverrides.value,
})
}, {
watch: [path, refreshTime],
watch: [path, refreshTime, ogImageKey],
})
}

Expand Down
1 change: 1 addition & 0 deletions client/util/logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const globalRefreshTime = ref(Date.now())

export const description = ref<string | null>(null)
export const hostname = window.location.host
export const ogImageKey = ref()
export const path = ref('/')
export const query = ref()
export const base = ref('/')
Expand Down
1 change: 1 addition & 0 deletions playground/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export default defineNuxtConfig({
plugins: ['plugins/hooks.ts'],
prerender: {
routes: [
'/multiple',
'/chromium/component',
'/chromium/delayed',
'/chromium/screenshot',
Expand Down
6 changes: 6 additions & 0 deletions playground/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ definePageMeta({

useServerSeoMeta({
title: 'Welcome to the playground',
description: 'Test description',
ogTitle: 'Welcome to the playground',
})

Expand Down Expand Up @@ -47,6 +48,11 @@ defineOgImageComponent('NuxtSeo', {
Satori image
</NuxtLink>
</li>
<li>
<NuxtLink to="/multiple">
Satori Multiple
</NuxtLink>
</li>
<li>
<NuxtLink to="/satori/mounted">
Satori onMounted
Expand Down
67 changes: 67 additions & 0 deletions playground/pages/multiple.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<script setup>
import { defineOgImageComponent, definePageMeta, useServerSeoMeta } from '#imports'

definePageMeta({
title: 'Home',
description: 'This is the home page',
breadcrumbTitle: 'Home',
})

useServerSeoMeta({
description: 'page description',
title: 'Welcome to the playground',
ogDescription: 'This is an og:image description for the multiple page for whatsapp preview.',
ogTitle: 'Welcome to the playground',
})

defineOgImageComponent('NuxtSeo', {
theme: '#fcfcfc',
colorMode: 'dark',
siteLogo: '/my-img.png',
})

defineOgImageComponent('NuxtSeo', {
title: 'Square Image',
theme: '#fcfcfc',
colorMode: 'dark',
siteLogo: '/my-img.png',
}, {
key: 2,
width: 450,
height: 450,
})
</script>

<template>
<div class="px-7 my-5">
<div>
<ul>
<li>
<NuxtLink href="/chromium/screenshot">
Chromium screenshot
</NuxtLink>
</li>
<li>
<NuxtLink href="/chromium/component">
Chromium component
</NuxtLink>
</li>
</ul>
<ul>
<li>
<NuxtLink href="/satori/">
Satori index
</NuxtLink>
</li>
<li>
<a target="_blank" href="/_og/image/satori/og.png">Satori png</a>
</li>
<li>
<NuxtLink to="/satori/image">
Satori image
</NuxtLink>
</li>
</ul>
</div>
</div>
</template>
11 changes: 9 additions & 2 deletions playground/pages/prebuilt.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
<script lang="ts" setup>
import { defineOgImage } from '#imports'
import { defineOgImage, useSeoMeta } from '#imports'

defineOgImage({
url: 'https://www.nitro-ui.com/og.png',
url: 'https://nitro.build/_og/_index.png?name=Nitro&title=Next+Generation+Server+Toolkit&description=Create+web+servers+with+everything+you+need+and+deploy+them+wherever+you+prefer.',
width: 1200,
height: 630,
alt: 'Nitro UI',
})

useSeoMeta({
title: 'Nitro UI',
ogTitle: 'Nitro UI',
description: 'Next Generation Server Toolkit',
ogDescription: 'Create web servers with everything you need and deploy them wherever you prefer.',
})
</script>

<template>
Expand Down
Loading
Loading