Improve overlay readability and swap lyrics/album layout

Now-playing ticker gets a dark blurred pill, lyrics get a fading dark
veil plus dark text shadows so both stay readable over the Milky Way.
Lyrics move back to the left, album art to the right. PLANET_SPECS no
longer hard-crashes the whole script if Astronomy Engine fails to load,
and CLAUDE.md catches up with the planetarium features.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015HtPHsTnDcLVSCiF5FGHZj
This commit is contained in:
vini 2026-07-19 21:06:36 -03:00
parent f3019d044f
commit f5e1949c91
2 changed files with 37 additions and 11 deletions

View file

@ -34,8 +34,14 @@ plugin/
main.qml # Plasma WallpaperItem — hosts everything main.qml # Plasma WallpaperItem — hosts everything
dancer.html # Self-contained animation (HTML + CSS + JS) dancer.html # Self-contained animation (HTML + CSS + JS)
starcatalog.js # Generated star data (Yale BSC) — see tools/build_catalog.py starcatalog.js # Generated star data (Yale BSC) — see tools/build_catalog.py
constellations.js # Generated zodiac lines/anchors — see tools/build_constellations.py
constellation-art/ # 12 zodiac PNGs (black background → alpha at load)
milkyway.jpg # Gaia all-sky photo, plate carrée — see tools/build_skymap.py
astronomy.browser.min.js # Astronomy Engine (MIT) — Moon/planet ephemerides
tools/ tools/
build_catalog.py # Regenerates starcatalog.js from the Yale Bright Star Catalog build_catalog.py # Regenerates starcatalog.js from the Yale Bright Star Catalog
build_constellations.py # Regenerates constellations.js (Stellarium Modern + d3-celestial)
build_skymap.py # Reprojects ESA Gaia all-sky map (Hammer → plate carrée)
``` ```
### main.qml — the Plasma host ### main.qml — the Plasma host
@ -50,13 +56,18 @@ Pure HTML/CSS/JS, no external dependencies. All visuals are self-contained:
| Layer | Technique | | Layer | Technique |
|---|---| |---|---|
| Realistic sky (stars + Milky Way) | `<canvas>`; real-time alt-az projection of `starcatalog.js` (2,887 Yale BSC stars) + seeded procedural Milky Way for the configured lat/long (default Toledo-PR). Static layer pre-rendered offscreen, bright stars twinkle per frame; reprojects every 30 s. Debug: `window.__skyTimeOffset` (ms) fast-forwards the sky | | Realistic sky (stars) | `<canvas>`; real-time alt-az projection of `starcatalog.js` (8,404 Yale BSC stars, V ≤ 6.5) for the configured lat/long (default Toledo-PR). Static layer pre-rendered offscreen with triple-buffer + 1.4 s crossfade between reprojections (every 30 s; 4 s during timelapse/camera rotation); bright stars twinkle per frame. Debug: `window.__skyTimeOffset` (ms) fast-forwards the sky |
| Milky Way | Per-pixel sampling of `milkyway.jpg` (Gaia photo, full res): `buildMwGrid` caches viewport unit vectors + extinction/glare, `renderMilkyWay` applies only the sidereal rotation (camera azimuth folded into the equatorial→galactic matrix) and stylized "development" (deep blacks, saturation, cool shadows). Decoded texture stays resident (~75 MB) |
| Moon & planets | Astronomy Engine topocentric ephemerides: Moon with real phase texture + bright-limb angle, 7 planets with magnitude-scaled dots (labels only in constellation mode) |
| Zodiac constellations | Stellarium Modern lines + anchored PNG art (similarity transform, no shear); toggled via `/tmp` file or `C` key |
| Nebula blobs, grid floor, horizon | CSS animations | | Nebula blobs, grid floor, horizon | CSS animations |
| DANCEBOT-9000 robot | Pure CSS (divs + `@keyframes`) | | DANCEBOT-9000 robot | Pure CSS (divs + `@keyframes`) |
| Chest EQ bars & VU meter | JS-driven `requestAnimationFrame` | | Chest EQ bars & VU meter | JS-driven `requestAnimationFrame` |
| Music ticker | DOM text set by `updateMusic(artist, title)` (called from QML) | | Music ticker / album art / karaoke lyrics | DOM text set by `updateMusic(artist, title)` (called from QML); lyrics left, album art right, ticker centered — all with dark veils/shadows for contrast over the Milky Way |
| Dance style cycling | `data-dance` attribute on `<body>`, toggled by JS every 8 s | | Dance style cycling | `data-dance` attribute on `<body>`, toggled by JS every 8 s |
**Runtime controls** (state files in `/tmp/skeledance-*`, polled every 800 ms by dancer.html; each has a root-level helper script): `toggle-balada.sh`, `toggle-chuva.sh` (mutually exclusive modes), `toggle-constellations.sh`, `preview-constellation.sh <Vir|off>`, `set-camera-position.sh <norte|leste|sul|oeste|graus>`, `toggle-camera-rotation.sh` (360° pan in 5 min), `toggle-sky-timelapse.sh` (360× time). `test-modes.sh` reloads the plugin and cycles modes.
**Tempo anchor**: everything is keyed to 120 BPM (`BEAT_MS = 500 ms`). CSS animation durations for the robot parts use multiples/fractions of 0.5 s, 1 s, or 2 s. **Tempo anchor**: everything is keyed to 120 BPM (`BEAT_MS = 500 ms`). CSS animation durations for the robot parts use multiples/fractions of 0.5 s, 1 s, or 2 s.
**Dance styles** (cycle order): `bounce → wave → shuffle → moonwalk → spin`. CSS selectors like `[data-dance="spin"] .robot { ... }` override the default animations via `!important`. **Dance styles** (cycle order): `bounce → wave → shuffle → moonwalk → spin`. CSS selectors like `[data-dance="spin"] .robot { ... }` override the default animations via `!important`.

