All files / src/routes Gitea.svelte

0% Statements 0/74
0% Branches 0/1
0% Functions 0/1
0% Lines 0/74

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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                                                                                                                                                                                                                                                         
<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>