fix(codebase): remove legacy single-codebase hint description from api endpoints

This commit is contained in:
2026-06-14 13:20:49 -07:00
parent adc60690c8
commit d738842069
3 changed files with 135 additions and 87 deletions

View File

@@ -48,95 +48,124 @@ export default function CodeTab() {
const showLoading = loading && !anatomy; const showLoading = loading && !anatomy;
return ( return (
<div style={{ minHeight: "100vh", background: THEME.canvasGradient, fontFamily: THEME.font, padding: "36px 48px" }}> <div
style={{
minHeight: "100vh",
background: THEME.canvasGradient,
fontFamily: THEME.font,
padding: "36px 48px",
}}
>
<div style={{ maxWidth: 1400, margin: "0 auto" }}> <div style={{ maxWidth: 1400, margin: "0 auto" }}>
<PageHeader title="Codebase" subtitle="Explore the repository files for your application." /> <PageHeader
<div style={grid}> title="Codebase"
{/* ── Left rail ── */} subtitle="Explore the repository files for your application."
<section style={leftCol}> />
{showLoading && ( <div style={grid}>
{/* ── Left rail ── */}
<section style={leftCol}>
{showLoading && (
<Card> <Card>
<div style={{ display: "flex", alignItems: "center", gap: 8, color: THEME.mid, fontSize: "0.875rem" }}> <div
style={{
display: "flex",
alignItems: "center",
gap: 8,
color: THEME.mid,
fontSize: "0.875rem",
}}
>
<Loader2 size={15} className="animate-spin" /> Loading <Loader2 size={15} className="animate-spin" /> Loading
</div> </div>
</Card> </Card>
)} )}
{error && !showLoading && ( {error && !showLoading && (
<Card> <Card>
<div style={{ display: "flex", alignItems: "center", gap: 8, color: THEME.danger, fontSize: "0.875rem" }}> <div
style={{
display: "flex",
alignItems: "center",
gap: 8,
color: THEME.danger,
fontSize: "0.875rem",
}}
>
<AlertCircle size={15} /> {error} <AlertCircle size={15} /> {error}
</div> </div>
</Card> </Card>
)} )}
{anatomy && ( {anatomy && (
<> <>
{/* Code Files */} {/* Code Files */}
<RailGroup title="Code files" count={codebases?.length ?? 0}> <RailGroup title="Code files" count={codebases?.length ?? 0}>
{codebases && codebases.length === 0 && ( {codebases && codebases.length === 0 && (
<RailEmpty> <RailEmpty>
{reason === "no_repo" ? ( {reason === "no_repo" ? (
<> <>
No codebase yet.{" "} No codebase yet.{" "}
<span style={nudge}> <span style={nudge}>
Try: &quot;Start building my app&quot; Try: &quot;Start building my app&quot;
</span> </span>
</> </>
) : ( ) : (
<> <>
Repo is empty push a first commit.{" "} Repo is empty push a first commit.{" "}
<span style={nudge}> <span style={nudge}>
Try: &quot;Scaffold a Next.js app&quot; Try: &quot;Scaffold a Next.js app&quot;
</span> </span>
</> </>
)} )}
</RailEmpty> </RailEmpty>
)} )}
{codebases?.map((cb) => { {codebases?.map((cb) => {
return ( return (
<article key={cb.id} style={codebaseTile}> <article key={cb.id} style={codebaseTile}>
<div style={tileHeader}> <div style={tileHeader}>
<span style={chevronCell}> <span style={chevronCell}>
<ChevronDown size={13} style={{ color: THEME.mid }} /> <ChevronDown
</span> size={13}
<Box style={{ color: THEME.mid }}
size={13} />
style={{ color: THEME.mid, flexShrink: 0 }} </span>
/> <Box
<div style={{ minWidth: 0, textAlign: "left" }}> size={13}
<div style={tileLabel}>{cb.label}</div> style={{ color: THEME.mid, flexShrink: 0 }}
{cb.hint && <div style={tileHint}>{cb.hint}</div>} />
<div style={{ minWidth: 0, textAlign: "left" }}>
<div style={tileLabel}>{cb.label}</div>
{cb.hint && <div style={tileHint}>{cb.hint}</div>}
</div>
</div> </div>
</div> <div style={tileBody}>
<div style={tileBody}> <GiteaFileTree
<GiteaFileTree projectId={projectId}
projectId={projectId} rootPath={cb.path}
rootPath={cb.path} selectedPath={
selectedPath={ selection?.type === "file" &&
selection?.type === "file" && selection.codebaseId === cb.id
selection.codebaseId === cb.id ? selection.path
? selection.path : undefined
: undefined }
} onSelectFile={(p) =>
onSelectFile={(p) => setSelection({
setSelection({ type: "file",
type: "file", codebaseId: cb.id,
codebaseId: cb.id, path: p,
path: p, })
}) }
} />
/> </div>
</div> </article>
</article> );
); })}
})} </RailGroup>
</RailGroup> </>
</> )}
)} </section>
</section>
{/* ── Right pane ── */} {/* ── Right pane ── */}
<aside style={rightCol}> <aside style={rightCol}>
<Card <Card
padding={0} padding={0}
style={{ style={{
@@ -150,13 +179,24 @@ export default function CodeTab() {
<GiteaFileViewer projectId={projectId} path={selection.path} /> <GiteaFileViewer projectId={projectId} path={selection.path} />
)} )}
{!selection && ( {!selection && (
<div style={{ flex: 1, display: "flex", alignItems: "center", justifyContent: "center", color: THEME.muted, fontSize: "0.85rem", padding: "32px 16px", textAlign: "center" }}> <div
style={{
flex: 1,
display: "flex",
alignItems: "center",
justifyContent: "center",
color: THEME.muted,
fontSize: "0.85rem",
padding: "32px 16px",
textAlign: "center",
}}
>
Pick a codebase file on the left. Pick a codebase file on the left.
</div> </div>
)} )}
</Card> </Card>
</aside> </aside>
</div> </div>
</div> </div>
</div> </div>
); );

View File

@@ -254,7 +254,7 @@ async function discoverCodebases(giteaRepo: string): Promise<{
id: "root", id: "root",
label: repoName, label: repoName,
path: "", path: "",
hint: "Single-codebase project — repository root.", hint: "",
}, },
]; ];
} }

