Skip to content

Commit c4d4dda

Browse files
committed
feat(metadata): update tab title with appropriate file name, path or repository name.
1 parent bb66666 commit c4d4dda

File tree

2 files changed

+49
-45
lines changed

2 files changed

+49
-45
lines changed

packages/web/src/app/[domain]/browse/[...path]/page.tsx

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { CodePreviewPanel } from "./components/codePreviewPanel";
44
import { Loader2 } from "lucide-react";
55
import { TreePreviewPanel } from "./components/treePreviewPanel";
66
import { Metadata } from "next";
7-
import { parseRepoPath } from "@/lib/utils";
7+
import { parsePathForTitle} from "@/lib/utils";
88

99
type Props = {
1010
params: {
@@ -14,26 +14,22 @@ type Props = {
1414
};
1515

1616
export async function generateMetadata({ params }: Props): Promise<Metadata> {
17-
let title = 'Browse'; // Current Default
17+
let title = 'Browse'; // Default Fallback
1818

1919
try {
20-
const parsedInfo = parseRepoPath(params.path);
20+
title = parsePathForTitle(params.path);
2121

22-
if (parsedInfo) {
23-
const { fullRepoName, revision } = parsedInfo;
24-
title = `${fullRepoName}${revision ? ` @ ${revision}` : ''}`;
25-
}
2622
} catch (error) {
27-
// Log the error for debugging, but don't crash the page render.
23+
// TODO: Maybe I need to look into a better way of handling this error.
24+
// for now, it is just a log, fallback tab title and prevents the app from crashing.
2825
console.error("Failed to generate metadata title from path:", params.path, error);
2926
}
3027

3128
return {
32-
title, // e.g., "sourcebot-dev/sourcebot @ HEAD"
29+
title,
3330
};
3431
}
3532

36-
3733
interface BrowsePageProps {
3834
params: Promise<{
3935
path: string[];
@@ -48,6 +44,7 @@ export default async function BrowsePage(props: BrowsePageProps) {
4844
} = params;
4945

5046
const rawPath = _rawPath.join('/');
47+
console.log("rawPath:", rawPath);
5148
const { repoName, revisionName, path, pathType } = getBrowseParamsFromPathParam(rawPath);
5249

5350
return (

packages/web/src/lib/utils.ts

Lines changed: 42 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -488,54 +488,61 @@ export const isHttpError = (error: unknown, status: number): boolean => {
488488
&& error.status === status;
489489
}
490490

491-
492491
/**
493-
* Parses a URL path array to extract the full repository name and revision.
494-
* This function assumes a URL structure like:
495-
* `.../[hostname]/[owner]/[repo@revision]/-/tree/...`
496-
* Or for nested groups (like GitLab):
497-
* `.../[hostname]/[group]/[subgroup]/[repo@revision]/-/tree/...`
492+
* Parses the URL path to generate a descriptive title.
493+
* It handles three cases:
494+
* 1. File view (`blob`): "filename.ts - owner/repo"
495+
* 2. Directory view (`tree`): "directory/ - owner/repo"
496+
* 3. Repository root: "owner/repo"
498497
*
499498
* @param path The array of path segments from Next.js params.
500-
* @returns An object with fullRepoName and revision, or null if parsing fails.
499+
* @returns A formatted title string.
501500
*/
502-
export const parseRepoPath = (path: string[]): { fullRepoName: string; revision: string } | null => {
503-
if (path.length < 2) {
504-
return null; // Not enough path segments to parse.
505-
}
506-
507-
// Find the index of the `-` delimiter which separates the repo info from the file tree info.
501+
export const parsePathForTitle = (path: string[]): string => {
508502
const delimiterIndex = path.indexOf('-');
509-
510-
// If no delimiter is found, we can't reliably parse the path.
511-
if (delimiterIndex === -1) {
512-
return null;
503+
if (delimiterIndex === -1 || delimiterIndex === 0) {
504+
return 'Browse';
513505
}
514506

515-
// The repository parts are between the hostname (index 0) and the delimiter.
516-
// e.g., ["github.com", "sourcebot-dev", "sourcebot"] -> slice will be ["sourcebot-dev", "sourcebot"]
517507
const repoParts = path.slice(1, delimiterIndex);
508+
if (repoParts.length === 0) return 'Browse';
518509

519-
if (repoParts.length === 0) {
520-
return null;
521-
}
510+
const lastPart = decodeURIComponent(repoParts.pop()!);
511+
const [repoNamePart, revision = ''] = lastPart.split('@');
512+
const ownerParts = repoParts;
513+
const fullRepoName = [...ownerParts, repoNamePart].join('/');
514+
const repoAndRevision = `${fullRepoName}${revision ? ` @ ${revision}` : ''}`;
522515

523-
// The last part of the repo segment potentially contains the revision.
524-
const lastPart = repoParts[repoParts.length - 1];
516+
// Check for file (`blob`) or directory (`tree`) view
517+
const blobIndex = path.indexOf('blob');
518+
const treeIndex = path.indexOf('tree');
525519

526-
// URL segments are encoded. Decode it to handle characters like '@' (%40).
527-
const decodedLastPart = decodeURIComponent(lastPart);
520+
// Case 1: Viewing a file
521+
if (blobIndex !== -1 && path.length > blobIndex + 1) {
522+
const encodedFilePath = path[blobIndex + 1];
523+
const filePath = decodeURIComponent(encodedFilePath);
528524

529-
const [repoNamePart, revision = ''] = decodedLastPart.split('@');
525+
const fileName = filePath.split('/').pop() || filePath;
526+
527+
// Return a title like: "agents.ts - sourcebot-dev/sourcebot @ HEAD"
528+
return `${fileName} - ${repoAndRevision}`;
529+
}
530530

531-
// The preceding parts form the owner/group path.
532-
// e.g., ["sourcebot"] or ["my-group", "my-subgroup"]
533-
const ownerParts = repoParts.slice(0, repoParts.length - 1);
531+
// Case 2: Viewing a directory
532+
if (treeIndex !== -1 && path.length > treeIndex + 1) {
533+
const encodedDirPath = path[treeIndex + 1];
534+
const dirPath = decodeURIComponent(encodedDirPath);
535+
536+
// If we're at the root of the tree, just show the repo name
537+
if (dirPath === '/' || dirPath === '') {
538+
return repoAndRevision;
539+
}
534540

535-
// Reconstruct the full repository name.
536-
// e.g., "sourcebot-dev" + "/" + "sourcebot"
537-
// e.g., "my-group/my-subgroup" + "/" + "my-repo"
538-
const fullRepoName = [...ownerParts, repoNamePart].join('/');
541+
// Otherwise, show the directory path
542+
// Return a title like: "client/src/store/ - sourcebot-dev/sourcebot @ HEAD"
543+
return `${dirPath.endsWith('/') ? dirPath : dirPath + '/'} - ${repoAndRevision}`;
544+
}
539545

540-
return { fullRepoName, revision };
546+
// Case 3: Fallback to the repository root
547+
return repoAndRevision;
541548
}

0 commit comments

Comments
 (0)