setup: the installer is our own window — the alpha key is the gate, the release comes down with it and is verified before the silent install, and the in-app tour asks for the key too

This commit is contained in:
KeelrLeek committed 2026-09-09 18:00:02 -05:00
1 parent 8093835482
commit f8508da081
17 files changed
+1960 -13

No files matched your search

+49 -3
View File
@@ -27,6 +27,7 @@
const STEPS = [
{ id: "welcome", label: "Welcome", icon: "sparkle" },
{ id: "alpha", label: "Alpha key", icon: "key" },
{ id: "library", label: "Your library", icon: "library" },
{ id: "soulseek", label: "Soulseek", icon: "users" },
{ id: "community", label: "Community", icon: "globe" },
@@ -53,6 +54,10 @@
let displayName = $state("");
let glass = $state(false);
let loudness = $state(true);
// The closed alpha's read-only release key (updater.rs): typed here,
// kept by the backend in the Credential Manager, never read back.
let alphaKey = $state("");
let hasToken = $state(false);
let busy = $state(false);
let error = $state("");
let closing = $state(false);
@@ -90,6 +95,11 @@
} catch {
freenetOn = false;
}
try {
hasToken = !!(await api.updaterStatus()).has_token;
} catch {
hasToken = false;
}
await tick();
panelEl?.querySelector(".primary")?.focus();
}
@@ -132,7 +142,25 @@
async function next() {
if (busy || closing) return;
if (cur.id === "library") {
if (cur.id === "alpha") {
const k = alphaKey.trim();
if (k) {
busy = true;
error = "";
try {
await api.updaterSet({ token: k });
hasToken = true;
alphaKey = "";
// The first check can start now; the toast reports it.
void api.updaterCheck().catch(() => {});
} catch (e) {
error = String(e);
} finally {
busy = false;
}
if (error) return;
}
} else if (cur.id === "library") {
const patch = {};
if (libraryDir && libraryDir !== settings?.download_dir) patch.downloadDir = libraryDir;
const lay = layout.trim();
@@ -274,11 +302,28 @@
that runs on a peer you own. Four short questions and the app is yours.
</p>
<ul class="pills" in:fly={{ y: 10, duration: dur(520), delay: dur(460), easing: quintOut }}>
{#each STEPS.slice(1, 5) as s (s.id)}
{#each STEPS.slice(1, 6) as s (s.id)}
<li><Icon name={s.icon} size={13} /> {s.label}</li>
{/each}
</ul>
</div>
{:else if cur.id === "alpha"}
<h1 id="fr-title">Your alpha key</h1>
<p class="lede">
SoulStream is in a closed alpha. New builds come from a private repository, and
this key — one per tester, able only to read them — is how your copy finds
updates. It goes to the Windows Credential Manager, not a plain file. Without it
the app runs, but cannot update itself.
</p>
<div class="card">
<div class="row">
<div class="label">
<div class="name">Alpha key {#if hasToken}<span class="tag ok">set</span>{/if}</div>
<div class="hint">{hasToken ? "A key is saved on this computer. Paste another to replace it, or press Next to keep it." : "Paste the key you were given. It starts with github_pat_."}</div>
</div>
<div class="ctl"><input type="password" class="mono" bind:value={alphaKey} placeholder={hasToken ? "••••••••••••" : "github_pat_…"} spellcheck="false" autocomplete="off" aria-label="Alpha key" /></div>
</div>
</div>
{:else if cur.id === "library"}
<h1 id="fr-title">Where does your music live?</h1>
<p class="lede">
@@ -400,6 +445,7 @@
<p class="lede">Here is what you chose. Every line of it lives in Settings.</p>
<ul class="summary">
{#each [
["key", "Alpha key", hasToken ? "set" : "not set — updates need it"],
["folder-open", "Library", libraryDir || "not chosen"],
["users", "Soulseek", online ? `signed in as ${app.username}` : "not signed in yet"],
["globe", "Community peer", freenetOn ? (displayName.trim() ? `on · ${displayName.trim()}` : "on") : "off"],
@@ -430,7 +476,7 @@
{:else if cur.id === "ready"}
<button class="ax-fill primary big" onclick={finish} disabled={closing}>Open SoulStream</button>
{:else}
{#if cur.id === "soulseek" && !online}
{#if (cur.id === "soulseek" && !online) || (cur.id === "alpha" && !hasToken && !alphaKey.trim())}
<button class="lnk" onclick={next} disabled={busy}>Later</button>
{/if}
<button class="ax-fill primary" onclick={next} disabled={busy || closing}>{busy ? "Saving…" : "Next"} <Icon name="chevron-right" size={14} /></button>