Get rid of explicit excerpt on Post
This commit is contained in:
parent
e029c6b61d
commit
4a82426dd5
15 changed files with 85 additions and 83 deletions
|
|
@ -0,0 +1,2 @@
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Post" DROP COLUMN "excerpt";
|
||||||
|
|
@ -46,7 +46,6 @@ model Post {
|
||||||
postType String @db.VarChar(50) // blog, microblog, link, photo, album
|
postType String @db.VarChar(50) // blog, microblog, link, photo, album
|
||||||
title String? @db.VarChar(255) // Optional for microblog posts
|
title String? @db.VarChar(255) // Optional for microblog posts
|
||||||
content Json? // BlockNote JSON for blog/microblog
|
content Json? // BlockNote JSON for blog/microblog
|
||||||
excerpt String? @db.Text
|
|
||||||
|
|
||||||
// Type-specific fields
|
// Type-specific fields
|
||||||
linkUrl String? @db.VarChar(500)
|
linkUrl String? @db.VarChar(500)
|
||||||
|
|
|
||||||
|
|
@ -75,10 +75,6 @@
|
||||||
<div class="post-body">
|
<div class="post-body">
|
||||||
{@html renderedContent}
|
{@html renderedContent}
|
||||||
</div>
|
</div>
|
||||||
{:else if post.excerpt}
|
|
||||||
<div class="post-body">
|
|
||||||
<p>{post.excerpt}</p>
|
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<footer class="post-footer">
|
<footer class="post-footer">
|
||||||
|
|
|
||||||
|
|
@ -194,7 +194,7 @@
|
||||||
// Second item is Photos (index 2) - animation handled by individual rect animations
|
// Second item is Photos (index 2) - animation handled by individual rect animations
|
||||||
|
|
||||||
// Third item is Labs (index 3)
|
// Third item is Labs (index 3)
|
||||||
.nav-item:nth-of-type(3) :global(svg.animate) {
|
.nav-item:nth-of-type(2) :global(svg.animate) {
|
||||||
animation: tubeRotate 0.6s ease;
|
animation: tubeRotate 0.6s ease;
|
||||||
transform-origin: center bottom;
|
transform-origin: center bottom;
|
||||||
}
|
}
|
||||||
|
|
@ -205,19 +205,19 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Specific animation for photo masonry rectangles
|
// Specific animation for photo masonry rectangles
|
||||||
.nav-item:nth-of-type(2) :global(svg.animate rect:nth-child(1)) {
|
.nav-item:nth-of-type(3) :global(svg.animate rect:nth-child(1)) {
|
||||||
animation: masonryRect1 0.6s ease;
|
animation: masonryRect1 0.6s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item:nth-of-type(2) :global(svg.animate rect:nth-child(2)) {
|
.nav-item:nth-of-type(3) :global(svg.animate rect:nth-child(2)) {
|
||||||
animation: masonryRect2 0.6s ease;
|
animation: masonryRect2 0.6s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item:nth-of-type(2) :global(svg.animate rect:nth-child(3)) {
|
.nav-item:nth-of-type(3) :global(svg.animate rect:nth-child(3)) {
|
||||||
animation: masonryRect3 0.6s ease;
|
animation: masonryRect3 0.6s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item:nth-of-type(2) :global(svg.animate rect:nth-child(4)) {
|
.nav-item:nth-of-type(3) :global(svg.animate rect:nth-child(4)) {
|
||||||
animation: masonryRect4 0.6s ease;
|
animation: masonryRect4 0.6s ease;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Snippet } from 'svelte'
|
import type { Snippet } from 'svelte'
|
||||||
import UniverseIcon from '$icons/universe.svg'
|
import UniverseIcon from '$icons/universe.svg'
|
||||||
|
import PhotosIcon from '$icons/photos.svg'
|
||||||
import { formatDate } from '$lib/utils/date'
|
import { formatDate } from '$lib/utils/date'
|
||||||
import { goto } from '$app/navigation'
|
import { goto } from '$app/navigation'
|
||||||
|
|
||||||
|
|
@ -55,12 +56,18 @@
|
||||||
{formatDate(item.publishedAt)}
|
{formatDate(item.publishedAt)}
|
||||||
</time>
|
</time>
|
||||||
</a>
|
</a>
|
||||||
<UniverseIcon class="universe-icon" />
|
{#if type === 'album'}
|
||||||
|
<PhotosIcon class="card-icon" />
|
||||||
|
{:else}
|
||||||
|
<UniverseIcon class="card-icon" />
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
@import '../../assets/styles/animations';
|
||||||
|
|
||||||
.universe-card {
|
.universe-card {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 700px;
|
max-width: 700px;
|
||||||
|
|
@ -107,7 +114,7 @@
|
||||||
transition: color 0.2s ease;
|
transition: color 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.universe-icon) {
|
:global(.card-icon) {
|
||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
fill: $grey-40;
|
fill: $grey-40;
|
||||||
|
|
@ -120,7 +127,7 @@
|
||||||
color: $red-60;
|
color: $red-60;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.universe-icon) {
|
:global(.card-icon) {
|
||||||
fill: $red-60;
|
fill: $red-60;
|
||||||
transform: rotate(15deg);
|
transform: rotate(15deg);
|
||||||
}
|
}
|
||||||
|
|
@ -143,9 +150,32 @@
|
||||||
color: $red-60;
|
color: $red-60;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.universe-icon) {
|
:global(.card-icon) {
|
||||||
fill: $red-60;
|
fill: $red-60;
|
||||||
transform: rotate(15deg);
|
}
|
||||||
|
|
||||||
|
:global(.card-icon rect:nth-child(1)) {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
height: 6px;
|
||||||
|
y: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.card-icon rect:nth-child(2)) {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
height: 10px;
|
||||||
|
y: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.card-icon rect:nth-child(3)) {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
height: 8px;
|
||||||
|
y: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.card-icon rect:nth-child(4)) {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
height: 4px;
|
||||||
|
y: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.card-title-link) {
|
:global(.card-title-link) {
|
||||||
|
|
@ -153,6 +183,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Base state for smooth transition back
|
||||||
|
:global(.card-icon rect) {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
:global(.card-title-link) {
|
:global(.card-title-link) {
|
||||||
color: $grey-10;
|
color: $grey-10;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,16 @@
|
||||||
import type { UniverseItem } from '../../routes/api/universe/+server'
|
import type { UniverseItem } from '../../routes/api/universe/+server'
|
||||||
|
|
||||||
let { post }: { post: UniverseItem } = $props()
|
let { post }: { post: UniverseItem } = $props()
|
||||||
|
|
||||||
|
// Check if content is truncated
|
||||||
|
const isContentTruncated = $derived(() => {
|
||||||
|
if (post.content) {
|
||||||
|
// Check if the excerpt is shorter than the full content
|
||||||
|
const excerpt = getContentExcerpt(post.content)
|
||||||
|
return excerpt.endsWith('...')
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<UniverseCard item={post} type="post">
|
<UniverseCard item={post} type="post">
|
||||||
|
|
@ -25,15 +35,13 @@
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="post-excerpt">
|
{#if post.content}
|
||||||
{#if post.excerpt}
|
<div class="post-excerpt">
|
||||||
<p>{post.excerpt}</p>
|
<p>{getContentExcerpt(post.content, 150)}</p>
|
||||||
{:else if post.content}
|
</div>
|
||||||
<p>{getContentExcerpt(post.content)}</p>
|
{/if}
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if post.postType === 'essay'}
|
{#if post.postType === 'essay' && isContentTruncated}
|
||||||
<p>
|
<p>
|
||||||
<a href="/universe/{post.slug}" class="read-more" onclick={(e) => e.preventDefault()} tabindex="-1">Continue reading</a>
|
<a href="/universe/{post.slug}" class="read-more" onclick={(e) => e.preventDefault()} tabindex="-1">Continue reading</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -97,7 +105,7 @@
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
-webkit-line-clamp: 4;
|
-webkit-line-clamp: 2;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@
|
||||||
initialData?: {
|
initialData?: {
|
||||||
title: string
|
title: string
|
||||||
slug: string
|
slug: string
|
||||||
excerpt: string
|
|
||||||
content: JSONContent
|
content: JSONContent
|
||||||
tags: string[]
|
tags: string[]
|
||||||
status: 'draft' | 'published'
|
status: 'draft' | 'published'
|
||||||
|
|
@ -33,7 +32,6 @@
|
||||||
// Form data
|
// Form data
|
||||||
let title = $state(initialData?.title || '')
|
let title = $state(initialData?.title || '')
|
||||||
let slug = $state(initialData?.slug || '')
|
let slug = $state(initialData?.slug || '')
|
||||||
let excerpt = $state(initialData?.excerpt || '')
|
|
||||||
let content = $state<JSONContent>(initialData?.content || { type: 'doc', content: [] })
|
let content = $state<JSONContent>(initialData?.content || { type: 'doc', content: [] })
|
||||||
let tags = $state<string[]>(initialData?.tags || [])
|
let tags = $state<string[]>(initialData?.tags || [])
|
||||||
let status = $state<'draft' | 'published'>(initialData?.status || 'draft')
|
let status = $state<'draft' | 'published'>(initialData?.status || 'draft')
|
||||||
|
|
@ -103,7 +101,6 @@
|
||||||
postType: 'blog', // 'blog' is the database value for essays
|
postType: 'blog', // 'blog' is the database value for essays
|
||||||
status,
|
status,
|
||||||
content,
|
content,
|
||||||
excerpt,
|
|
||||||
tags
|
tags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -268,15 +265,6 @@
|
||||||
|
|
||||||
<Input label="Slug" bind:value={slug} placeholder="essay-url-slug" />
|
<Input label="Slug" bind:value={slug} placeholder="essay-url-slug" />
|
||||||
|
|
||||||
<Input
|
|
||||||
type="textarea"
|
|
||||||
label="Excerpt"
|
|
||||||
helpText="Brief description shown in post lists"
|
|
||||||
bind:value={excerpt}
|
|
||||||
rows={3}
|
|
||||||
placeholder="A brief summary of your essay..."
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="tags-field">
|
<div class="tags-field">
|
||||||
<label class="input-label">Tags</label>
|
<label class="input-label">Tags</label>
|
||||||
<div class="tag-input-wrapper">
|
<div class="tag-input-wrapper">
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,6 @@
|
||||||
.map((tag) => tag.trim())
|
.map((tag) => tag.trim())
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
: [],
|
: [],
|
||||||
excerpt: generateExcerpt(editorContent)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = mode === 'edit' ? `/api/posts/${postId}` : '/api/posts'
|
const url = mode === 'edit' ? `/api/posts/${postId}` : '/api/posts'
|
||||||
|
|
@ -160,22 +159,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateExcerpt(content: JSONContent): string {
|
|
||||||
// Extract plain text from editor content for excerpt
|
|
||||||
if (!content?.content) return ''
|
|
||||||
|
|
||||||
let text = ''
|
|
||||||
const extractText = (node: any) => {
|
|
||||||
if (node.type === 'text') {
|
|
||||||
text += node.text
|
|
||||||
} else if (node.content) {
|
|
||||||
node.content.forEach(extractText)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
content.content.forEach(extractText)
|
|
||||||
return text.substring(0, 200) + (text.length > 200 ? '...' : '')
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handlePublish() {
|
async function handlePublish() {
|
||||||
status = 'published'
|
status = 'published'
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
post: any
|
post: any
|
||||||
postType: 'post' | 'essay'
|
postType: 'post' | 'essay'
|
||||||
slug: string
|
slug: string
|
||||||
excerpt: string
|
|
||||||
tags: string[]
|
tags: string[]
|
||||||
tagInput: string
|
tagInput: string
|
||||||
triggerElement: HTMLElement
|
triggerElement: HTMLElement
|
||||||
|
|
@ -19,7 +18,6 @@
|
||||||
post,
|
post,
|
||||||
postType,
|
postType,
|
||||||
slug = $bindable(),
|
slug = $bindable(),
|
||||||
excerpt = $bindable(),
|
|
||||||
tags = $bindable(),
|
tags = $bindable(),
|
||||||
tagInput = $bindable(),
|
tagInput = $bindable(),
|
||||||
triggerElement,
|
triggerElement,
|
||||||
|
|
@ -32,8 +30,6 @@
|
||||||
function handleFieldUpdate(key: string, value: any) {
|
function handleFieldUpdate(key: string, value: any) {
|
||||||
if (key === 'slug') {
|
if (key === 'slug') {
|
||||||
slug = value
|
slug = value
|
||||||
} else if (key === 'excerpt') {
|
|
||||||
excerpt = value
|
|
||||||
} else if (key === 'tagInput') {
|
} else if (key === 'tagInput') {
|
||||||
tagInput = value
|
tagInput = value
|
||||||
}
|
}
|
||||||
|
|
@ -48,17 +44,6 @@
|
||||||
label: 'Slug',
|
label: 'Slug',
|
||||||
placeholder: 'post-slug'
|
placeholder: 'post-slug'
|
||||||
},
|
},
|
||||||
...(postType === 'essay'
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
type: 'textarea' as const,
|
|
||||||
key: 'excerpt',
|
|
||||||
label: 'Excerpt',
|
|
||||||
rows: 3,
|
|
||||||
placeholder: 'Brief description...'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
{
|
{
|
||||||
type: 'tags',
|
type: 'tags',
|
||||||
key: 'tags',
|
key: 'tags',
|
||||||
|
|
@ -79,7 +64,6 @@
|
||||||
// Create a reactive data object
|
// Create a reactive data object
|
||||||
let popoverData = $state({
|
let popoverData = $state({
|
||||||
slug,
|
slug,
|
||||||
excerpt,
|
|
||||||
tags,
|
tags,
|
||||||
tagInput,
|
tagInput,
|
||||||
createdAt: post.createdAt,
|
createdAt: post.createdAt,
|
||||||
|
|
@ -91,7 +75,6 @@
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
popoverData = {
|
popoverData = {
|
||||||
slug,
|
slug,
|
||||||
excerpt,
|
|
||||||
tags,
|
tags,
|
||||||
tagInput,
|
tagInput,
|
||||||
createdAt: post.createdAt,
|
createdAt: post.createdAt,
|
||||||
|
|
|
||||||
|
|
@ -73,17 +73,27 @@ export const renderEdraContent = (content: any): string => {
|
||||||
|
|
||||||
// Extract text content from Edra JSON for excerpt
|
// Extract text content from Edra JSON for excerpt
|
||||||
export const getContentExcerpt = (content: any, maxLength = 200): string => {
|
export const getContentExcerpt = (content: any, maxLength = 200): string => {
|
||||||
if (!content || !content.content) return ''
|
if (!content) return ''
|
||||||
|
|
||||||
|
// Handle both { blocks: [...] } and { content: [...] } formats
|
||||||
|
const blocks = content.blocks || content.content || []
|
||||||
|
if (!Array.isArray(blocks)) return ''
|
||||||
|
|
||||||
const extractText = (node: any): string => {
|
const extractText = (node: any): string => {
|
||||||
|
// For block-level content
|
||||||
|
if (node.type && node.content && typeof node.content === 'string') {
|
||||||
|
return node.content
|
||||||
|
}
|
||||||
|
// For inline content with text property
|
||||||
if (node.text) return node.text
|
if (node.text) return node.text
|
||||||
|
// For nested content
|
||||||
if (node.content && Array.isArray(node.content)) {
|
if (node.content && Array.isArray(node.content)) {
|
||||||
return node.content.map(extractText).join(' ')
|
return node.content.map(extractText).join(' ')
|
||||||
}
|
}
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const text = content.content.map(extractText).join(' ').trim()
|
const text = blocks.map(extractText).join(' ').trim()
|
||||||
if (text.length <= maxLength) return text
|
if (text.length <= maxLength) return text
|
||||||
return text.substring(0, maxLength).trim() + '...'
|
return text.substring(0, maxLength).trim() + '...'
|
||||||
}
|
}
|
||||||
|
|
@ -126,7 +126,6 @@ export const POST: RequestHandler = async (event) => {
|
||||||
postType: data.type,
|
postType: data.type,
|
||||||
status: data.status,
|
status: data.status,
|
||||||
content: postContent,
|
content: postContent,
|
||||||
excerpt: data.excerpt,
|
|
||||||
linkUrl: data.link_url,
|
linkUrl: data.link_url,
|
||||||
linkDescription: data.linkDescription,
|
linkDescription: data.linkDescription,
|
||||||
featuredImage: featuredImageId,
|
featuredImage: featuredImageId,
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,6 @@ export const PUT: RequestHandler = async (event) => {
|
||||||
postType: data.type,
|
postType: data.type,
|
||||||
status: data.status,
|
status: data.status,
|
||||||
content: postContent,
|
content: postContent,
|
||||||
excerpt: data.excerpt,
|
|
||||||
linkUrl: data.link_url,
|
linkUrl: data.link_url,
|
||||||
linkDescription: data.linkDescription,
|
linkDescription: data.linkDescription,
|
||||||
featuredImage: featuredImageId,
|
featuredImage: featuredImageId,
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ export interface UniverseItem {
|
||||||
type: 'post' | 'album'
|
type: 'post' | 'album'
|
||||||
slug: string
|
slug: string
|
||||||
title?: string
|
title?: string
|
||||||
excerpt?: string
|
|
||||||
content?: any
|
content?: any
|
||||||
publishedAt: string
|
publishedAt: string
|
||||||
createdAt: string
|
createdAt: string
|
||||||
|
|
@ -47,7 +46,6 @@ export const GET: RequestHandler = async (event) => {
|
||||||
postType: true,
|
postType: true,
|
||||||
title: true,
|
title: true,
|
||||||
content: true,
|
content: true,
|
||||||
excerpt: true,
|
|
||||||
linkUrl: true,
|
linkUrl: true,
|
||||||
linkDescription: true,
|
linkDescription: true,
|
||||||
attachments: true,
|
attachments: true,
|
||||||
|
|
@ -96,7 +94,6 @@ export const GET: RequestHandler = async (event) => {
|
||||||
type: 'post' as const,
|
type: 'post' as const,
|
||||||
slug: post.slug,
|
slug: post.slug,
|
||||||
title: post.title || undefined,
|
title: post.title || undefined,
|
||||||
excerpt: post.excerpt || undefined,
|
|
||||||
content: post.content,
|
content: post.content,
|
||||||
postType: post.postType,
|
postType: post.postType,
|
||||||
linkUrl: post.linkUrl || undefined,
|
linkUrl: post.linkUrl || undefined,
|
||||||
|
|
@ -113,7 +110,6 @@ export const GET: RequestHandler = async (event) => {
|
||||||
slug: album.slug,
|
slug: album.slug,
|
||||||
title: album.title,
|
title: album.title,
|
||||||
description: album.description || undefined,
|
description: album.description || undefined,
|
||||||
excerpt: album.description || undefined,
|
|
||||||
location: album.location || undefined,
|
location: album.location || undefined,
|
||||||
date: album.date?.toISOString(),
|
date: album.date?.toISOString(),
|
||||||
photosCount: album._count.photos,
|
photosCount: album._count.photos,
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ export const GET: RequestHandler = async (event) => {
|
||||||
id: post.id.toString(),
|
id: post.id.toString(),
|
||||||
title:
|
title:
|
||||||
post.title || `${post.postType.charAt(0).toUpperCase() + post.postType.slice(1)} Post`,
|
post.title || `${post.postType.charAt(0).toUpperCase() + post.postType.slice(1)} Post`,
|
||||||
description: post.excerpt || extractTextSummary(post.content) || '',
|
description: extractTextSummary(post.content) || '',
|
||||||
content: convertContentToHTML(post.content),
|
content: convertContentToHTML(post.content),
|
||||||
link: `${event.url.origin}/universe/${post.slug}`,
|
link: `${event.url.origin}/universe/${post.slug}`,
|
||||||
guid: `${event.url.origin}/universe/${post.slug}`,
|
guid: `${event.url.origin}/universe/${post.slug}`,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Page from '$components/Page.svelte'
|
import Page from '$components/Page.svelte'
|
||||||
import DynamicPostContent from '$components/DynamicPostContent.svelte'
|
import DynamicPostContent from '$components/DynamicPostContent.svelte'
|
||||||
|
import { getContentExcerpt } from '$lib/utils/content'
|
||||||
import type { PageData } from './$types'
|
import type { PageData } from './$types'
|
||||||
|
|
||||||
let { data }: { data: PageData } = $props()
|
let { data }: { data: PageData } = $props()
|
||||||
|
|
@ -8,6 +9,9 @@
|
||||||
const post = $derived(data.post)
|
const post = $derived(data.post)
|
||||||
const error = $derived(data.error)
|
const error = $derived(data.error)
|
||||||
const pageTitle = $derived(post?.title || 'Post')
|
const pageTitle = $derived(post?.title || 'Post')
|
||||||
|
const description = $derived(
|
||||||
|
post?.content ? getContentExcerpt(post.content, 160) : `${post?.postType === 'essay' ? 'Essay' : 'Post'} by jedmund`
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
|
|
@ -15,14 +19,14 @@
|
||||||
<title>{pageTitle} - jedmund</title>
|
<title>{pageTitle} - jedmund</title>
|
||||||
<meta
|
<meta
|
||||||
name="description"
|
name="description"
|
||||||
content={post.excerpt || `${post.postType === 'essay' ? 'Essay' : 'Post'} by jedmund`}
|
content={description}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Open Graph meta tags -->
|
<!-- Open Graph meta tags -->
|
||||||
<meta property="og:title" content={pageTitle} />
|
<meta property="og:title" content={pageTitle} />
|
||||||
<meta
|
<meta
|
||||||
property="og:description"
|
property="og:description"
|
||||||
content={post.excerpt || `${post.postType === 'essay' ? 'Essay' : 'Post'} by jedmund`}
|
content={description}
|
||||||
/>
|
/>
|
||||||
<meta property="og:type" content="article" />
|
<meta property="og:type" content="article" />
|
||||||
{#if post.attachments && post.attachments.length > 0}
|
{#if post.attachments && post.attachments.length > 0}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue