android: the phone, on the real core — the mockup shell, playback, the media card, the catalog, the resolver, Save, the dev flavour
The same Tauri 2 project builds for Android: mobile.html as a third Vite
entry (src/mobile/, the mockup on real data), tauri.android.conf.json, the
mobile stubs (updater, lyric alignment, vocal separation), rustls reqwest
on Android, the AAudio buffer, and a hand-driven detour build
(src-tauri/android/{env,build,deploy,dev}.sh) while Developer Mode is off.
ANDROID.md is the recipe.
Playback: the media-session plugin crate (lock-screen card, MediaStyle
notification, audio focus, headset buttons, a foreground service that
promotes itself first — Android 14 kills the app otherwise), session
restore across reload and restart, solid chrome after measuring the
backdrop-filter lag.
The catalog: Search is the desktop's top bar (the top-result rule, artist
cards), the artist page and the record page are the desktop's, Home is the
desktop's landing page, Gaps per followed shelf. Playing goes through the
desktop's own source rules, moved verbatim from state.svelte.js into
src/lib/sources.js so both apps share one resolver; the phone readies the
next three tracks and crosses boundaries gapless. Save fetches a record or
a track into the library by the download rules; Transfers shows the
burst. The raw Soulseek search stays, offered on request, a peer's copy
streaming with the lane rising as the bytes arrive. Community is the
Soulseek side (rooms, chat, buddies); the private layer waits.
The opener and storage plugins (social links, the picked library folder,
all-files access), the volume normalization switch, and the harness:
tests/mobile/smoke.mjs walks the mock (a fake network behind
src/lib/events.js) and the real phone over CDP, perf.mjs measures frames.
This commit is contained in:
1 parent
972dd103cb
commit
bb828ff2dc
102 files changed
+12258
-830
No files matched your search
@@ -0,0 +1,142 @@
|
||||
/* ═══════════════════════════════════════════════════════════════════════════
|
||||
SoulStream on Android — foundations.
|
||||
|
||||
The palette is the desktop palette, unchanged (global.css / the mockup's
|
||||
tokens.css): same greys, same accent, same surface ladder. What is new is
|
||||
spatial — radii open up, type goes up a step, every tappable thing is 48dp
|
||||
— and the material: the page ground is the artwork in play, blurred.
|
||||
Text is authored in px; the Android WebView applies the system font scale
|
||||
to CSS px on its own, so there is no sp arithmetic here.
|
||||
═══════════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
/* the desktop's face, from the same files */
|
||||
@font-face { font-family: "Nunito"; font-style: normal; font-weight: 400; font-display: swap; src: url("/assets/fonts/nunito-400.woff2") format("woff2"); }
|
||||
@font-face { font-family: "Nunito"; font-style: normal; font-weight: 600; font-display: swap; src: url("/assets/fonts/nunito-600.woff2") format("woff2"); }
|
||||
@font-face { font-family: "Nunito"; font-style: normal; font-weight: 700; font-display: swap; src: url("/assets/fonts/nunito-700.woff2") format("woff2"); }
|
||||
|
||||
/* Typed registration so the per-artwork accent can TRANSITION. The desktop
|
||||
crossfades its accent on a rAF loop (state.svelte.js); on the phone the
|
||||
compositor does it — see the transition on html below. */
|
||||
@property --accent { syntax: "<color>"; inherits: true; initial-value: #1f9ad6; }
|
||||
@property --accent-hover { syntax: "<color>"; inherits: true; initial-value: #41b7ee; }
|
||||
|
||||
:root {
|
||||
--bg-base: #121212;
|
||||
--bg-elevated: #181818;
|
||||
--bg-highlight: #242424;
|
||||
--bg-press: #2a2a2a;
|
||||
--text-primary: #ffffff;
|
||||
--text-secondary: #b3b3b3;
|
||||
--text-muted: #7a7a7a;
|
||||
--accent: #1f9ad6;
|
||||
--accent-hover: #41b7ee;
|
||||
--accent-soft: color-mix(in srgb, var(--accent) 15%, transparent);
|
||||
--border: #2a2a2a;
|
||||
|
||||
/* the surface ladder: the same three alphas the desktop's glass mode
|
||||
uses, because the ground under a surface is the ambience here */
|
||||
--surface: rgba(255, 255, 255, 0.03);
|
||||
--hover: rgba(255, 255, 255, 0.08);
|
||||
--press: rgba(255, 255, 255, 0.11);
|
||||
--hairline: rgba(255, 255, 255, 0.1);
|
||||
--ring: inset 0 0 0 1px rgba(255, 255, 255, 0.06);
|
||||
--well: rgba(0, 0, 0, 0.18);
|
||||
|
||||
--r-art: 10px;
|
||||
--r-card: 14px;
|
||||
--r-sheet: 26px;
|
||||
--r-pill: 999px;
|
||||
--pad: 18px;
|
||||
--gap: 10px;
|
||||
|
||||
--nav-h: 60px;
|
||||
--mini-h: 70px; /* a 62dp row over a 16dp lane strip */
|
||||
--bar-h: 52px; /* the glass bar */
|
||||
--tap: 48px;
|
||||
|
||||
/* Android's own insets: the status bar above, the gesture handle below. */
|
||||
--inset-top: env(safe-area-inset-top, 0px);
|
||||
--inset-bottom: env(safe-area-inset-bottom, 0px);
|
||||
--top-h: calc(var(--bar-h) + var(--inset-top));
|
||||
|
||||
/* ── motion: real springs, sampled ────────────────────────────────────
|
||||
Material 3 Expressive specifies motion as springs; CSS takes a sampled
|
||||
curve through linear(). These are the mockup's generated tokens
|
||||
(research/mobile-mockup/tools/springs.mjs): the spatial ones overshoot
|
||||
because real objects do, the effects ones never do. */
|
||||
--sp-spatial-fast: linear(0, 0.0512, 0.176, 0.3375, 0.5079, 0.6679, 0.8057, 0.9155, 0.9963, 1.0502, 1.081, 1.0937, 1.0931, 1.0838, 1.0696, 1.0535, 1.0378, 1.0237, 1.0121, 1.0033, 0.9971, 0.9933, 0.9915, 0.991, 0.9916, 0.9928, 0.9943, 0.9958, 0.9972, 0.9984, 0.9994, 1, 1.0005, 1.0008, 1);
|
||||
--spd-spatial-fast: 413ms;
|
||||
--sp-spatial-default: linear(0, 0.0329, 0.1132, 0.2191, 0.335, 0.4503, 0.558, 0.6543, 0.7374, 0.8069, 0.8634, 0.9081, 0.9425, 0.9681, 0.9865, 0.9992, 1.0074, 1.0122, 1.0145, 1.0152, 1.0146, 1.0134, 1.0118, 1.01, 1.0083, 1.0067, 1.0052, 1.0039, 1.0029, 1.002, 1.0014, 1.0009, 1.0005, 1.0002, 1);
|
||||
--spd-spatial-default: 482ms;
|
||||
--sp-spatial-slow: linear(0, 0.0309, 0.1068, 0.2078, 0.3194, 0.4314, 0.5372, 0.6329, 0.7165, 0.7874, 0.8459, 0.893, 0.9298, 0.9579, 0.9787, 0.9935, 1.0035, 1.0098, 1.0134, 1.0149, 1.0151, 1.0143, 1.013, 1.0114, 1.0097, 1.008, 1.0065, 1.0051, 1.0039, 1.0029, 1.002, 1.0014, 1.0009, 1.0005, 1);
|
||||
--spd-spatial-slow: 641ms;
|
||||
--sp-effects-fast: linear(0, 0.0527, 0.1672, 0.3005, 0.4304, 0.5464, 0.6448, 0.7255, 0.7901, 0.8409, 0.8803, 0.9105, 0.9334, 0.9507, 0.9637, 0.9733, 0.9804, 0.9857, 0.9896, 0.9924, 0.9945, 0.996, 0.9971, 0.9979, 0.9985, 0.9989, 0.9992, 0.9994, 0.9996, 0.9997, 0.9998, 0.9999, 0.9999, 0.9999, 1);
|
||||
--spd-effects-fast: 202ms;
|
||||
--sp-effects-default: linear(0, 0.0436, 0.1412, 0.2593, 0.3786, 0.4892, 0.5866, 0.6696, 0.7385, 0.7947, 0.84, 0.876, 0.9044, 0.9266, 0.9439, 0.9572, 0.9675, 0.9754, 0.9814, 0.986, 0.9894, 0.9921, 0.9941, 0.9956, 0.9967, 0.9975, 0.9982, 0.9986, 0.999, 0.9992, 0.9994, 0.9996, 0.9997, 0.9998, 1);
|
||||
--spd-effects-default: 280ms;
|
||||
--sp-effects-slow: linear(0, 0.0388, 0.1273, 0.2365, 0.3492, 0.4558, 0.5517, 0.635, 0.7056, 0.7644, 0.8128, 0.8521, 0.8837, 0.9089, 0.929, 0.9448, 0.9572, 0.9669, 0.9745, 0.9804, 0.9849, 0.9885, 0.9912, 0.9933, 0.9949, 0.9961, 0.997, 0.9977, 0.9983, 0.9987, 0.999, 0.9993, 0.9994, 0.9996, 1);
|
||||
--spd-effects-slow: 371ms;
|
||||
|
||||
--m-fast: var(--spd-effects-fast) var(--sp-effects-fast);
|
||||
--m-base: var(--spd-spatial-fast) var(--sp-spatial-fast);
|
||||
--m-sheet: var(--spd-spatial-default) var(--sp-spatial-default);
|
||||
--m-spring: var(--spd-spatial-fast) var(--sp-spatial-fast);
|
||||
--art-grow: 1.035;
|
||||
|
||||
--ambience: 0.55; /* how much of the artwork the page ground admits */
|
||||
--grain: 0.05;
|
||||
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
/* The registered property only makes --accent animatable; this is the
|
||||
crossfade. Slow on purpose — it is the app changing colour, and it should
|
||||
read as a change rather than a flicker. */
|
||||
html {
|
||||
transition:
|
||||
--accent var(--spd-effects-slow) linear,
|
||||
--accent-hover var(--spd-effects-slow) linear;
|
||||
}
|
||||
/* Appearance → Accent from artwork OFF: the brand blue everywhere */
|
||||
html[data-accent="brand"] { --accent: #1f9ad6 !important; --accent-hover: #41b7ee !important; }
|
||||
/* Appearance → Ambience OFF: the honest floor, the desktop's measured greys */
|
||||
html[data-material="solid"] {
|
||||
--ambience: 0;
|
||||
--surface: var(--bg-elevated);
|
||||
--hover: var(--bg-highlight);
|
||||
--press: var(--bg-press);
|
||||
--hairline: rgba(255, 255, 255, 0.07);
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
background: var(--bg-base);
|
||||
color: var(--text-primary);
|
||||
font-family: "Nunito", -apple-system, "Segoe UI", Roboto, sans-serif;
|
||||
font-size: 15px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
overscroll-behavior: none;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
#mobile { height: 100%; }
|
||||
|
||||
/* :where() so the reset carries zero specificity and never beats a
|
||||
component rule (the mockup lost every accent-filled control to this once) */
|
||||
:where(button) {
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
background: none;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
cursor: default;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
:where(input) { font: inherit; color: inherit; }
|
||||
img { display: block; }
|
||||
Reference in new issue
Block a user