@lettercamp/cli 0.1.13 → 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -21,8 +21,8 @@ import { googleAnalyticsScript } from '../lib/analytics';
|
|
|
21
21
|
import Header from '../components/HeaderSlot.astro';
|
|
22
22
|
import Footer from '../components/FooterSlot.astro';
|
|
23
23
|
|
|
24
|
-
const { site, page, article = false } = Astro.props;
|
|
25
|
-
const meta = pageMetadata(site, page, Astro.url, article, basePath);
|
|
24
|
+
const { site, page, article = false, posts = [] } = Astro.props;
|
|
25
|
+
const meta = pageMetadata(site, page, Astro.url, article, basePath, posts);
|
|
26
26
|
const typography = site.design?.typography ?? {};
|
|
27
27
|
const fonts = fontResources(typography, article ? 700 : 500);
|
|
28
28
|
const fontSize = (value, fallback) => typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : fallback;
|
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
// Generated from lettercamp/lib/page-metadata.ts.
|
|
2
|
-
|
|
2
|
+
const text = (value) => typeof value === "string" ? value.trim() : "";
|
|
3
|
+
function siteCover(site, posts) {
|
|
4
|
+
const timestamp = (value) => {
|
|
5
|
+
if (value && typeof value === "object" && "timestamp" in value && typeof value.timestamp === "number")
|
|
6
|
+
return value.timestamp;
|
|
7
|
+
const time = value ? new Date(String(value)).valueOf() : NaN;
|
|
8
|
+
return Number.isNaN(time) ? -Infinity : time;
|
|
9
|
+
};
|
|
10
|
+
const post = posts.filter(post => post.draft !== true && (text(post.image) || text(post.ogImage)))
|
|
11
|
+
.sort((a, b) => timestamp(b.date) - timestamp(a.date) || (a.href ?? "").localeCompare(b.href ?? ""))[0];
|
|
12
|
+
return post
|
|
13
|
+
? { image: text(post.image) || text(post.ogImage), alt: (text(post.image) ? text(post.imageAlt) : text(post.ogImageAlt)) || text(post.title) }
|
|
14
|
+
: { image: text(site.logo), alt: text(site.name) };
|
|
15
|
+
}
|
|
16
|
+
export function pageMetadata(site, page, url, article = false, basePath = "", posts = []) {
|
|
3
17
|
const seo = site.seo && typeof site.seo === "object" ? site.seo : {};
|
|
4
|
-
const
|
|
18
|
+
const homepage = !article && new URL(url).pathname.replace(/\/+$/, "") === basePath.replace(/\/+$/, "");
|
|
19
|
+
// Homepage overrides belong to the blog root, not posts or category pages.
|
|
20
|
+
if (homepage)
|
|
21
|
+
page = { ...seo, ...page };
|
|
5
22
|
const title = text(page.title) || text(site.name);
|
|
6
23
|
const pageTitle = title === site.name ? title : `${title} — ${text(site.name)}`;
|
|
7
|
-
const homepage = !article && new URL(url).pathname.replace(/\/+$/, "") === basePath.replace(/\/+$/, "");
|
|
8
24
|
const documentTitle = text(page.metaTitle) || (homepage && text(seo.metaTitle)) || pageTitle;
|
|
9
25
|
const description = text(page.metaDescription) || text(page.description) || text(site.description);
|
|
10
26
|
const socialTitle = text(page.ogTitle) || (homepage && (text(seo.ogTitle) || text(seo.metaTitle))) || title;
|
|
@@ -21,8 +37,10 @@ export function pageMetadata(site, page, url, article = false, basePath = "") {
|
|
|
21
37
|
return "";
|
|
22
38
|
}
|
|
23
39
|
};
|
|
24
|
-
const
|
|
25
|
-
const
|
|
40
|
+
const explicitImage = text(page.ogImage) || text(page.image) || text(seo.ogImage);
|
|
41
|
+
const fallback = homepage && !explicitImage ? siteCover(site, posts) : undefined;
|
|
42
|
+
const imageUrl = resolveUrl(explicitImage || fallback?.image);
|
|
43
|
+
const imageAlt = text(page.ogImageAlt) || (fallback ? fallback.alt : page.ogImage || page.image ? text(page.imageAlt) : text(seo.ogImageAlt));
|
|
26
44
|
const twitterImageUrl = resolveUrl(page.twitterImage) || imageUrl;
|
|
27
45
|
const language = text(page.language) || text(site.language) || "en";
|
|
28
46
|
let locale = "";
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
import SiteLayout from '../layouts/SiteLayout.astro';
|
|
3
3
|
import BlogHome from '../components/BlogHome.astro';
|
|
4
4
|
import site from '../site.js';
|
|
5
|
+
import { getPosts } from '../lib/posts.js';
|
|
5
6
|
const title = site.home.title ?? `${site.name} Blog`;
|
|
6
7
|
---
|
|
7
8
|
|
|
8
|
-
<SiteLayout site={site} page={{ title }}>
|
|
9
|
+
<SiteLayout site={site} page={{ title }} posts={getPosts(site.language, site.timeZone)}>
|
|
9
10
|
<BlogHome site={site} />
|
|
10
11
|
</SiteLayout>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
.post-search-trigger { position: relative; flex: 0 1 240px; min-width: 180px; margin-left: auto; }
|
|
2
|
+
.blog-toolbar:not(:has(> .category-selector:not([hidden]))) > .post-search-trigger { margin-left: 0; }
|
|
2
3
|
.post-search-trigger svg { position: absolute; left: 13px; top: 50%; width: 16px; height: 16px; transform: translateY(-50%); color: var(--site-text-muted); pointer-events: none; }
|
|
3
4
|
.post-search-trigger button { width: 100%; height: 36px; padding: 0 14px 0 38px; border: 0; border-radius: 999px; background: var(--site-surface-hover); color: var(--site-text-secondary); font: 400 var(--text-label)/1.5 var(--font-family); text-align: left; cursor: pointer; }
|
|
4
5
|
.post-search-trigger button:hover { background: var(--site-surface-active); color: var(--site-foreground); }
|
|
@@ -73,7 +73,7 @@ html:has(.site-header[data-header-position='fixed']) { scroll-padding-top: 80px;
|
|
|
73
73
|
.mobile-menu-toggle svg { transition: none; }
|
|
74
74
|
}
|
|
75
75
|
.blog-home { padding-block: 72px 100px; }
|
|
76
|
-
.blog-heading { margin-bottom:
|
|
76
|
+
.blog-heading { margin-bottom: 40px; }
|
|
77
77
|
.blog-home:not(:has(.blog-toolbar > :not([hidden], script, dialog))) > .blog-heading { margin-bottom: 64px; }
|
|
78
78
|
.blog-heading h1 { margin-bottom: 0; }
|
|
79
79
|
.article-category { margin: 0 0 20px; }
|
|
@@ -272,7 +272,7 @@ h1 { font-size: var(--text-title); font-weight: 500; line-height: 1.15; letter-s
|
|
|
272
272
|
@media (prefers-reduced-motion: no-preference) { .mobile-navigation[open] .mobile-menu-panel { animation: mobile-menu-enter 180ms ease-out; } }
|
|
273
273
|
.blog-home { padding-block: 46px 64px; }
|
|
274
274
|
.blog-home:not(:has(.blog-toolbar > :not([hidden], script, dialog))) > .blog-heading { margin-bottom: 40px; }
|
|
275
|
-
.blog-toolbar { margin-bottom: 36px; }
|
|
275
|
+
.blog-heading, .blog-toolbar { margin-bottom: 36px; }
|
|
276
276
|
.category-selector { gap: 4px 20px; }
|
|
277
277
|
.post-list { grid-template-columns: 1fr; gap: 48px; }
|
|
278
278
|
.related-posts { margin-top: 56px; padding-top: 32px; }
|