forked from Mike/freno-dev
nook: fix the landing title, isolate /blog, grow the sitemap
The landing page spent its strongest on-page signal on the word "Home"; the title now carries the positioning line and the OG title carries the full brand phrase. /blog on a product subdomain served the personal blog in dev (vinxi ignores the vercel.json rewrites that protect production), splitting the brand and crawl equity — subdomain hits now render the shared 404. /agents, /changelog and /faq join the sitemap registry so they are indexed as they land. Tests: bun test (539 pass); vinxi build clean, no new tsc errors. Issues: FRE-8, FRE-15, FRE-9
This commit is contained in:
@@ -83,6 +83,9 @@ export const SITEMAP_ROUTES: Record<SiteId, SitemapEntry[]> = {
|
|||||||
nook: [
|
nook: [
|
||||||
{ path: "/", changefreq: "weekly", priority: 1.0 },
|
{ path: "/", changefreq: "weekly", priority: 1.0 },
|
||||||
{ path: "/checkout", changefreq: "monthly", priority: 0.5 },
|
{ path: "/checkout", changefreq: "monthly", priority: 0.5 },
|
||||||
{ path: "/privacy", changefreq: "yearly", priority: 0.4 }
|
{ path: "/privacy", changefreq: "yearly", priority: 0.4 },
|
||||||
|
{ path: "/agents", changefreq: "weekly", priority: 0.9 },
|
||||||
|
{ path: "/changelog", changefreq: "daily", priority: 0.7 },
|
||||||
|
{ path: "/faq", changefreq: "monthly", priority: 0.8 }
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import { api } from "~/lib/api";
|
|||||||
import CustomScrollbar from "~/components/CustomScrollbar";
|
import CustomScrollbar from "~/components/CustomScrollbar";
|
||||||
import "../post.css";
|
import "../post.css";
|
||||||
import { Post } from "~/db/types";
|
import { Post } from "~/db/types";
|
||||||
|
import { useSite } from "~/context/SiteContext";
|
||||||
|
import NotFound from "../../[...404]";
|
||||||
|
|
||||||
const getPostByTitle = query(
|
const getPostByTitle = query(
|
||||||
async (
|
async (
|
||||||
@@ -262,6 +264,7 @@ const getPostByTitle = query(
|
|||||||
);
|
);
|
||||||
|
|
||||||
export default function PostPage() {
|
export default function PostPage() {
|
||||||
|
const site = useSite();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
|
|
||||||
@@ -297,6 +300,12 @@ export default function PostPage() {
|
|||||||
return str.includes('data-type="mermaid"');
|
return str.includes('data-type="mermaid"');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Host-dispatch guard: blog posts live on the apex domain only (see the
|
||||||
|
// guard in blog/index.tsx for the dev-server rationale).
|
||||||
|
if (site().id !== "main") {
|
||||||
|
return <NotFound />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Show
|
<Show
|
||||||
when={data()}
|
when={data()}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import PostSorting from "~/components/blog/PostSorting";
|
|||||||
import PublishStatusToggle from "~/components/blog/PublishStatusToggle";
|
import PublishStatusToggle from "~/components/blog/PublishStatusToggle";
|
||||||
import { TerminalSplash } from "~/components/TerminalSplash";
|
import { TerminalSplash } from "~/components/TerminalSplash";
|
||||||
import { CACHE_CONFIG } from "~/config";
|
import { CACHE_CONFIG } from "~/config";
|
||||||
|
import { useSite } from "~/context/SiteContext";
|
||||||
|
import NotFound from "../[...404]";
|
||||||
|
|
||||||
const getPosts = query(async () => {
|
const getPosts = query(async () => {
|
||||||
"use server";
|
"use server";
|
||||||
@@ -77,6 +79,7 @@ const getPosts = query(async () => {
|
|||||||
}, "posts");
|
}, "posts");
|
||||||
|
|
||||||
export default function BlogIndex() {
|
export default function BlogIndex() {
|
||||||
|
const site = useSite();
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
|
|
||||||
const sort = () => searchParams.sort || "newest";
|
const sort = () => searchParams.sort || "newest";
|
||||||
@@ -89,6 +92,15 @@ export default function BlogIndex() {
|
|||||||
|
|
||||||
const data = createAsync(() => getPosts(), { deferStream: true });
|
const data = createAsync(() => getPosts(), { deferStream: true });
|
||||||
|
|
||||||
|
// Host-dispatch guard: /blog is the personal blog on the apex domain only.
|
||||||
|
// Production rewrites already send `<sub>.freno.me/blog` → `/<sub>/blog`,
|
||||||
|
// which has no route — but in dev the vinxi server ignores vercel.json and
|
||||||
|
// would render the personal blog on a product subdomain, splitting the
|
||||||
|
// brand and crawl equity. Render the shared 404 instead.
|
||||||
|
if (site().id !== "main") {
|
||||||
|
return <NotFound />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<EdgeCacheHeaders maxAge={60} staleSeconds={3600} />
|
<EdgeCacheHeaders maxAge={60} staleSeconds={3600} />
|
||||||
|
|||||||
@@ -55,10 +55,10 @@ export default function NookLanding() {
|
|||||||
<>
|
<>
|
||||||
<EdgeCacheHeaders maxAge={300} />
|
<EdgeCacheHeaders maxAge={300} />
|
||||||
<PageHead
|
<PageHead
|
||||||
title="Home"
|
title="Agent control for your Mac"
|
||||||
|
ogTitle="The Nook — agent control for your Mac"
|
||||||
description="The Nook — a native macOS utility for coding-agent orchestration, fan and thermal control."
|
description="The Nook — a native macOS utility for coding-agent orchestration, fan and thermal control."
|
||||||
ogImage={`https://${site().domain}/nook/og.png`}
|
ogImage={`https://${site().domain}/nook/og.png`}
|
||||||
ogTitle="The Nook — native macOS agent orchestration"
|
|
||||||
ogDescription="A one-time-purchase macOS app for coding-agent orchestration, fan control, and thermal insight."
|
ogDescription="A one-time-purchase macOS app for coding-agent orchestration, fan control, and thermal insight."
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user