125 lines
5.1 KiB
Svelte
125 lines
5.1 KiB
Svelte
<script>
|
|
import { onMount } from "svelte";
|
|
import { get } from "../../lib/api.js";
|
|
|
|
let repos = $state([]);
|
|
let commits = $state([]);
|
|
let selectedRepo = $state("");
|
|
let loading = $state(true);
|
|
let loadingCommits = $state(false);
|
|
|
|
async function load() {
|
|
loading = true;
|
|
const data = await get("/gitea/repos");
|
|
repos = Array.isArray(data) ? data : data?.data || [];
|
|
loading = false;
|
|
}
|
|
|
|
async function showCommits(owner, repo) {
|
|
selectedRepo = `${owner}/${repo}`;
|
|
loadingCommits = true;
|
|
const data = await get(`/gitea/repos/${owner}/${repo}/commits`);
|
|
commits = Array.isArray(data) ? data : [];
|
|
loadingCommits = false;
|
|
}
|
|
|
|
function timeAgo(d) {
|
|
const s = Math.floor((Date.now() - new Date(d)) / 1000);
|
|
if (s < 60) return "just now";
|
|
if (s < 3600) return Math.floor(s / 60) + "m ago";
|
|
if (s < 86400) return Math.floor(s / 3600) + "h ago";
|
|
return Math.floor(s / 86400) + "d ago";
|
|
}
|
|
|
|
const langColors = {
|
|
Python: "#3572A5", JavaScript: "#f1e05a", Shell: "#89e051", Go: "#00ADD8",
|
|
TypeScript: "#3178c6", Markdown: "#083fa1", CSS: "#563d7c", HTML: "#e34c26",
|
|
};
|
|
|
|
onMount(load);
|
|
</script>
|
|
|
|
<div class="space-y-6">
|
|
<div>
|
|
<h1 class="text-2xl font-bold text-surface-900 dark:text-white tracking-tight">Repositories</h1>
|
|
<p class="text-sm text-surface-400 mt-1">{repos.length} repositories on Gitea</p>
|
|
</div>
|
|
|
|
{#if loading}
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
|
{#each Array(4) as _}
|
|
<div class="h-[100px] rounded-xl bg-surface-100 dark:bg-surface-800 animate-pulse"></div>
|
|
{/each}
|
|
</div>
|
|
{:else}
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
|
{#each repos as r}
|
|
<button
|
|
class="bg-white dark:bg-surface-800 rounded-xl border border-surface-200 dark:border-surface-700 p-4 text-left shadow-sm hover:shadow-md hover:border-primary-200 dark:hover:border-primary-700 transition-all duration-200 group {selectedRepo === `${r.owner?.login}/${r.name}` ? 'ring-2 ring-primary-500 border-primary-300' : ''}"
|
|
onclick={() => showCommits(r.owner?.login, r.name)}
|
|
>
|
|
<div class="flex items-start justify-between">
|
|
<div class="min-w-0">
|
|
<p class="text-sm font-semibold text-surface-800 dark:text-surface-100 group-hover:text-primary-600 transition-colors truncate">{r.name}</p>
|
|
<p class="text-xs text-surface-400 mt-1 line-clamp-1">{r.description || "No description"}</p>
|
|
</div>
|
|
{#if r.language}
|
|
<span class="flex items-center gap-1 text-[11px] text-surface-500 shrink-0 ml-2">
|
|
<span class="w-2 h-2 rounded-full" style="background: {langColors[r.language] || '#888'}"></span>
|
|
{r.language}
|
|
</span>
|
|
{/if}
|
|
</div>
|
|
<p class="text-[11px] text-surface-400 mt-2.5">Updated {timeAgo(r.updated_at)}</p>
|
|
</button>
|
|
{/each}
|
|
</div>
|
|
{/if}
|
|
|
|
<!-- Commits Panel -->
|
|
{#if selectedRepo}
|
|
<div class="bg-white dark:bg-surface-800 rounded-xl border border-surface-200 dark:border-surface-700 p-5 shadow-sm">
|
|
<div class="flex items-center justify-between mb-4">
|
|
<div>
|
|
<h3 class="text-sm font-bold text-surface-800 dark:text-surface-100">Recent Commits</h3>
|
|
<p class="text-xs text-surface-400 mt-0.5">{selectedRepo}</p>
|
|
</div>
|
|
<button
|
|
aria-label="Close commits"
|
|
class="text-xs text-surface-400 hover:text-surface-600 transition-colors"
|
|
onclick={() => { selectedRepo = ""; commits = []; }}
|
|
>
|
|
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg>
|
|
</button>
|
|
</div>
|
|
{#if loadingCommits}
|
|
<div class="space-y-3">
|
|
{#each Array(3) as _}
|
|
<div class="h-12 rounded-lg bg-surface-100 animate-pulse"></div>
|
|
{/each}
|
|
</div>
|
|
{:else}
|
|
<div class="space-y-0">
|
|
{#each commits.slice(0, 15) as c, i}
|
|
<div class="flex gap-3 py-2.5 {i < commits.length - 1 ? 'border-b border-surface-100 dark:border-surface-700' : ''}">
|
|
<div class="flex flex-col items-center pt-1">
|
|
<div class="w-2 h-2 rounded-full bg-primary-400 shrink-0"></div>
|
|
{#if i < commits.length - 1}
|
|
<div class="w-px flex-1 bg-surface-200 mt-1"></div>
|
|
{/if}
|
|
</div>
|
|
<div class="min-w-0 flex-1">
|
|
<p class="text-sm text-surface-700 dark:text-surface-200 truncate">{c.commit?.message?.split("\n")[0]}</p>
|
|
<p class="text-[11px] text-surface-400 mt-0.5">
|
|
<span class="font-medium">{c.commit?.author?.name}</span> · {timeAgo(c.commit?.author?.date)}
|
|
</p>
|
|
</div>
|
|
<span class="text-[10px] font-mono text-surface-400 bg-surface-50 dark:bg-surface-700 px-1.5 py-0.5 rounded h-fit shrink-0">{c.sha?.slice(0, 7)}</span>
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
{/if}
|
|
</div>
|