feat(codebase): auto-select the best entry point file on load so preview isn't empty

This commit is contained in:
2026-06-14 13:47:08 -07:00
parent ccba3d42d2
commit 93e08e5c8e
2 changed files with 67 additions and 2 deletions

View File

@@ -183,6 +183,9 @@ export default function CodeTab() {
<GiteaFileTree
projectId={projectId}
rootPath={cb.path}
autoSelect={
codebases.length > 0 && cb.id === codebases[0].id
}
selectedPath={
selection?.type === "file" &&
selection.codebaseId === cb.id

View File

@@ -9,7 +9,7 @@
* Gitea's web UI on click.
*/
import { useEffect, useState, useCallback } from "react";
import { useEffect, useState, useCallback, useRef } from "react";
import { Loader2, AlertCircle } from "lucide-react";
import { Tree, Folder, File } from "@/components/ui/file-tree";
import { THEME } from "@/components/project/dashboard-ui";
@@ -36,6 +36,8 @@ interface GiteaFileTreeProps {
onSelectFile?: (path: string) => void;
/** Path of the currently-selected file, used to highlight the row. */
selectedPath?: string;
/** If true, automatically selects the best available file (e.g. page.tsx, package.json) on mount. */
autoSelect?: boolean;
}
// ── In-memory cache to persist tree state across tab navigations ──
@@ -48,13 +50,45 @@ const treeCache: Record<
}
> = {};
function pickBestFile(paths: string[]): string | undefined {
const PREFERRED = [
"src/app/page.tsx",
"src/app/page.jsx",
"app/page.tsx",
"app/page.jsx",
"src/pages/index.tsx",
"src/pages/index.jsx",
"pages/index.tsx",
"pages/index.jsx",
"src/App.tsx",
"src/App.jsx",
"src/main.tsx",
"src/main.jsx",
"src/index.ts",
"src/index.js",
"package.json",
"README.md",
];
for (const p of PREFERRED) {
if (paths.includes(p)) return p;
}
const fallbackExts = [".tsx", ".ts", ".jsx", ".js", ".json"];
for (const ext of fallbackExts) {
const found = paths.find((p) => p.endsWith(ext));
if (found) return found;
}
return paths[0];
}
export function GiteaFileTree({
projectId,
rootPath,
onSelectFile,
selectedPath,
autoSelect,
}: GiteaFileTreeProps) {
const cacheKey = `${projectId}::${rootPath}`;
const autoSelectedRef = useRef(false);
const [rootItems, setRootItems] = useState<TreeItem[] | null>(() => {
return treeCache[cacheKey]?.rootItems ?? null;
@@ -101,7 +135,22 @@ export function GiteaFileTree({
// Load root whenever projectId or rootPath changes
useEffect(() => {
// If we already loaded this from cache on mount, skip fetching again
if (treeCache[cacheKey]) return;
if (treeCache[cacheKey]) {
if (autoSelect && !autoSelectedRef.current && onSelectFile) {
autoSelectedRef.current = true;
const cached = treeCache[cacheKey];
const allItems = [
...cached.rootItems,
...Object.values(cached.childrenByPath).flat(),
];
const filePaths = allItems
.filter((i) => i.type === "file")
.map((i) => i.path);
const best = pickBestFile(filePaths);
if (best) onSelectFile(best);
}
return;
}
let cancelled = false;
setLoading(true);
@@ -184,6 +233,19 @@ export function GiteaFileTree({
);
}
if (autoSelect && !autoSelectedRef.current && onSelectFile) {
autoSelectedRef.current = true;
const allItems = [
...items,
...Object.values(newChildrenByPath).flat(),
];
const filePaths = allItems
.filter((i) => i.type === "file")
.map((i) => i.path);
const best = pickBestFile(filePaths);
if (best) onSelectFile(best);
}
if (cancelled) return;
setRootItems(items);