View file

@ -164,7 +164,7 @@ body.balada .nebula::after { opacity: 1; }
.album-art { .album-art {
position: absolute; position: absolute;
top: 4%; top: 4%;
left: 2%; right: 2%;
width: 330px; width: 330px;
height: 330px; height: 330px;
border-radius: 6px; border-radius: 6px;
@ -194,6 +194,12 @@ body.balada .nebula::after { opacity: 1; }
overflow: hidden; overflow: hidden;
opacity: 0; opacity: 0;
transition: opacity 0.8s, top 0.8s ease; transition: opacity 0.8s, top 0.8s ease;
/* legibilidade sobre a Via Láctea: pílula escura + desfoque do fundo */
padding: 5px 16px;
border-radius: 999px;
background: rgba(3, 8, 20, 0.55);
backdrop-filter: blur(6px);
border: 1px solid rgba(0, 200, 255, 0.14);
} }
body.balada .music-ticker { body.balada .music-ticker {
/* acima do VU meter: bottom 5% + 70px barras + 10px gap */ /* acima do VU meter: bottom 5% + 70px barras + 10px gap */
@ -219,7 +225,7 @@ body.balada .music-ticker {
color: #ffffff; color: #ffffff;
font-family: 'Courier New', monospace; font-family: 'Courier New', monospace;
font-size: clamp(11px, 1.2vw, 17px); font-size: clamp(11px, 1.2vw, 17px);
text-shadow: 0 0 8px rgba(0,200,255,0.7); text-shadow: 0 1px 3px rgba(0,0,0,0.9), 0 0 8px rgba(0,200,255,0.7);
white-space: nowrap; white-space: nowrap;
animation: musicScroll 18s linear infinite; animation: musicScroll 18s linear infinite;
} }
@ -254,14 +260,20 @@ body.balada .music-ticker {
============================================================ */ ============================================================ */
.lyric-display { .lyric-display {
position: absolute; position: absolute;
right: 2%; left: 2%;
top: 4%; top: 4%;
width: 38%; width: 38%;
height: 150px; height: 150px;
padding: 0 14px;
overflow: hidden; overflow: hidden;
pointer-events: none; pointer-events: none;
opacity: 0; opacity: 0;
transition: opacity 0.5s ease; transition: opacity 0.5s ease;
/* véu escuro atrás da letra p/ ler sobre a Via Láctea; a máscara vertical
também esmaece o véu, então as bordas continuam suaves */
background: linear-gradient(to right,
rgba(3, 8, 20, 0.58) 0%, rgba(3, 8, 20, 0.38) 70%, transparent 100%);
border-radius: 10px;
-webkit-mask-image: linear-gradient(to bottom, transparent 0%, black 15%, black 85%, transparent 100%); -webkit-mask-image: linear-gradient(to bottom, transparent 0%, black 15%, black 85%, transparent 100%);
mask-image: linear-gradient(to bottom, transparent 0%, black 15%, black 85%, transparent 100%); mask-image: linear-gradient(to bottom, transparent 0%, black 15%, black 85%, transparent 100%);
} }
@ -273,7 +285,7 @@ body.balada .music-ticker {
.lyric-ctx { .lyric-ctx {
display: block; display: block;
width: 100%; width: 100%;
text-align: right; /* letra ancorada à direita da tela */ text-align: left; /* letra ancorada à esquerda da tela */
font-family: 'Courier New', monospace; font-family: 'Courier New', monospace;
letter-spacing: 0.02em; letter-spacing: 0.02em;
white-space: normal; white-space: normal;
@ -1268,7 +1280,8 @@ const HORIZON_F = 0.585; // fração da altura da tela até o horizonte
const TWINKLE_MAG = 4.0; // até esta magnitude a estrela cintila; acima é estática const TWINKLE_MAG = 4.0; // até esta magnitude a estrela cintila; acima é estática
const DEG = Math.PI / 180; const DEG = Math.PI / 180;
window.__skyTimeOffset = 0; // debug: ms somados ao relógio p/ testar a rotação window.__skyTimeOffset = 0; // debug: ms somados ao relógio p/ testar a rotação
const PLANET_SPECS = [ /* Sem o Astronomy Engine o céu continua funcionando; só Lua/planetas somem */
const PLANET_SPECS = typeof Astronomy === 'undefined' ? [] : [
[Astronomy.Body.Mercury, 'Mercúrio', [190, 188, 182]], [Astronomy.Body.Mercury, 'Mercúrio', [190, 188, 182]],
[Astronomy.Body.Venus, 'Vênus', [255, 241, 204]], [Astronomy.Body.Venus, 'Vênus', [255, 241, 204]],
[Astronomy.Body.Mars, 'Marte', [238, 105, 70]], [Astronomy.Body.Mars, 'Marte', [238, 105, 70]],
@ -2430,12 +2443,14 @@ const ACTIVE_SHADOW =
'0 0 6px var(--theme-color,rgba(0,200,255,0.5)),' + '0 0 6px var(--theme-color,rgba(0,200,255,0.5)),' +
'0 2px 10px rgba(0,0,0,0.95)'; '0 2px 10px rgba(0,0,0,0.95)';
const CTX_SHADOW = '0 1px 3px rgba(0,0,0,0.95), 0 0 8px rgba(0,0,0,0.8)';
const VERSE_STYLES = [ const VERSE_STYLES = [
{ opacity:'0.2', fontSize:'clamp(11px,0.85vw,13px)', color:'#777', shadow:'' }, { opacity:'0.2', fontSize:'clamp(11px,0.85vw,13px)', color:'#777', shadow: CTX_SHADOW },
{ opacity:'0.48', fontSize:'clamp(13px,1.0vw,15px)', color:'#bbb', shadow:'' }, { opacity:'0.48', fontSize:'clamp(13px,1.0vw,15px)', color:'#bbb', shadow: CTX_SHADOW },
{ opacity:'1', fontSize:'clamp(16px,1.3vw,18px)', color:'#fff', shadow: ACTIVE_SHADOW }, { opacity:'1', fontSize:'clamp(16px,1.3vw,18px)', color:'#fff', shadow: ACTIVE_SHADOW },
{ opacity:'0.48', fontSize:'clamp(13px,1.0vw,15px)', color:'#bbb', shadow:'' }, { opacity:'0.48', fontSize:'clamp(13px,1.0vw,15px)', color:'#bbb', shadow: CTX_SHADOW },
{ opacity:'0.2', fontSize:'clamp(11px,0.85vw,13px)', color:'#777', shadow:'' }, { opacity:'0.2', fontSize:'clamp(11px,0.85vw,13px)', color:'#777', shadow: CTX_SHADOW },
]; ];
let verseEls = []; let verseEls = [];