JustAPI Bot fix: comprehensive UI, docs, security, and deployment improvements · 7/14/2026
381467d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
---
import { Icon } from "astro-icon/components";
import { Table, TableBody, TableCell, TableRow } from "@/components/ui/table";
interface Props {
owner: string;
repo: string;
branch: string;
path?: string;
files: {
name: string;
type: "file" | "directory" | "submodule";
lastCommit?: {
message: string;
date: string;
hash: string;
};
}[];
}
const { owner, repo, branch, path = "", files } = Astro.props;
const getUrl = (file: any) => {
const type = file.type === "directory" ? "tree" : "blob";
const filePath = path ? `${path}/${file.name}` : file.name;
return `/${owner}/${repo}/${type}/${branch}/${filePath}`;
};
const getIconName = (fileName: string) => {
const ext = fileName.split(".").pop()?.toLowerCase();
const lowerName = fileName.toLowerCase();
if (lowerName === "package.json") return "vscode-icons:file-type-node";
if (lowerName === "tsconfig.json") return "vscode-icons:file-type-tsconfig";
if (lowerName === "dockerfile" || ext === "dockerignore") return "vscode-icons:file-type-docker";
if (lowerName === ".gitignore") return "vscode-icons:file-type-git";
if (ext === "md") return "vscode-icons:file-type-markdown";
if (ext === "json") return "vscode-icons:file-type-json";
if (ext === "js" || ext === "cjs" || ext === "mjs") return "vscode-icons:file-type-js";
if (ext === "jsx") return "vscode-icons:file-type-reactjs";
if (ext === "ts") return "vscode-icons:file-type-typescript";
if (ext === "tsx") return "vscode-icons:file-type-reactts";
if (ext === "html" || ext === "htm") return "vscode-icons:file-type-html";
if (ext === "css") return "vscode-icons:file-type-css";
if (ext === "scss" || ext === "sass") return "vscode-icons:file-type-sass";
if (ext === "py") return "vscode-icons:file-type-python";
if (ext === "go") return "vscode-icons:file-type-go";
if (ext === "rs") return "vscode-icons:file-type-rust";
if (ext === "java") return "vscode-icons:file-type-java";
if (ext === "c") return "vscode-icons:file-type-c";
if (ext === "cpp" || ext === "cc" || ext === "cxx") return "vscode-icons:file-type-cpp";
if (ext === "h" || ext === "hpp") return "vscode-icons:file-type-cplusplus";
if (ext === "sh" || ext === "bash") return "vscode-icons:file-type-shell";
if (ext === "yaml" || ext === "yml") return "vscode-icons:file-type-yaml";
if (ext === "toml") return "vscode-icons:file-type-toml";
if (ext === "xml") return "vscode-icons:file-type-xml";
if (ext === "png" || ext === "jpg" || ext === "jpeg" || ext === "gif" || ext === "svg" || ext === "webp") return "vscode-icons:file-type-image";
if (ext === "astro") return "vscode-icons:file-type-astro";
if (ext === "vue") return "vscode-icons:file-type-vue";
return "vscode-icons:default-file";
};
---
<div class="w-full">
<table class="w-full text-sm">
<tbody class="divide-y divide-border">
{
path && (
<tr class="hover:bg-muted/50 transition-colors">
<td class="p-4 py-2 w-10">
<span class="text-muted-foreground">..</span>
</td>
<td class="p-4 py-2 font-medium">
<a
href={`/${owner}/${repo}/tree/${branch}/${path.split("/").slice(0, -1).join("/")}`}
class="hover:underline hover:text-primary text-blue-500 block"
>
..
</a>
</td>
<td class="p-4 py-2" />
<td class="p-4 py-2" />
</tr>
)
}
{
files.length > 0 ? (
files.map((file) => (
<tr class="hover:bg-muted/50 transition-colors group">
<td class="p-4 py-2.5 w-10">
{file.type === "directory" ? (
<Icon name="vscode-icons:default-folder" class="h-4 w-4" />
) : (
<Icon name={getIconName(file.name)} class="h-4 w-4" />
)}
</td>
<td class="p-4 py-2.5">
<a
href={getUrl(file)}
class="hover:underline hover:text-primary text-foreground font-medium block"
>
{file.name}
</a>
</td>
<td class="p-4 py-2.5 text-muted-foreground max-w-md truncate">
{file.lastCommit ? (
<a
href={`/${owner}/${repo}/commit/${file.lastCommit.hash}`}
class="hover:text-primary hover:underline"
>
{file.lastCommit.message}
</a>
) : (
<span class="animate-pulse bg-muted h-4 w-24 rounded inline-block" />
)}
</td>
<td class="p-4 py-2.5 text-right text-muted-foreground whitespace-nowrap text-xs">
{file.lastCommit?.date || ""}
</td>
</tr>
))
) : (
<tr>
<td colspan={4} class="p-8 text-center text-muted-foreground">
This directory is empty.
</td>
</tr>
)
}
</tbody>
</table>
</div>