diff --git a/web/src/app.d.ts b/web/src/app.d.ts
index a708f0d8a..fbdefa38d 100644
--- a/web/src/app.d.ts
+++ b/web/src/app.d.ts
@@ -1,6 +1,15 @@
declare global {
namespace App {
interface PageData {
+ // per-page og overrides; the repo layout merges these over its
+ // repo-level defaults and renders the one
+ meta?: {
+ title?: string;
+ description?: string;
+ type?: "website" | "profile" | "article";
+ image?: string;
+ noindex?: boolean;
+ };
publicConfig?: {
bobbinUrl: string;
knotMirrorUrl: string;
diff --git a/web/src/lib/components/seo/PageHead.svelte b/web/src/lib/components/seo/PageHead.svelte
deleted file mode 100644
index c7c2b8e97..000000000
--- a/web/src/lib/components/seo/PageHead.svelte
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
- {#if fullTitle}
- {fullTitle}
-
-
- {/if}
- {#if description}
-
-
-
- {/if}
- {#if type}
-
- {/if}
- {#if noindex}
-
- {/if}
-
diff --git a/web/src/routes/[handle]/[repo]/+layout@.svelte b/web/src/routes/[handle]/[repo]/+layout@.svelte
index 99c2906f8..cd5e80ede 100644
--- a/web/src/routes/[handle]/[repo]/+layout@.svelte
+++ b/web/src/routes/[handle]/[repo]/+layout@.svelte
@@ -89,6 +89,23 @@
},
...(repoDesc ? { description: repoDesc } : {})
});
+ // the repo-level og defaults; pages override fields (title, card, ...) by
+ // returning meta from their load, which lands here through page data
+ const og = $derived({
+ title: repoTitle,
+ description: repoDescription,
+ image:
+ resolvedRepo || repoMeta
+ ? repoCard(
+ (resolvedRepo || repoMeta)!,
+ resolvedCounts,
+ data.publicConfig?.ogreUrl,
+ ogLanguages
+ )
+ : undefined,
+ noindex: false,
+ ...page.data.meta
+ });
const isCustomOgChildRoute = $derived(
(page.route.id ?? "").includes("/issues/[aturi]") ||
@@ -157,18 +174,13 @@
{#if !isCustomOgChildRoute}
{/if}
diff --git a/web/src/routes/[handle]/[repo]/commit/[ref]/+page.svelte b/web/src/routes/[handle]/[repo]/commit/[ref]/+page.svelte
index 2934fb935..c77aef50e 100644
--- a/web/src/routes/[handle]/[repo]/commit/[ref]/+page.svelte
+++ b/web/src/routes/[handle]/[repo]/commit/[ref]/+page.svelte
@@ -10,7 +10,6 @@
import repoCommitviewFullCommit from "$lib/bones/repo-commitview--full-commit.bones.json";
import CommitView from "$lib/components/repo/CommitView.svelte";
import Pending from "$lib/components/ui/Pending.svelte";
- import PageHead from "$lib/components/seo/PageHead.svelte";
import type { DiffStyle } from "$lib/components/repo/pierre";
let { data } = $props();
@@ -22,7 +21,6 @@
const synchronousRepo = $derived(data.repo instanceof Promise ? null : data.repo);
let asyncRepo = $state(null);
let asyncCommitPage = $state | null>(null);
- let asyncAuthor = $state | null>(null);
$effect(() => {
let active = true;
@@ -32,9 +30,6 @@
void Promise.resolve(data.commitPage).then((cp) => {
if (active) asyncCommitPage = cp;
});
- void Promise.resolve(data.author).then((a) => {
- if (active) asyncAuthor = a;
- });
return () => {
active = false;
};
@@ -42,20 +37,6 @@
const resolvedRepo = $derived(asyncRepo ?? synchronousRepo);
const repoName = $derived(resolvedRepo?.name ?? decodeURIComponent(page.params.repo ?? ""));
- const commitSummary = $derived(
- asyncCommitPage?.commit?.subject || `Commit ${data.ref.slice(0, 8)}`
- );
- const commitTitle = $derived(`${commitSummary} · ${repoName} · Tangled`);
- const authorLabel = $derived(
- asyncAuthor?.handle
- ? `@${asyncAuthor.handle}`
- : asyncCommitPage?.commit?.authorName || "Unknown author"
- );
- const commitBodyText = $derived(asyncCommitPage?.commit?.body?.trim());
- const commitDesc = $derived(
- `${commitSummary} by ${authorLabel}${commitBodyText ? ` — ${commitBodyText.replace(/\s+/g, " ").slice(0, 100)}` : ""}`
- );
-
const pipelineStatuses = liveCommitStatuses(() =>
Promise.all([data.repo, data.commitPage]).then(([repo, commitPage]) => ({
repo,
@@ -64,8 +45,6 @@
);
-
-
{#snippet skeleton()}
diff --git a/web/src/routes/[handle]/[repo]/commit/[ref]/+page.ts b/web/src/routes/[handle]/[repo]/commit/[ref]/+page.ts
index 1996b482c..4edd4c0e7 100644
--- a/web/src/routes/[handle]/[repo]/commit/[ref]/+page.ts
+++ b/web/src/routes/[handle]/[repo]/commit/[ref]/+page.ts
@@ -113,6 +113,11 @@ export const load: PageLoad = async (event) => {
return settle({
ref: event.params.ref,
commitPage: stream(commitPage),
- author: stream(authorPromise)
+ author: stream(authorPromise),
+ meta: {
+ title: `Commit ${event.params.ref.slice(0, 8)} · ${event.params.repo} · Tangled`,
+ description: `Commit ${event.params.ref.slice(0, 8)} in ${event.params.repo} on Tangled.`,
+ type: "article" as const
+ }
});
};
diff --git a/web/src/routes/[handle]/[repo]/fork/+page.svelte b/web/src/routes/[handle]/[repo]/fork/+page.svelte
index 737e7d909..07cdde19f 100644
--- a/web/src/routes/[handle]/[repo]/fork/+page.svelte
+++ b/web/src/routes/[handle]/[repo]/fork/+page.svelte
@@ -1,5 +1,4 @@
-
-
{#snippet skeleton()}
diff --git a/web/src/routes/[handle]/[repo]/fork/+page.ts b/web/src/routes/[handle]/[repo]/fork/+page.ts
index 5d0e47c7a..9afb5dc8e 100644
--- a/web/src/routes/[handle]/[repo]/fork/+page.ts
+++ b/web/src/routes/[handle]/[repo]/fork/+page.ts
@@ -4,5 +4,10 @@ import type { PageLoad } from "./$types";
export const load: PageLoad = async (event) => {
const parent = await event.parent();
- return settle(await repoCreationOptions(parent, event.fetch));
+ const handle = decodeURIComponent(event.params.handle ?? "");
+ const repo = decodeURIComponent(event.params.repo ?? "");
+ return settle({
+ ...(await repoCreationOptions(parent, event.fetch)),
+ meta: { noindex: true, title: `Fork · ${handle}/${repo}` }
+ });
};
diff --git a/web/src/routes/[handle]/[repo]/issues/new/+page.svelte b/web/src/routes/[handle]/[repo]/issues/new/+page.svelte
index 0d17d0766..0d637f0f5 100644
--- a/web/src/routes/[handle]/[repo]/issues/new/+page.svelte
+++ b/web/src/routes/[handle]/[repo]/issues/new/+page.svelte
@@ -1,5 +1,4 @@
-
-
Create a new issue
diff --git a/web/src/routes/[handle]/[repo]/issues/new/+page.ts b/web/src/routes/[handle]/[repo]/issues/new/+page.ts
new file mode 100644
index 000000000..265daab3d
--- /dev/null
+++ b/web/src/routes/[handle]/[repo]/issues/new/+page.ts
@@ -0,0 +1,9 @@
+import type { PageLoad } from "./$types";
+
+export const load: PageLoad = async (event) => {
+ const handle = decodeURIComponent(event.params.handle ?? "");
+ const repo = decodeURIComponent(event.params.repo ?? "");
+ return {
+ meta: { noindex: true, title: `New issue · ${handle}/${repo}` }
+ };
+};
diff --git a/web/src/routes/[handle]/[repo]/pulls/new/+page.svelte b/web/src/routes/[handle]/[repo]/pulls/new/+page.svelte
index c4519802e..1f2765ab8 100644
--- a/web/src/routes/[handle]/[repo]/pulls/new/+page.svelte
+++ b/web/src/routes/[handle]/[repo]/pulls/new/+page.svelte
@@ -1,5 +1,4 @@
-
-
{#snippet skeleton()}
diff --git a/web/src/routes/[handle]/[repo]/pulls/new/+page.ts b/web/src/routes/[handle]/[repo]/pulls/new/+page.ts
index c420eb0ca..791550987 100644
--- a/web/src/routes/[handle]/[repo]/pulls/new/+page.ts
+++ b/web/src/routes/[handle]/[repo]/pulls/new/+page.ts
@@ -5,6 +5,8 @@ import type { PageLoad } from "./$types";
export const load: PageLoad = async (event) => {
const parent = await event.parent();
const repo = Promise.resolve(parent.repo);
+ const handle = decodeURIComponent(event.params.handle ?? "");
+ const name = decodeURIComponent(event.params.repo ?? "");
return settle({
compose: stream(
@@ -15,6 +17,7 @@ export const load: PageLoad = async (event) => {
params: event.url.searchParams,
fetch: event.fetch
})
- )
+ ),
+ meta: { noindex: true, title: `New pull · ${handle}/${name}` }
});
};
diff --git a/web/src/routes/[handle]/[repo]/tree/[ref]/[...path]/+page.svelte b/web/src/routes/[handle]/[repo]/tree/[ref]/[...path]/+page.svelte
index 0d06b4e5c..067feab15 100644
--- a/web/src/routes/[handle]/[repo]/tree/[ref]/[...path]/+page.svelte
+++ b/web/src/routes/[handle]/[repo]/tree/[ref]/[...path]/+page.svelte
@@ -9,7 +9,6 @@
import bonesTreeviewSkeletonFixture from "$lib/bones/bones-treeview--skeleton-fixture.bones.json";
import TreeHeader from "$lib/components/repo/TreeHeader.svelte";
import Pending from "$lib/components/ui/Pending.svelte";
- import PageHead from "$lib/components/seo/PageHead.svelte";
import TabPanel from "$lib/components/ui/TabPanel.svelte";
let { data } = $props();
@@ -29,13 +28,8 @@
const resolvedRepo = $derived(asyncRepo ?? synchronousRepo);
const repoName = $derived(resolvedRepo?.name ?? decodeURIComponent(page.params.repo ?? ""));
- const filePath = $derived(data.path);
- const treeTitle = $derived(`${filePath} · ${repoName} · Tangled`);
- const treeDesc = $derived(`Viewing ${filePath} in ${repoName} on Tangled.`);
-
-
{#snippet skeleton()}
diff --git a/web/src/routes/[handle]/[repo]/tree/[ref]/[...path]/+page.ts b/web/src/routes/[handle]/[repo]/tree/[ref]/[...path]/+page.ts
index 1c73a3353..62a35ada1 100644
--- a/web/src/routes/[handle]/[repo]/tree/[ref]/[...path]/+page.ts
+++ b/web/src/routes/[handle]/[repo]/tree/[ref]/[...path]/+page.ts
@@ -5,5 +5,11 @@ import type { PageLoad } from "./$types";
// the rest param arrives decoded and joined, so it is the path as git knows it
export const load: PageLoad = async (event) => {
const parent = await event.parent();
- return settle(await loadRepoTree(event, parent, event.params.ref, event.params.path));
+ return settle({
+ ...(await loadRepoTree(event, parent, event.params.ref, event.params.path)),
+ meta: {
+ title: `${event.params.path} · ${event.params.repo} · Tangled`,
+ description: `Viewing ${event.params.path} in ${event.params.repo} on Tangled.`
+ }
+ });
};