Add read-only repository viewer (UI + backend)

Implements a read-only repository viewer for remote Gitea repos and local clones. Adds UI/CSS for viewer panels, breadcrumb/branch controls, file table, code/Markdown preview, and readme rendering (frontend/css/components.css, frontend/js/app.js). Extends app state and wiring (state.js, app.js) with viewer actions, branch/content loading, local/remote navigation, and preview helpers (base64 decoding, markdown rendering, syntax highlighting, 256 KB preview limit). Adds Gitea API helpers to fetch repo branches and contents (frontend/js/gitea-api.js) and Tauri JS bindings for local repo operations (tauri-api.js). Implements Rust backend commands to list branches, tree entries, and file contents (with size/binary checks and helper utilities) and wires them into the Tauri command registry (src-tauri/src/lib.rs). Also updates README to mention the new read-only viewer.
This commit is contained in:
2026-05-09 18:45:51 +12:00
parent 17c0174e7d
commit f0358dbdfe
7 changed files with 1276 additions and 5 deletions
+51
View File
@@ -50,3 +50,54 @@ export async function fetchRepositories(serverConfig, page = 1, limit = 50) {
}
return response.json();
}
function encodePathSegment(value = "") {
return encodeURIComponent(value);
}
function encodeRepoPath(path = "") {
return path
.split("/")
.filter(Boolean)
.map(encodePathSegment)
.join("/");
}
function splitRepoFullName(fullName = "") {
const [owner, repo] = fullName.split("/");
if (!owner || !repo) {
throw new Error("Repository full name must be in owner/repo format.");
}
return { owner, repo };
}
export async function fetchRepoBranches(serverConfig, fullName) {
const apiBase = buildApiBaseUrl(serverConfig.serverUrl);
const { owner, repo } = splitRepoFullName(fullName);
const url = `${apiBase}/repos/${encodePathSegment(owner)}/${encodePathSegment(repo)}/branches`;
const response = await fetch(url, {
headers: buildHeaders(serverConfig),
});
if (!response.ok) {
throw new Error(`Gitea API error: ${response.status}`);
}
return response.json();
}
export async function fetchRepoContents(serverConfig, fullName, path = "", ref = "") {
const apiBase = buildApiBaseUrl(serverConfig.serverUrl);
const { owner, repo } = splitRepoFullName(fullName);
const encodedPath = encodeRepoPath(path);
const pathSuffix = encodedPath ? `/${encodedPath}` : "";
const query = ref ? `?ref=${encodeURIComponent(ref)}` : "";
const url = `${apiBase}/repos/${encodePathSegment(owner)}/${encodePathSegment(repo)}/contents${pathSuffix}${query}`;
const response = await fetch(url, {
headers: buildHeaders(serverConfig),
});
if (!response.ok) {
throw new Error(`Gitea API error: ${response.status}`);
}
return response.json();
}