Add GitHub plugin release imports and update discovery

This commit is contained in:
2026-09-18 01:29:38 -04:00
parent a718efc9e7
commit 3c49437bf9
16 changed files with 847 additions and 2 deletions
@@ -0,0 +1,47 @@
<script lang="ts">
import { onMount } from 'svelte';
import type { PluginUpdate } from '$lib/plugin-github.js';
import Card from './primitives/Card.svelte';
import Button from './primitives/Button.svelte';
let updates = $state<PluginUpdate[]>([]);
let checking = $state(false);
let checked = $state(false);
let error = $state('');
const tracked = $derived(updates.filter(u => u.status !== 'untracked'));
async function check() {
if (checking) return;
checking = true; error = '';
try {
const response = await fetch('/api/plugins/updates');
const body = await response.json();
if (!response.ok) throw new Error(body.error ?? 'Could not check updates.');
updates = body.updates; checked = true;
} catch (e) { error = (e as Error).message; }
finally { checking = false; }
}
onMount(() => { void check(); });
</script>
<Card padding="lg">
<div class="heading"><h2>Release updates</h2><Button size="sm" loading={checking} onclick={check}>Check for updates</Button></div>
<p>Latest stable GitHub releases. Checks are cached for five minutes.</p>
{#if error}<p class="error" role="alert">{error}</p>{/if}
{#if checked && !tracked.length}<p>Import a plugin from GitHub to start tracking its releases.</p>{/if}
{#each tracked as update (update.pluginId)}
<div class="row">
<div><a href={`/plugins/${update.pluginId}`}>{update.name}</a>
<p>{update.status === 'available' ? `${update.release?.tag} available` : update.status === 'imported' ? `${update.release?.tag} imported — awaiting promotion` : update.status === 'current' ? `${update.release?.tag} promoted` : update.status === 'ahead' ? 'Ahead of the latest stable release' : update.error}</p>
</div>
{#if update.status === 'available'}<Button size="sm" href={`/plugins/${update.pluginId}#github-releases`}>Review update</Button>{/if}
</div>
{/each}
</Card>
<style>
.heading, .row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); flex-wrap: wrap; }
h2 { margin: 0; font-size: var(--text-md); font-weight: var(--weight-semibold); }
p { color: var(--text-secondary); font-size: var(--text-sm); margin: var(--space-2) 0 0; }
.row { padding-top: var(--space-3); margin-top: var(--space-3); border-top: 1px solid var(--border-subtle); }
a { color: var(--accent-primary-hover); }
.error { color: var(--state-danger); }
</style>