mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-12 04:15:30 +00:00
feat(metadata): Enhance metadata generation for repository browsing
feat(utils): Add parseRepoPath function to extract repository name and revision from URL path
This commit is contained in:
parent
c3fae1aaab
commit
bb66666ced
3 changed files with 91 additions and 3 deletions
|
|
@ -3,6 +3,36 @@ import { getBrowseParamsFromPathParam } from "../hooks/utils";
|
||||||
import { CodePreviewPanel } from "./components/codePreviewPanel";
|
import { CodePreviewPanel } from "./components/codePreviewPanel";
|
||||||
import { Loader2 } from "lucide-react";
|
import { Loader2 } from "lucide-react";
|
||||||
import { TreePreviewPanel } from "./components/treePreviewPanel";
|
import { TreePreviewPanel } from "./components/treePreviewPanel";
|
||||||
|
import { Metadata } from "next";
|
||||||
|
import { parseRepoPath } from "@/lib/utils";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
params: {
|
||||||
|
domain: string;
|
||||||
|
path: string[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
||||||
|
let title = 'Browse'; // Current Default
|
||||||
|
|
||||||
|
try {
|
||||||
|
const parsedInfo = parseRepoPath(params.path);
|
||||||
|
|
||||||
|
if (parsedInfo) {
|
||||||
|
const { fullRepoName, revision } = parsedInfo;
|
||||||
|
title = `${fullRepoName}${revision ? ` @ ${revision}` : ''}`;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Log the error for debugging, but don't crash the page render.
|
||||||
|
console.error("Failed to generate metadata title from path:", params.path, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
title, // e.g., "sourcebot-dev/sourcebot @ HEAD"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
interface BrowsePageProps {
|
interface BrowsePageProps {
|
||||||
params: Promise<{
|
params: Promise<{
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,15 @@ import { PlanProvider } from "@/features/entitlements/planProvider";
|
||||||
import { getEntitlements } from "@sourcebot/shared";
|
import { getEntitlements } from "@sourcebot/shared";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Sourcebot",
|
// Using the title.template will allow child pages to set the title
|
||||||
description: "Sourcebot is a self-hosted code understanding tool. Ask questions about your codebase and get rich Markdown answers with inline citations.",
|
// while keeping a consistent suffix.
|
||||||
manifest: "/manifest.json",
|
title: {
|
||||||
|
default: "Sourcebot",
|
||||||
|
template: "%s | Sourcebot",
|
||||||
|
},
|
||||||
|
description:
|
||||||
|
"Sourcebot is a self-hosted code understanding tool. Ask questions about your codebase and get rich Markdown answers with inline citations.",
|
||||||
|
manifest: "/manifest.json",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
|
|
|
||||||
|
|
@ -487,3 +487,55 @@ export const isHttpError = (error: unknown, status: number): boolean => {
|
||||||
&& 'status' in error
|
&& 'status' in error
|
||||||
&& error.status === status;
|
&& error.status === status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a URL path array to extract the full repository name and revision.
|
||||||
|
* This function assumes a URL structure like:
|
||||||
|
* `.../[hostname]/[owner]/[repo@revision]/-/tree/...`
|
||||||
|
* Or for nested groups (like GitLab):
|
||||||
|
* `.../[hostname]/[group]/[subgroup]/[repo@revision]/-/tree/...`
|
||||||
|
*
|
||||||
|
* @param path The array of path segments from Next.js params.
|
||||||
|
* @returns An object with fullRepoName and revision, or null if parsing fails.
|
||||||
|
*/
|
||||||
|
export const parseRepoPath = (path: string[]): { fullRepoName: string; revision: string } | null => {
|
||||||
|
if (path.length < 2) {
|
||||||
|
return null; // Not enough path segments to parse.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the index of the `-` delimiter which separates the repo info from the file tree info.
|
||||||
|
const delimiterIndex = path.indexOf('-');
|
||||||
|
|
||||||
|
// If no delimiter is found, we can't reliably parse the path.
|
||||||
|
if (delimiterIndex === -1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The repository parts are between the hostname (index 0) and the delimiter.
|
||||||
|
// e.g., ["github.com", "sourcebot-dev", "sourcebot"] -> slice will be ["sourcebot-dev", "sourcebot"]
|
||||||
|
const repoParts = path.slice(1, delimiterIndex);
|
||||||
|
|
||||||
|
if (repoParts.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The last part of the repo segment potentially contains the revision.
|
||||||
|
const lastPart = repoParts[repoParts.length - 1];
|
||||||
|
|
||||||
|
// URL segments are encoded. Decode it to handle characters like '@' (%40).
|
||||||
|
const decodedLastPart = decodeURIComponent(lastPart);
|
||||||
|
|
||||||
|
const [repoNamePart, revision = ''] = decodedLastPart.split('@');
|
||||||
|
|
||||||
|
// The preceding parts form the owner/group path.
|
||||||
|
// e.g., ["sourcebot"] or ["my-group", "my-subgroup"]
|
||||||
|
const ownerParts = repoParts.slice(0, repoParts.length - 1);
|
||||||
|
|
||||||
|
// Reconstruct the full repository name.
|
||||||
|
// e.g., "sourcebot-dev" + "/" + "sourcebot"
|
||||||
|
// e.g., "my-group/my-subgroup" + "/" + "my-repo"
|
||||||
|
const fullRepoName = [...ownerParts, repoNamePart].join('/');
|
||||||
|
|
||||||
|
return { fullRepoName, revision };
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue