Everything written this session that has not been through the device: rounds 43 to 48 (each patch adversarially verified, 43's transient-notice expectation fixed), a partial round 49, the pipeline scripts, the Android Auto design and the icon comparison mock. Also the two harness changes that were never A/B'd — adaptive waits, which may only shorten a declared wait and never lengthen it, and a ship that runs the mock walk and the deploy at the same time. The A/B and its 255-step baseline are in handoff/tools; main stays at what the phone has actually walked.
109 lines
5.7 KiB
Plaintext
109 lines
5.7 KiB
Plaintext
<script>
|
|
/* Diagnostics — the desktop's debug overlay (Ctrl+Shift+D) as a screen,
|
|
on the dev flavour only: what this process costs the phone, what the
|
|
network is moving, the track playing and its loudness correction, the
|
|
tracks the pre-buffer is fetching behind it, and how big the play
|
|
history has grown. Polled once a second while the screen is open and
|
|
never otherwise, the way the overlay does it. The GPU counters are
|
|
Windows PDH and the private commit is a Win32 call, so neither row is
|
|
here — on the phone they would read nothing at all. */
|
|
import Icon from "../../components/Icon.svelte";
|
|
import * as api from "../../lib/api.js";
|
|
import { fmtBytes, basename } from "../util.js";
|
|
|
|
let stats = $state(null);
|
|
let plays = $state(null);
|
|
let failed = $state("");
|
|
|
|
/* nothing reactive is READ in here: the effect runs once when the screen
|
|
mounts and its cleanup when the screen leaves. An effect that reads
|
|
what it writes takes its whole subtree down with it. */
|
|
$effect(() => {
|
|
let live = true;
|
|
const tick = async () => {
|
|
try {
|
|
const s = await api.debugStats();
|
|
if (live) { stats = s; failed = ""; }
|
|
} catch (e) {
|
|
if (live) failed = String(e);
|
|
}
|
|
};
|
|
void tick();
|
|
api.playRowCount().then((n) => { if (live) plays = n; }, () => {});
|
|
const id = setInterval(tick, 1000);
|
|
return () => { live = false; clearInterval(id); };
|
|
});
|
|
|
|
const pct = (v) => (v == null ? "—" : v.toFixed(1) + "%");
|
|
const size = (n) => (n ? fmtBytes(n) : "—");
|
|
const rate = (n) => (n ? fmtBytes(n) + "/s" : "—");
|
|
const db = (v) => (v == null ? "—" : (v > 0 ? "+" : "") + v.toFixed(1) + " dB");
|
|
const upfor = (s) => {
|
|
if (s == null) return "—";
|
|
const h = Math.floor(s / 3600), m = Math.floor((s % 3600) / 60);
|
|
return h ? h + " hr " + m + " min" : m ? m + " min" : s + " s";
|
|
};
|
|
const share = (p) => (p.total ? Math.max(0.02, Math.min(1, p.received / p.total)) : 0);
|
|
</script>
|
|
|
|
<div class="phead"><div class="row"><h1>Diagnostics</h1></div></div>
|
|
<div class="sec-note">The desktop's debug overlay, on the dev flavour. Polled once a second while this screen is open.</div>
|
|
|
|
{#if failed}
|
|
<div class="stub">
|
|
<b>No diagnostics.</b>
|
|
{failed}
|
|
</div>
|
|
{/if}
|
|
|
|
<div class="sheet-sec">This process</div>
|
|
<div class="group diag">
|
|
<div class="kv"><span class="k">App CPU</span><span class="v">{pct(stats?.cpuProcess)}</span></div>
|
|
<div class="kv"><span class="k">System CPU</span><span class="v">{pct(stats?.cpuSystem)}</span></div>
|
|
<div class="kv"><span class="k">Memory</span><span class="v">{size(stats?.memWorkingSet)}</span></div>
|
|
<div class="kv"><span class="k">System memory</span><span class="v dim">{size(stats?.memSystemUsed)} of {size(stats?.memSystemTotal)}</span></div>
|
|
<div class="kv"><span class="k">Processes</span><span class="v">{stats?.processCount ?? "—"}</span></div>
|
|
<div class="kv"><span class="k">Up</span><span class="v">{upfor(stats?.uptimeSecs)}</span></div>
|
|
</div>
|
|
<div class="sec-note">Processes counts this app's own tree. Android runs the WebView in a system process of its own, so it is outside these numbers; on the desktop the whole WebView2 family is inside them.</div>
|
|
|
|
<div class="sheet-sec">Network</div>
|
|
<div class="group diag">
|
|
<div class="kv"><span class="k">Down</span><span class="v">{rate(stats?.netRxBps)}</span></div>
|
|
<div class="kv"><span class="k">Up</span><span class="v">{rate(stats?.netTxBps)}</span></div>
|
|
</div>
|
|
<div class="sec-note">Every interface on the phone, not this app alone.</div>
|
|
|
|
<div class="sheet-sec">The track playing</div>
|
|
<div class="group diag">
|
|
<div class="kv"><span class="k">Stream</span><span class="v">{!stats ? "—" : stats.dlTotal ? size(stats.dlReceived) + " of " + size(stats.dlTotal) + (stats.dlDone ? " · done" : "") : "nothing streaming"}</span></div>
|
|
<div class="kv"><span class="k">Normalization</span><span class="v">{!stats ? "—" : stats.normalizationEnabled ? "on · " + (stats.targetLufs ?? -14) + " LUFS" : "off"}</span></div>
|
|
<div class="kv"><span class="k">Measured</span><span class="v dim">{stats?.loudness?.lufs != null ? stats.loudness.lufs.toFixed(1) + " LUFS" + (stats.loudness.analyzing ? " · measuring" : "") : "—"}</span></div>
|
|
<div class="kv"><span class="k">Gain</span><span class="v">{db(stats?.normNowDb)}{stats && stats.normNowDb != null && stats.normNowDb !== stats.normTargetDb ? " → " + db(stats.normTargetDb) : ""}</span></div>
|
|
</div>
|
|
|
|
<div class="sheet-sec">On deck</div>
|
|
{#if stats?.preloadDl?.length}
|
|
<div class="group diag-deck">
|
|
{#each stats.preloadDl as p (p.username + p.path)}
|
|
<div class="row">
|
|
<span class="lead"><Icon name="download" size={20} /></span>
|
|
<span class="b">
|
|
<span class="t">{basename(p.path)}</span>
|
|
<span class="m">{p.username} · {p.state}{p.place != null ? " · queue " + p.place : ""}{p.speedBps ? " · " + fmtBytes(p.speedBps) + "/s" : ""}</span>
|
|
{#if p.total}<span class="lbar"><i style="transform: scaleX({share(p).toFixed(3)})"></i><em>{fmtBytes(p.received)}</em></span>{/if}
|
|
</span>
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
{:else}
|
|
<div class="sec-note">Nothing pre-buffering. The player readies the next three tracks while one plays.</div>
|
|
{/if}
|
|
|
|
<div class="sheet-sec">Library</div>
|
|
<div class="group diag">
|
|
<div class="kv"><span class="k">Play rows</span><span class="v">{plays != null ? plays.toLocaleString() : "—"}</span></div>
|
|
</div>
|
|
|
|
<div class="endpad"></div>
|