android: the sweep — what thirty rounds left behind, and the byte that hid a file from grep

Nothing here changes what the phone does, and the walks are untouched on
purpose: they are the guard, not the subject. Out go nine imports no file
ever names again, two things nothing in src, tests, crates or src-tauri
has ever referenced — `forgetAccount` in the Soulseek store, whose Sign
out row calls `disconnect`, and `isLossless` in util.js — and two CSS
rules no markup can reach, `.cardpad` and `.iconbtn.liked`; `stamp` and
`fmtCount` had each been written twice word for word, so both bodies move
into util.js unchanged and four screens gain a name on an import line they
already had. The byte is in player.svelte.js, where the play-history key
joins artist and title on a NUL that was written into the source as the
character itself rather than as the escape catalog.svelte.js already uses
— that made the file non-text, so every grep of it since has answered
"Binary file matches" and shown nothing. One line is added rather than
removed: `.mini .mini-lane { width: auto }` reads exactly like the strays
and is the only thing keeping the lane from coming out 32px wider than the
strip it was cut for, so it now says so — and `.feat`, declared twice in
shell.css with different values, is left alone, because untangling that
moves pixels and this round may not. A tenth dead import is missing from
that first list on purpose: Sheet.svelte's `import * as api` is a namespace
import, the one shape Rollup cannot shake, so its reason was the bundle
rather than drift and it went out with the round before.
This commit is contained in:
KeelrLeek committed 2026-09-14 01:55:00 -05:00
1 parent 01bab007f7
commit 46cf6a98fe
15 files changed
+31 -39

No files matched your search

