diff --git a/web/src/layouts/Base.astro b/web/src/layouts/Base.astro
index f58357c4..c00f21cb 100644
--- a/web/src/layouts/Base.astro
+++ b/web/src/layouts/Base.astro
@@ -5,13 +5,16 @@ import '@fontsource/noto-naskh-arabic/700.css';
import '@fontsource/noto-nastaliq-urdu/400.css';
import '@fontsource/noto-nastaliq-urdu/700.css';
import '../styles/global.css';
-interface Props { title?: string; description?: string; q?: string; author?: string; jsonld?: object }
+interface Props { title?: string; description?: string; q?: string; author?: string; jsonld?: object; ogType?: string }
// author and jsonld describe a work for search engines and the browser's own reader view
-const { title, description = 'اردو کا کلاسیکی ادب', q = '', author, jsonld } = Astro.props;
+const { title, description = 'اردو کا کلاسیکی ادب', q = '', author, jsonld, ogType = 'website' } = Astro.props;
+// one address per page for search engines: the public origin, no query (?hl=, ?font=, ?kind= are views of the same page)
+const canonical = siteUrl(Astro.url) + Astro.url.pathname;
const fullTitle = title ? `${title} · ${brand.name}` : `${brand.name} · ${brand.tagline}`;
import Icon from '../components/Icon.astro';
import logo from '../../public/logo/turanj.svg?raw'; // inline, so the page can turn its rays
import { brand } from '../lib/brand';
+import { siteUrl } from '../lib/site-url';
---
@@ -20,6 +23,7 @@ import { brand } from '../lib/brand';
{fullTitle}
+
@@ -29,11 +33,11 @@ import { brand } from '../lib/brand';
-
+
-
+
diff --git a/web/src/pages/[...path].astro b/web/src/pages/[...path].astro
index 23a40834..19a3c05a 100644
--- a/web/src/pages/[...path].astro
+++ b/web/src/pages/[...path].astro
@@ -88,10 +88,21 @@ const ebookCount = page.type === 'poet' ? ((await api(`/api/ebooks?poet=$
const tagTarget = page.type === 'poem' ? `work/${poemId}` : `category/${page.category?.id}`;
const bookmark = (c: number) => JSON.stringify({ kind: 'couplet', poemId, couplet: c });
const title = page.type === 'poem' ? page.poem.title : page.type === 'poet' ? page.poet.nickname : page.category.title;
+// for search engines and link previews: a work's opening lines, a poet's intro, a book's contents (#83)
+const clip = (t: string) => (t.length > 160 ? t.slice(0, 159) + '…' : t);
+const description = clip(page.type === 'poem' ? `${blocks[0]?.lines.join(' / ') ?? page.poem.title} (${page.poet.nickname})`
+ : page.type === 'poet' ? page.poet.description || `${page.poet.nickname}: کلام، کتابیں اور ای بکس`
+ : `${page.category.title} · ${page.poet.nickname}: ${page.poems?.length ? `${ud(page.poems.length)} تخلیقات` : `${ud(page.children?.length ?? 0)} حصے`}`);
+const here = siteUrl(Astro.url) + url, poetLd = { '@type': 'Person', name: page.poet?.nickname, url: siteUrl(Astro.url) + (crumbs[0]?.url ?? '') };
+const jsonld = { '@context': 'https://schema.org', inLanguage: 'ur', url: here, ...(
+ page.type === 'poem' ? { '@type': 'CreativeWork', name: page.poem.title, author: poetLd, isPartOf: crumbs.at(-1)?.title }
+ : page.type === 'poet' ? { ...poetLd, url: here, description: page.poet.description || undefined }
+ : { '@type': 'CollectionPage', name: page.category.title, author: poetLd }) };
+import { siteUrl } from '../lib/site-url';
import Icon from '../components/Icon.astro';
import TagsBox from '../components/TagsBox.astro';
---
-
+
{page.type !== 'poet' && (