mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-12 12:25:22 +00:00
feat(metadata): update tab title with appropriate file name, path or repository name.
This commit is contained in:
parent
bb66666ced
commit
c4d4dda130
2 changed files with 51 additions and 47 deletions
|
|
@ -4,7 +4,7 @@ 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 { Metadata } from "next";
|
||||||
import { parseRepoPath } from "@/lib/utils";
|
import { parsePathForTitle} from "@/lib/utils";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
params: {
|
params: {
|
||||||
|
|
@ -14,26 +14,22 @@ type Props = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
||||||
let title = 'Browse'; // Current Default
|
let title = 'Browse'; // Default Fallback
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const parsedInfo = parseRepoPath(params.path);
|
title = parsePathForTitle(params.path);
|
||||||
|
|
||||||
if (parsedInfo) {
|
|
||||||
const { fullRepoName, revision } = parsedInfo;
|
|
||||||
title = `${fullRepoName}${revision ? ` @ ${revision}` : ''}`;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Log the error for debugging, but don't crash the page render.
|
// TODO: Maybe I need to look into a better way of handling this error.
|
||||||
|
// for now, it is just a log, fallback tab title and prevents the app from crashing.
|
||||||
console.error("Failed to generate metadata title from path:", params.path, error);
|
console.error("Failed to generate metadata title from path:", params.path, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title, // e.g., "sourcebot-dev/sourcebot @ HEAD"
|
title,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
interface BrowsePageProps {
|
interface BrowsePageProps {
|
||||||
params: Promise<{
|
params: Promise<{
|
||||||
path: string[];
|
path: string[];
|
||||||
|
|
@ -48,6 +44,7 @@ export default async function BrowsePage(props: BrowsePageProps) {
|
||||||
} = params;
|
} = params;
|
||||||
|
|
||||||
const rawPath = _rawPath.join('/');
|
const rawPath = _rawPath.join('/');
|
||||||
|
console.log("rawPath:", rawPath);
|
||||||
const { repoName, revisionName, path, pathType } = getBrowseParamsFromPathParam(rawPath);
|
const { repoName, revisionName, path, pathType } = getBrowseParamsFromPathParam(rawPath);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -488,54 +488,61 @@ export const isHttpError = (error: unknown, status: number): boolean => {
|
||||||
&& error.status === status;
|
&& error.status === status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a URL path array to extract the full repository name and revision.
|
* Parses the URL path to generate a descriptive title.
|
||||||
* This function assumes a URL structure like:
|
* It handles three cases:
|
||||||
* `.../[hostname]/[owner]/[repo@revision]/-/tree/...`
|
* 1. File view (`blob`): "filename.ts - owner/repo"
|
||||||
* Or for nested groups (like GitLab):
|
* 2. Directory view (`tree`): "directory/ - owner/repo"
|
||||||
* `.../[hostname]/[group]/[subgroup]/[repo@revision]/-/tree/...`
|
* 3. Repository root: "owner/repo"
|
||||||
*
|
*
|
||||||
* @param path The array of path segments from Next.js params.
|
* @param path The array of path segments from Next.js params.
|
||||||
* @returns An object with fullRepoName and revision, or null if parsing fails.
|
* @returns A formatted title string.
|
||||||
*/
|
*/
|
||||||
export const parseRepoPath = (path: string[]): { fullRepoName: string; revision: string } | null => {
|
export const parsePathForTitle = (path: string[]): string => {
|
||||||
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('-');
|
const delimiterIndex = path.indexOf('-');
|
||||||
|
if (delimiterIndex === -1 || delimiterIndex === 0) {
|
||||||
// If no delimiter is found, we can't reliably parse the path.
|
return 'Browse';
|
||||||
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);
|
const repoParts = path.slice(1, delimiterIndex);
|
||||||
|
if (repoParts.length === 0) return 'Browse';
|
||||||
|
|
||||||
if (repoParts.length === 0) {
|
const lastPart = decodeURIComponent(repoParts.pop()!);
|
||||||
return null;
|
const [repoNamePart, revision = ''] = lastPart.split('@');
|
||||||
|
const ownerParts = repoParts;
|
||||||
|
const fullRepoName = [...ownerParts, repoNamePart].join('/');
|
||||||
|
const repoAndRevision = `${fullRepoName}${revision ? ` @ ${revision}` : ''}`;
|
||||||
|
|
||||||
|
// Check for file (`blob`) or directory (`tree`) view
|
||||||
|
const blobIndex = path.indexOf('blob');
|
||||||
|
const treeIndex = path.indexOf('tree');
|
||||||
|
|
||||||
|
// Case 1: Viewing a file
|
||||||
|
if (blobIndex !== -1 && path.length > blobIndex + 1) {
|
||||||
|
const encodedFilePath = path[blobIndex + 1];
|
||||||
|
const filePath = decodeURIComponent(encodedFilePath);
|
||||||
|
|
||||||
|
const fileName = filePath.split('/').pop() || filePath;
|
||||||
|
|
||||||
|
// Return a title like: "agents.ts - sourcebot-dev/sourcebot @ HEAD"
|
||||||
|
return `${fileName} - ${repoAndRevision}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The last part of the repo segment potentially contains the revision.
|
// Case 2: Viewing a directory
|
||||||
const lastPart = repoParts[repoParts.length - 1];
|
if (treeIndex !== -1 && path.length > treeIndex + 1) {
|
||||||
|
const encodedDirPath = path[treeIndex + 1];
|
||||||
|
const dirPath = decodeURIComponent(encodedDirPath);
|
||||||
|
|
||||||
// URL segments are encoded. Decode it to handle characters like '@' (%40).
|
// If we're at the root of the tree, just show the repo name
|
||||||
const decodedLastPart = decodeURIComponent(lastPart);
|
if (dirPath === '/' || dirPath === '') {
|
||||||
|
return repoAndRevision;
|
||||||
|
}
|
||||||
|
|
||||||
const [repoNamePart, revision = ''] = decodedLastPart.split('@');
|
// Otherwise, show the directory path
|
||||||
|
// Return a title like: "client/src/store/ - sourcebot-dev/sourcebot @ HEAD"
|
||||||
|
return `${dirPath.endsWith('/') ? dirPath : dirPath + '/'} - ${repoAndRevision}`;
|
||||||
|
}
|
||||||
|
|
||||||
// The preceding parts form the owner/group path.
|
// Case 3: Fallback to the repository root
|
||||||
// e.g., ["sourcebot"] or ["my-group", "my-subgroup"]
|
return repoAndRevision;
|
||||||
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