+1
View File
@@ -26,6 +26,7 @@ implements is `research/mobile-mockup/` (gitignored; see its README).
| `src/mobile/Sheet.svelte`, `HalfSheet.svelte`, `SyncRoom.svelte`, `Lane.svelte`, `Cover.svelte`, `Release.svelte` | the player (art / lyrics / queue faces, container transform from the mini bar, drags), the bottom sheets (track, record, sort, the page overflow, the sleep timer), the tap-to-sync room, the waveform lane, the two card pieces (a long press on a card is its sheet) | | `src/mobile/Sheet.svelte`, `HalfSheet.svelte`, `SyncRoom.svelte`, `Lane.svelte`, `Cover.svelte`, `Release.svelte` | the player (art / lyrics / queue faces, container transform from the mini bar, drags), the bottom sheets (track, record, sort, the page overflow, the sleep timer), the tap-to-sync room, the waveform lane, the two card pieces (a long press on a card is its sheet) |
| `src/mobile/screens/` | Home, Search (the library half: records and artists here, tracks through `library_track_search`), Library (albums / artists / tracks / playlists), Album, Artist (the library's shell at once, the catalog dissolving in: portrait, Popular, discography with what is here marked), CatalogAlbum (a catalog record, rows the library has play), Playlist, Community (sign in; rooms, chat, buddies, taste — the Soulseek side; the private layer waits), Room, Thread, You (Appearance switches, real) | | `src/mobile/screens/` | Home, Search (the library half: records and artists here, tracks through `library_track_search`), Library (albums / artists / tracks / playlists), Album, Artist (the library's shell at once, the catalog dissolving in: portrait, Popular, discography with what is here marked), CatalogAlbum (a catalog record, rows the library has play), Playlist, Community (sign in; rooms, chat, buddies, taste — the Soulseek side; the private layer waits), Room, Thread, You (Appearance switches, real) |
| `src/mobile/prefs.svelte.js` | Appearance, kept in localStorage: ambience, accent from artwork, motion | | `src/mobile/prefs.svelte.js` | Appearance, kept in localStorage: ambience, accent from artwork, motion |
| `src/mobile/util.js` | what every screen shares and no screen should write twice: the formatters (`mmss` / `longDur` / `stamp` for a length in seconds, in words and in milliseconds; `ago` / `clock`; `fmtBytes` / `fmtCount`; `fmtOf` / `albumFmt`), the name and folder readers a peer's file needs, the `drag` action, and `devBuild` |
| `src-tauri/tauri.android.conf.json` | merged over `tauri.conf.json` on Android: one window at `mobile.html`, no desktop resources, `minSdkVersion` 26 | | `src-tauri/tauri.android.conf.json` | merged over `tauri.conf.json` on Android: one window at `mobile.html`, no desktop resources, `minSdkVersion` 26 |
| `src-tauri/gen/android/` | the generated Gradle project (`npx tauri android init`); `minSdk = 26`. Gitignored | | `src-tauri/gen/android/` | the generated Gradle project (`npx tauri android init`); `minSdk = 26`. Gitignored |
| `src-tauri/android/build.sh`, `deploy.sh` | the detour build (below) and build → install → launch → screenshot | | `src-tauri/android/build.sh`, `deploy.sh` | the detour build (below) and build → install → launch → screenshot |
+1 -1
View File
@@ -16,7 +16,7 @@
import { pls, bulk, bulkable, runBulk, coverFor, queueable, entryOf, playFrom, shufflePlay, togglePin, renamePlaylist, describePlaylist, duplicatePlaylist, deletePlaylist, removeItems, addEntries, createPlaylist, moveItem, coverFromPhoto, coverClear } from "./playlists.svelte.js"; import { pls, bulk, bulkable, runBulk, coverFor, queueable, entryOf, playFrom, shufflePlay, togglePin, renamePlaylist, describePlaylist, duplicatePlaylist, deletePlaylist, removeItems, addEntries, createPlaylist, moveItem, coverFromPhoto, coverClear } from "./playlists.svelte.js";
import { DOORS, start as startImport, pickImportFolder, startFolder } from "./importer.svelte.js"; import { DOORS, start as startImport, pickImportFolder, startFolder } from "./importer.svelte.js";
import { slsk, refreshRooms, disconnect, openThread, addBuddy, removeBuddy, leaveRoom } from "./slsk.svelte.js"; import { slsk, refreshRooms, disconnect, openThread, addBuddy, removeBuddy, leaveRoom } from "./slsk.svelte.js";
import { lib, albumById, albumsBy, tracksOf, loadLibrary, refreshPlaylists, refreshRecents } from "./lib.svelte.js"; import { lib, albumById, tracksOf, loadLibrary, refreshPlaylists } from "./lib.svelte.js";
import { lyr, nudge, searchCandidates, closeCandidates, chooseCandidate, forgetLyrics, pasteLyrics, setAutoSync, openTapSync, tapProgress, reduceVocals, backToOriginal } from "./lyrics.svelte.js"; import { lyr, nudge, searchCandidates, closeCandidates, chooseCandidate, forgetLyrics, pasteLyrics, setAutoSync, openTapSync, tapProgress, reduceVocals, backToOriginal } from "./lyrics.svelte.js";
import { art, wantArt } from "./art.svelte.js"; import { art, wantArt } from "./art.svelte.js";
import { metaOf, display, realName, setMeta, clearMeta } from "./contacts.svelte.js"; import { metaOf, display, realName, setMeta, clearMeta } from "./contacts.svelte.js";
-1
View File
@@ -13,7 +13,6 @@
import Lane from "./Lane.svelte"; import Lane from "./Lane.svelte";
import Cover from "./Cover.svelte"; import Cover from "./Cover.svelte";
import { art } from "./art.svelte.js"; import { art } from "./art.svelte.js";
import { albumsBy } from "./lib.svelte.js";
import { player, next, prev, toggle, seekFrac, seekSecs, playQueueAt, toggleShuffle, cycleRepeat } from "./player.svelte.js"; import { player, next, prev, toggle, seekFrac, seekSecs, playQueueAt, toggleShuffle, cycleRepeat } from "./player.svelte.js";
import { nav, closeSheet, morphSource, openHalf, openViewer, push } from "./nav.svelte.js"; import { nav, closeSheet, morphSource, openHalf, openViewer, push } from "./nav.svelte.js";
import { lyr, loadLyrics } from "./lyrics.svelte.js"; import { lyr, loadLyrics } from "./lyrics.svelte.js";
+1 -1
View File
@@ -724,7 +724,7 @@ function listenTick(pos) {
const title = t.title; const title = t.title;
const artist = (t.artist || player.album?.artist || "").trim(); const artist = (t.artist || player.album?.artist || "").trim();
if (!title || !artist) return; if (!title || !artist) return;
const key = artist + "" + title; const key = artist + "\u0000" + title;
if (session?.key !== key) { if (session?.key !== key) {
listenFlush(); listenFlush();
scheduleRecents(1500); scheduleRecents(1500);
+1 -2
View File
@@ -22,7 +22,7 @@
import { catalog, lookupArtist, artistKey, fans, yearOf, looseTitle, loadExtras, loadPopular, loadHeroSeed, loadOverview, picture } from "../catalog.svelte.js"; import { catalog, lookupArtist, artistKey, fans, yearOf, looseTitle, loadExtras, loadPopular, loadHeroSeed, loadOverview, picture } from "../catalog.svelte.js";
import { resolver, streamSong, streamCatalogAlbum } from "../resolve.svelte.js"; import { resolver, streamSong, streamCatalogAlbum } from "../resolve.svelte.js";
import { discoTabs, activeYears, latestOf, timelineOf, isUpcoming, SOCIAL_LABEL } from "../disco.js"; import { discoTabs, activeYears, latestOf, timelineOf, isUpcoming, SOCIAL_LABEL } from "../disco.js";
import { shuffled, mmss, ago } from "../util.js"; import { shuffled, mmss, ago, fmtCount } from "../util.js";
/** `seed`: the search's own row (a CatalogArtist), when the page was /** `seed`: the search's own row (a CatalogArtist), when the page was
* opened from one — the identity is then known, no name search. */ * opened from one — the identity is then known, no name search. */
@@ -109,7 +109,6 @@
const popular = $derived((showAllPopular ? popularAll : popularAll.slice(0, 5)).map((t) => ({ t, hit: libByTitle.get(looseTitle(t.title)) || null }))); const popular = $derived((showAllPopular ? popularAll : popularAll.slice(0, 5)).map((t) => ({ t, hit: libByTitle.get(looseTitle(t.title)) || null })));
const popPeak = $derived(popularAll.reduce((m, t) => Math.max(m, t.listeners || 0), 0)); const popPeak = $derived(popularAll.reduce((m, t) => Math.max(m, t.listeners || 0), 0));
const share = (t) => (popPeak ? Math.max(0.04, (t.listeners || 0) / popPeak) : 0); const share = (t) => (popPeak ? Math.max(0.04, (t.listeners || 0) / popPeak) : 0);
const fmtCount = (n) => (!n ? "" : n >= 1e6 ? (n / 1e6).toFixed(1).replace(/\.0$/, "") + "M" : n >= 1e3 ? Math.round(n / 1e3) + "k" : String(n));
/* ── the discography, filed ──────────────────────────────────────── */ /* ── the discography, filed ──────────────────────────────────────── */
/* the overview's raw first page holds the discography up until the full /* the overview's raw first page holds the discography up until the full
+1 -2
View File
@@ -16,7 +16,7 @@
import { resolver, streamSong, streamCatalogAlbum, saveCatalogAlbum } from "../resolve.svelte.js"; import { resolver, streamSong, streamCatalogAlbum, saveCatalogAlbum } from "../resolve.svelte.js";
import { releaseFamilies, editionLabel, splitDescription } from "../../lib/editions.js"; import { releaseFamilies, editionLabel, splitDescription } from "../../lib/editions.js";
import { say } from "../trouble.js"; import { say } from "../trouble.js";
import { mmss, longDur } from "../util.js"; import { mmss, longDur, fmtCount } from "../util.js";
let { id, artist = "", title = "", screenEl = null } = $props(); let { id, artist = "", title = "", screenEl = null } = $props();
@@ -81,7 +81,6 @@
const peak = $derived(rows.reduce((m, t) => Math.max(m, listenersOf(t) || 0), 0)); const peak = $derived(rows.reduce((m, t) => Math.max(m, listenersOf(t) || 0), 0));
const showCount = $derived(peak > 0 && rows.length >= 3); const showCount = $derived(peak > 0 && rows.length >= 3);
const hasUnreleased = $derived(rows.some((t) => t.unreleased)); const hasUnreleased = $derived(rows.some((t) => t.unreleased));
const fmtCount = (n) => (!n ? "" : n >= 1e6 ? (n / 1e6).toFixed(1).replace(/\.0$/, "") + "M" : n >= 1e3 ? Math.round(n / 1e3) + "k" : String(n));
const fullDate = (d) => { const fullDate = (d) => {
const v = String(d || ""); const v = String(d || "");
if (v.length < 10) return yearOf(v); if (v.length < 10) return yearOf(v);
+1 -8
View File
@@ -7,20 +7,13 @@
import Icon from "../../components/Icon.svelte"; import Icon from "../../components/Icon.svelte";
import { importer, FORMAT_LABEL, kept, checkedFound, commit, cancel } from "../importer.svelte.js"; import { importer, FORMAT_LABEL, kept, checkedFound, commit, cancel } from "../importer.svelte.js";
import { openHalf, back, replace } from "../nav.svelte.js"; import { openHalf, back, replace } from "../nav.svelte.js";
import { mmss } from "../util.js"; import { mmss, stamp } from "../util.js";
const w = $derived(importer.draft); const w = $derived(importer.draft);
const rows = $derived(kept()); const rows = $derived(kept());
const n = $derived(rows.length); const n = $derived(rows.length);
const found = $derived(checkedFound()); const found = $derived(checkedFound());
function stamp(ms) {
const s = Math.floor((ms || 0) / 1000);
const h = Math.floor(s / 3600);
const m = Math.floor((s % 3600) / 60);
const sec = s % 60;
return (h ? h + ":" + String(m).padStart(2, "0") : String(m)) + ":" + String(sec).padStart(2, "0");
}
/* [tag class, label] — the desktop's evidence column */ /* [tag class, label] — the desktop's evidence column */
function verdict(e) { function verdict(e) {
if (e.resolution === "local") return ["ok", e.matched_by === "path" || e.matched_by === "file" ? "file" : e.matched_by === "relinked" ? "relinked" : "tags"]; if (e.resolution === "local") return ["ok", e.matched_by === "path" || e.matched_by === "file" ? "file" : e.matched_by === "relinked" ? "relinked" : "tags"];
-1
View File
@@ -13,7 +13,6 @@
import { push, go, patch, openHalf } from "../nav.svelte.js"; import { push, go, patch, openHalf } from "../nav.svelte.js";
import { looseTitle, yearOf } from "../catalog.svelte.js"; import { looseTitle, yearOf } from "../catalog.svelte.js";
import { isUpcoming } from "../disco.js"; import { isUpcoming } from "../disco.js";
import { longDur } from "../util.js";
let { entry, screenEl = null } = $props(); let { entry, screenEl = null } = $props();
+1 -8
View File
@@ -15,7 +15,7 @@
import { openHalf } from "../nav.svelte.js"; import { openHalf } from "../nav.svelte.js";
import { pls, bulk, cancelBulk, dismissBulk, coverFor, isSetlist, queueable, unresolvedCount, missingCount, totalSecs, playFrom, shufflePlay, rowClick } from "../playlists.svelte.js"; import { pls, bulk, cancelBulk, dismissBulk, coverFor, isSetlist, queueable, unresolvedCount, missingCount, totalSecs, playFrom, shufflePlay, rowClick } from "../playlists.svelte.js";
import { say } from "../trouble.js"; import { say } from "../trouble.js";
import { mmss, longDur } from "../util.js"; import { mmss, longDur, stamp } from "../util.js";
let { id } = $props(); let { id } = $props();
@@ -63,13 +63,6 @@
const playable = $derived(items.filter(queueable).length); const playable = $derived(items.filter(queueable).length);
const nowItemId = $derived(player.queue[player.qi]?.plItemId ?? null); const nowItemId = $derived(player.queue[player.qi]?.plItemId ?? null);
function stamp(ms) {
const s = Math.floor((ms || 0) / 1000);
const h = Math.floor(s / 3600);
const m = Math.floor((s % 3600) / 60);
const sec = s % 60;
return (h ? h + ":" + String(m).padStart(2, "0") : String(m)) + ":" + String(sec).padStart(2, "0");
}
function sheet(item) { openHalf("pltrack", { playlist: pl, items, item }); } function sheet(item) { openHalf("pltrack", { playlist: pl, items, item }); }
function tap(item) { if (!rowClick(pl, items, item)) sheet(item); } function tap(item) { if (!rowClick(pl, items, item)) sheet(item); }
</script> </script>
+2 -2
View File
@@ -4,9 +4,9 @@
opens that person — and a composer that stays above the mini bar. */ opens that person — and a composer that stays above the mini bar. */
import { tick } from "svelte"; import { tick } from "svelte";
import Icon from "../../components/Icon.svelte"; import Icon from "../../components/Icon.svelte";
import { slsk, sayRoom, leaveRoom, openThread, addBuddy } from "../slsk.svelte.js"; import { slsk, sayRoom, openThread } from "../slsk.svelte.js";
import { display, realName } from "../contacts.svelte.js"; import { display, realName } from "../contacts.svelte.js";
import { push, back, openHalf } from "../nav.svelte.js"; import { push, openHalf } from "../nav.svelte.js";
import { player } from "../player.svelte.js"; import { player } from "../player.svelte.js";
import { clock } from "../util.js"; import { clock } from "../util.js";
+1 -1
View File
@@ -22,7 +22,7 @@
import { catalog, artistKey, loadProfile, seedFor, lookupArtist, yearOf, fans, picture } from "../catalog.svelte.js"; import { catalog, artistKey, loadProfile, seedFor, lookupArtist, yearOf, fans, picture } from "../catalog.svelte.js";
import { latestOf } from "../disco.js"; import { latestOf } from "../disco.js";
import { say } from "../trouble.js"; import { say } from "../trouble.js";
import { mmss, fmtBytes, titleFromName, folderMeta } from "../util.js"; import { mmss, fmtBytes, titleFromName } from "../util.js";
let { entry, screenEl = null } = $props(); let { entry, screenEl = null } = $props();
+1 -1
View File
@@ -4,7 +4,7 @@
cancels it, finished rows clear together. The desktop's hub list. */ cancels it, finished rows clear together. The desktop's hub list. */
import Icon from "../../components/Icon.svelte"; import Icon from "../../components/Icon.svelte";
import Identicon from "../../components/Identicon.svelte"; import Identicon from "../../components/Identicon.svelte";
import { transfers, DONE, list, active, clearFinished, cancel, cancelAll, stateLine } from "../transfers.svelte.js"; import { DONE, list, active, clearFinished, cancel, cancelAll, stateLine } from "../transfers.svelte.js";
import { fmtBytes } from "../util.js"; import { fmtBytes } from "../util.js";
const rows = $derived(list()); const rows = $derived(list());
+5 -2
View File
@@ -164,7 +164,6 @@ html[data-material="solid"] .gbar { background: rgba(24, 24, 24, 0.94); backdrop
.iconbtn.sm { width: 38px; height: 38px; } .iconbtn.sm { width: 38px; height: 38px; }
.iconbtn.muted { color: var(--text-secondary); } .iconbtn.muted { color: var(--text-secondary); }
.iconbtn.on { color: var(--accent-hover); } .iconbtn.on { color: var(--accent-hover); }
.iconbtn.liked { color: var(--accent); }
/* ── large page header (the un-scrolled state) ────────────────────────── */ /* ── large page header (the un-scrolled state) ────────────────────────── */
.phead { padding: 14px var(--pad) 6px; position: relative; z-index: 2; } .phead { padding: 14px var(--pad) 6px; position: relative; z-index: 2; }
@@ -440,7 +439,6 @@ html[data-material="solid"] .cover-wrap { background: var(--bg-elevated); }
/* ── cards / groups ───────────────────────────────────────────────────── */ /* ── cards / groups ───────────────────────────────────────────────────── */
.card { background: var(--surface); box-shadow: var(--ring); border-radius: var(--r-card); overflow: hidden; } .card { background: var(--surface); box-shadow: var(--ring); border-radius: var(--r-card); overflow: hidden; }
.cardpad { padding: 14px; }
.group { margin: 0 var(--pad) 14px; border-radius: var(--r-card); overflow: hidden; background: var(--surface); box-shadow: var(--ring); } .group { margin: 0 var(--pad) 14px; border-radius: var(--r-card); overflow: hidden; background: var(--surface); box-shadow: var(--ring); }
html[data-material="solid"] .card, html[data-material="solid"] .group { background: var(--bg-elevated); box-shadow: none; } html[data-material="solid"] .card, html[data-material="solid"] .group { background: var(--bg-elevated); box-shadow: none; }
.group .row { padding: 11px 14px; } .group .row { padding: 11px 14px; }
@@ -1218,6 +1216,11 @@ html[data-material="solid"] .peer { background: var(--bg-elevated); box-shadow:
.countline { display: flex; align-items: center; justify-content: space-between; padding: 0 var(--pad) 10px; } .countline { display: flex; align-items: center; justify-content: space-between; padding: 0 var(--pad) 10px; }
/* a hero bleeds up under the status bar; the scrim keeps the chrome legible */ /* a hero bleeds up under the status bar; the scrim keeps the chrome legible */
.screen.has-hero { padding-top: 0; } .screen.has-hero { padding-top: 0; }
/* not a stray: `.lane` is width: 100%, and the mini bar's lane is ALSO
positioned with both left: 16px and right: 16px — an over-constrained
absolute box drops `right`, so without this it comes out 32px wider than
the strip it was cut for (395 where 363 was asked for, at 411), the bars
sit off-centre and the tail runs under the bar's own overflow: hidden. */
.mini .mini-lane { width: auto; } .mini .mini-lane { width: auto; }
/* ── screen transitions ─────────────────────────────────────────────────── /* ── screen transitions ───────────────────────────────────────────────────
-6
View File
@@ -81,12 +81,6 @@ export async function disconnect() {
slsk.joined = {}; slsk.joined = {};
} }
export async function forgetAccount() {
try { await api.forgetCredentials(); } catch { /* fine */ }
slsk.savedUser = "";
slsk.hasPassword = false;
}
let roomsTimer = null; let roomsTimer = null;
export function refreshRooms() { export function refreshRooms() {
slsk.roomsLate = false; slsk.roomsLate = false;
+15 -3
View File
@@ -15,6 +15,17 @@ export function longDur(secs) {
return m + " min"; return m + " min";
} }
/** "4:31", "1:02:07" — a length or a position in MILLISECONDS, which is how
* a playlist row's cue and an import draft's track both carry one. Hours
* only when there are hours; `mmss` above is the seconds-in version. */
export function stamp(ms) {
const s = Math.floor((ms || 0) / 1000);
const h = Math.floor(s / 3600);
const m = Math.floor((s % 3600) / 60);
const sec = s % 60;
return (h ? h + ":" + String(m).padStart(2, "0") : String(m)) + ":" + String(sec).padStart(2, "0");
}
/** A relative day, against the wall clock. `ts` is unix seconds. */ /** A relative day, against the wall clock. `ts` is unix seconds. */
export function ago(ts) { export function ago(ts) {
const d = Math.floor((Date.now() / 1000 - ts) / 86400); const d = Math.floor((Date.now() / 1000 - ts) / 86400);
@@ -66,6 +77,10 @@ export function fmtBytes(n) {
return Math.round(n / 1024) + " kB"; return Math.round(n / 1024) + " kB";
} }
/** "1.2M", "430k" — a listener or play count beside a row, the desktop's
* short form. Nothing under a thousand is abbreviated. */
export const fmtCount = (n) => (!n ? "" : n >= 1e6 ? (n / 1e6).toFixed(1).replace(/\.0$/, "") + "M" : n >= 1e3 ? Math.round(n / 1e3) + "k" : String(n));
export function basename(p) { export function basename(p) {
if (!p) return ""; if (!p) return "";
const i = Math.max(p.lastIndexOf("/"), p.lastIndexOf("\\")); const i = Math.max(p.lastIndexOf("/"), p.lastIndexOf("\\"));
@@ -93,9 +108,6 @@ export function fmtOf(t) {
if (t.bitrate_kbps) return name + " " + t.bitrate_kbps; if (t.bitrate_kbps) return name + " " + t.bitrate_kbps;
return name; return name;
} }
export function isLossless(fmt) {
return /^(FLAC|WAV|AIFF?|APE|WV|ALAC|DSF|DFF)\b/.test(fmt || "");
}
/** The format most of an album's tracks share. */ /** The format most of an album's tracks share. */
export function albumFmt(tracks) { export function albumFmt(tracks) {
const count = new Map(); const count = new Map();