{:else if kind === "confirm" && shown?.arg}
{@const a = shown.arg}
{a.title}
{#if a.body}
{a.body}
{/if}
{:else if kind === "import"}
Import a playlist
{#each DOORS as [door, icon, t, m] (door)}
{@render row(icon, t, m, () => (door === "folder" ? folderDoor() : door === "file" ? fileDoor() : importPrompt(door)))}
{/each}
A playlist file's rows are matched against the library on this phone — by path where the file names one, and by filename otherwise, so a file copied here from another machine still finds what you have.
{:else if kind === "plmatch" && shown?.arg?.item}
{@const p = shown.arg.playlist}
{@const item = shown.arg.item}
Find a local match · {item.title}
{ matchQ = e.currentTarget.value; void runMatch(); }} type="search" placeholder="Search your library" autocomplete="off" autocapitalize="off" spellcheck="false" aria-label="Search your library" />
{#if matchBusy && !matchRows.length}
Searching…
{:else if !matchRows.length}
No library tracks match. A named row still streams from the network when you tap it.
{:else}
{#each matchRows.slice(0, 30) as c (c.path)}
{/each}
{/if}
{:else if kind === "peer" && shown?.arg?.username}
{@const u = shown.arg.username}
{@const g = shown.arg.group || null}
{@const buddy = slsk.buddies.includes(u)}
{@const m = metaOf(u)}
#{shown.arg.name} · {room?.users.length ?? 0} here
{#each (room?.users ?? []).slice(0, 120) as u (u.username)}
{@const buddy = slsk.buddies.includes(u.username)}
{/each}
{:else if kind === "setup"}
{setup.reason === "preview" ? "Set up this phone" : "Welcome to SoulStream"}
Your library, the catalog and the network, in one place. Two things make the rest of it work — neither is required now, and both live on a screen you can come back to.
Where your music lives
One folder is the library: everything saved or downloaded lands in it, and everything in it is in the library. Until you pick one it is a folder inside the app that nothing else on this phone can see.
{setup.folder || "—"}
{@render row("folder", setup.picking ? "Picking…" : needsFolder() ? "Choose a folder" : "Choose a different folder", needsFolder() ? "somewhere on this phone's own storage" : "this one is yours", () => void chooseFolder())}
{#if !needsFolder() && setup.access === false}
{@render row("alert", "Allow all files access", "a folder on the phone's storage needs it — the system screen opens, come back after", () => void requestAccess())}
{/if}
Soulseek
Anything the library does not hold comes off the network: what Search finds, a song played from someone's copy, Save. The card asks for a username and a password and keeps them on this phone — nothing here does.
{#if needsAccount()}
{@render row("users", "Sign in on Community", "the card is on the Community tab", act(() => go("community")))}
{:else}
Signed in{slsk.username || slsk.savedUser}
{/if}
Nothing above is required. You → Set up this phone again opens it back up.
{:else if kind === "plexport" && shown?.arg?.playlist}
{@const p = shown.arg.playlist}
Export · {p.name}
{#each EXPORTS as [format, label, m] (format)}
{@render row("export", label, m, act(() => exportPlaylist(p, format).catch(oops)))}
{/each}
The file goes wherever the sheet points it — Downloads, Drive, anywhere the phone offers. A row with no file of its own keeps its name: M3U8 leaves a comment where the location would be, XSPF, CSV and the backup carry the row without one, and PLS holds only real files — and the locations that are there are paths on this phone.