Resolve merge conflicts — keep stashed improvements (SEO, sitemap)
All checks were successful
Build and Deploy to Storj / build-and-deploy (push) Successful in 10s

This commit is contained in:
Scott Scoble 2026-05-03 01:14:38 -07:00
parent 5bd78762d7
commit a4c14c95ab
2 changed files with 138 additions and 106 deletions

View file

@ -1,10 +1,10 @@
---
interface SEOProps {
interface Props {
title: string;
description: string;
ogImage?: string;
ogType?: string;
url?: string;
canonicalUrl?: string;
pageType?: 'home' | 'speaking' | 'books' | 'consulting' | 'contact' | 'blog';
}
@ -18,115 +18,150 @@ const {
} = Astro.props;
const siteUrl = 'https://scott.scoble.me';
const currentPath = canonicalUrl || Astro.url.pathname;
const fullTitle = title.includes('Scott Scoble') ? title : `${title} — Scott Scoble`;
const imageUrl = new URL(ogImage, siteUrl).href;
const pageUrl = canonicalUrl ? new URL(canonicalUrl, siteUrl).href : `${siteUrl}${Astro.url.pathname}`;
const pageUrl = new URL(currentPath, siteUrl).href;
// Build JSON-LD based on page type
const person = {
'@type': 'Person',
'@id': `${siteUrl}#person`,
name: 'Scott Scoble',
givenName: 'Scott',
familyName: 'Scoble',
jobTitle: 'Human Cultivation Expert',
url: siteUrl,
email: 'mailto:scott@scoble.me',
sameAs: [
'https://linkedin.com/in/scottscoble',
'https://x.com/scottscoble',
'https://github.com/sscoble',
],
};
// JSON-LD structured data
function getJsonLd(pageType: string): string {
const personLd = {
'@type': 'Person',
'@id': `${siteUrl}#person`,
name: 'Scott Scoble',
givenName: 'Scott',
familyName: 'Scoble',
jobTitle: 'Human Cultivation Expert',
url: siteUrl,
email: 'mailto:scott@scoble.me',
sameAs: [
'https://linkedin.com/in/scottscoble',
'https://x.com/scottscoble',
'https://github.com/sscoble',
],
};
const website = {
'@type': 'WebSite',
'@id': `${siteUrl}#website`,
name: 'Scott Scoble',
description: 'Human Cultivation Expert for the AI Age',
url: siteUrl,
};
switch (pageType) {
case 'home': {
const websiteLd = {
'@type': 'WebSite',
'@id': `${siteUrl}#website`,
name: 'Scott Scoble',
description: 'Human Cultivation Expert for the AI Age',
url: siteUrl,
};
return JSON.stringify([personLd, websiteLd], null, 2);
}
let jsonLd: any;
switch (pageType) {
case 'home':
jsonLd = [person, website];
break;
case 'speaking':
jsonLd = {
...person,
'@id': `${siteUrl}#speaker`,
jobTitle: 'Keynote Speaker',
description: 'Human Cultivation Expert speaking on AI, leadership, and team cultivation',
};
break;
case 'books':
jsonLd = [
{
'@type': 'Book',
name: 'The Soil Beneath the Code',
author: { '@type': 'Person', name: 'Scott Scoble' },
datePublished: '2023',
bookFormat: 'EBook, Hardcover',
},
{
'@type': 'Book',
name: 'A Walk in Mentoring',
author: { '@type': 'Person', name: 'Scott Scoble' },
datePublished: '2024',
bookFormat: 'EBook, Paperback',
},
];
break;
case 'consulting':
jsonLd = {
'@type': 'Service',
name: 'AI Team Consulting',
description: 'Helping leaders build AI systems that cultivate humans instead of strip-mining them',
provider: { '@type': 'Person', name: 'Scott Scoble' },
serviceType: 'Professional Consulting',
areaServed: 'Worldwide',
};
break;
case 'contact':
jsonLd = {
'@type': 'ContactPoint',
email: 'scott@scoble.me',
contactType: 'Customer Service',
availableLanguage: 'English',
};
break;
case 'blog':
default:
jsonLd = {
'@type': 'Blog',
name: 'Scott Scoble Blog',
description: 'Thoughts on AI, leadership, and human cultivation',
url: `${siteUrl}/blog`,
};
break;
case 'speaking':
return JSON.stringify({
'@type': 'Person',
'@id': `${siteUrl}#speaker`,
name: 'Scott Scoble',
jobTitle: 'Keynote Speaker',
description: 'Human Cultivation Expert speaking on AI, leadership, and team cultivation',
url: siteUrl,
sameAs: personLd.sameAs,
}, null, 2);
case 'books':
return JSON.stringify([
{
'@type': 'Book',
name: 'The Soil Beneath the Code',
author: 'Scott Scoble',
datePublished: '2023',
bookFormat: 'EBook, Hardcover',
},
{
'@type': 'Book',
name: 'A Walk in Mentoring',
author: 'Scott Scoble',
datePublished: '2024',
bookFormat: 'EBook, Paperback',
},
{
'@type': 'Book',
name: 'Dark Provenance',
author: 'Scott Scoble',
datePublished: '2026',
bookFormat: 'EBook, Hardcover',
},
], null, 2);
case 'consulting':
return JSON.stringify({
'@type': 'Service',
name: 'AI Team Consulting',
description: 'Helping leaders build AI systems that cultivate humans instead of strip-mining them',
provider: { '@type': 'Person', name: 'Scott Scoble' },
serviceType: 'Professional Consulting',
areaServed: 'Worldwide',
offering: [
{
'@type': 'Offering',
name: 'Keynote Speaking',
description: '45-60 minute keynotes on AI, leadership, and team cultivation',
},
{
'@type': 'Offering',
name: 'Workshops',
description: 'Half-day or full-day workshops on cultivation practices',
},
],
}, null, 2);
case 'contact':
return JSON.stringify({
'@type': 'ContactPoint',
email: 'scott@scoble.me',
contactType: 'Customer Service',
availableLanguage: 'English',
}, null, 2);
default:
return JSON.stringify({
'@type': 'Blog',
name: 'Scott Scoble Blog',
description: 'Thoughts on AI, leadership, and human cultivation',
url: `${siteUrl}/blog`,
}, null, 2);
}
}
const jsonLd = getJsonLd(pageType);
---
<!-- Primary Meta -->
<title>{fullTitle}</title>
<meta name="title" content={fullTitle} />
<meta name="description" content={description} />
<meta name="author" content="Scott Scoble" />
<meta name="theme-color" content="#3d5a3e" />
<!-- Open Graph -->
<meta property="og:title" content={fullTitle} />
<meta property="og:description" content={description} />
<meta property="og:type" content={ogType} />
<meta property="og:url" content={pageUrl} />
<meta property="og:image" content={imageUrl} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:site_name" content="Scott Scoble" />
<meta property="og:locale" content="en_US" />
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={fullTitle} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content={imageUrl} />
<meta name="twitter:site" content="@scottscoble" />
<!-- Canonical -->
<link rel="canonical" href={pageUrl} />
<script type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
<!-- JSON-LD Structured Data -->
<script type="application/ld+json" set:html={jsonLd} />
<!-- PWA / Theme -->
<meta name="theme-color" content="#3d5a3e" />
<link rel="manifest" href="/manifest.json" />

View file

@ -1,28 +1,25 @@
// Static sitemap for scott.scoble.me
// Generated at build time
import { getCollection } from 'astro:content';
const pages = [
{ url: '/', changefreq: 'weekly', priority: '1.0' },
{ url: '/speaking', changefreq: 'weekly', priority: '0.9' },
{ url: '/consulting', changefreq: 'weekly', priority: '0.8' },
{ url: '/books', changefreq: 'monthly', priority: '0.8' },
{ url: '/about', changefreq: 'monthly', priority: '0.7' },
{ url: '/blog', changefreq: 'weekly', priority: '0.7' },
{ url: '/contact', changefreq: 'monthly', priority: '0.6' },
{ url: '/privacy', changefreq: 'yearly', priority: '0.3' },
{ url: '/', lastmod: new Date().toISOString() },
{ url: '/speaking', lastmod: new Date().toISOString() },
{ url: '/consulting', lastmod: new Date().toISOString() },
{ url: '/books', lastmod: new Date().toISOString() },
{ url: '/contact', lastmod: new Date().toISOString() },
{ url: '/privacy', lastmod: new Date().toISOString() },
];
const siteUrl = 'https://scott.scoble.me';
const today = new Date().toISOString().split('T')[0];
// Render the sitemap XML
const xml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${pages.map(page => ` <url>
<loc>${siteUrl}${page.url}</loc>
<lastmod>${today}</lastmod>
<changefreq>${page.changefreq}</changefreq>
<priority>${page.priority}</priority>
</url>`).join('\n')}
${pages.map(page => `
<url>
<loc>${new URL(page.url, 'https://scott.scoble.me').href}</loc>
<lastmod>${typeof page.lastmod === 'string' ? page.lastmod : new Date(page.lastmod).toISOString().split('T')[0]}</lastmod>
<changefreq>weekly</changefreq>
<priority>${page.url === '/' ? '1.0' : '0.8'}</priority>
</url>
`).join('')}
</urlset>`;
export const GET = () => {
@ -32,4 +29,4 @@ export const GET = () => {
'Cache-Control': 'public, max-age=3600',
},
});
};
};