View File

@@ -35,14 +35,17 @@ interface GiteaItem {
type: "file" | "dir" | "symlink"; type: "file" | "dir" | "symlink";
} }
async function giteaList(repo: string, path: string): Promise<GiteaItem[] | null> { async function giteaList(
repo: string,
path: string,
): Promise<GiteaItem[] | null> {
const encoded = path ? encodeURIComponent(path).replace(/%2F/g, "/") : ""; const encoded = path ? encodeURIComponent(path).replace(/%2F/g, "/") : "";
const res = await fetch( const res = await fetch(
`${GITEA_API_URL}/api/v1/repos/${repo}/contents/${encoded}`, `${GITEA_API_URL}/api/v1/repos/${repo}/contents/${encoded}`,
{ {
headers: { Authorization: `token ${GITEA_API_TOKEN}` }, headers: { Authorization: `token ${GITEA_API_TOKEN}` },
next: { revalidate: 30 }, next: { revalidate: 30 },
} },
); );
if (res.status === 404) return null; if (res.status === 404) return null;
if (!res.ok) throw new Error(`Gitea ${res.status} listing ${repo}/${path}`); if (!res.ok) throw new Error(`Gitea ${res.status} listing ${repo}/${path}`);
@@ -52,7 +55,7 @@ async function giteaList(repo: string, path: string): Promise<GiteaItem[] | null
export async function GET( export async function GET(
_req: Request, _req: Request,
{ params }: { params: Promise<{ projectId: string }> } { params }: { params: Promise<{ projectId: string }> },
) { ) {
try { try {
const { projectId } = await params; const { projectId } = await params;
@@ -65,7 +68,7 @@ export async function GET(
`SELECT p.data FROM fs_projects p `SELECT p.data FROM fs_projects p
JOIN fs_users u ON u.id = p.user_id JOIN fs_users u ON u.id = p.user_id
WHERE p.id = $1 AND u.data->>'email' = $2 LIMIT 1`, WHERE p.id = $1 AND u.data->>'email' = $2 LIMIT 1`,
[projectId, session.user.email] [projectId, session.user.email],
); );
if (rows.length === 0) { if (rows.length === 0) {
return NextResponse.json({ error: "Project not found" }, { status: 404 }); return NextResponse.json({ error: "Project not found" }, { status: 404 });
@@ -89,15 +92,17 @@ export async function GET(
}); });
} }
const appsDir = root.find(item => item.type === "dir" && item.name === "apps"); const appsDir = root.find(
(item) => item.type === "dir" && item.name === "apps",
);
let codebases: Codebase[] = []; let codebases: Codebase[] = [];
if (appsDir) { if (appsDir) {
const appsChildren = await giteaList(giteaRepo, "apps"); const appsChildren = await giteaList(giteaRepo, "apps");
if (appsChildren) { if (appsChildren) {
codebases = appsChildren codebases = appsChildren
.filter(item => item.type === "dir") .filter((item) => item.type === "dir")
.map(item => ({ .map((item) => ({
id: item.name, id: item.name,
label: item.name, label: item.name,
path: `apps/${item.name}`, path: `apps/${item.name}`,
@@ -114,7 +119,7 @@ export async function GET(
id: "root", id: "root",
label: repoName, label: repoName,
path: "", path: "",
hint: "Single-codebase project — repository root.", hint: "",
}, },
]; ];
} }
@@ -122,6 +127,9 @@ export async function GET(
return NextResponse.json({ codebases }); return NextResponse.json({ codebases });
} catch (err) { } catch (err) {
console.error("[codebases API]", err); console.error("[codebases API]", err);
return NextResponse.json({ error: "Failed to list codebases" }, { status: 500 }); return NextResponse.json(
{ error: "Failed to list codebases" },
{ status: 500 },
);
} }
} }