From dc959d971bda04d960432f2c51f7aebe0e18eec1 Mon Sep 17 00:00:00 2001 From: Michael Freno Date: Sun, 4 Oct 2026 23:22:04 -0400 Subject: [PATCH] nook: fix the landing title, isolate /blog, grow the sitemap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/lib/sitemap-routes.ts | 5 ++++- src/routes/blog/[title]/index.tsx | 9 +++++++++ src/routes/blog/index.tsx | 12 ++++++++++++ src/routes/nook/index.tsx | 4 ++-- 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/lib/sitemap-routes.ts b/src/lib/sitemap-routes.ts index 16d9cbb..0b15819 100644 --- a/src/lib/sitemap-routes.ts +++ b/src/lib/sitemap-routes.ts @@ -83,6 +83,9 @@ export const SITEMAP_ROUTES: Record = { nook: [ { path: "/", changefreq: "weekly", priority: 1.0 }, { 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 } ] }; diff --git a/src/routes/blog/[title]/index.tsx b/src/routes/blog/[title]/index.tsx index eac56fe..8bf9de2 100644 --- a/src/routes/blog/[title]/index.tsx +++ b/src/routes/blog/[title]/index.tsx @@ -21,6 +21,8 @@ import { api } from "~/lib/api"; import CustomScrollbar from "~/components/CustomScrollbar"; import "../post.css"; import { Post } from "~/db/types"; +import { useSite } from "~/context/SiteContext"; +import NotFound from "../../[...404]"; const getPostByTitle = query( async ( @@ -262,6 +264,7 @@ const getPostByTitle = query( ); export default function PostPage() { + const site = useSite(); const params = useParams(); const [searchParams] = useSearchParams(); @@ -297,6 +300,12 @@ export default function PostPage() { 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 ; + } + return ( { "use server"; @@ -77,6 +79,7 @@ const getPosts = query(async () => { }, "posts"); export default function BlogIndex() { + const site = useSite(); const [searchParams] = useSearchParams(); const sort = () => searchParams.sort || "newest"; @@ -89,6 +92,15 @@ export default function BlogIndex() { const data = createAsync(() => getPosts(), { deferStream: true }); + // Host-dispatch guard: /blog is the personal blog on the apex domain only. + // Production rewrites already send `.freno.me/blog` → `//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 ; + } + return ( <> diff --git a/src/routes/nook/index.tsx b/src/routes/nook/index.tsx index 87c4a50..fcd624e 100644 --- a/src/routes/nook/index.tsx +++ b/src/routes/nook/index.tsx @@ -55,10 +55,10 @@ export default function NookLanding() { <>