feat(ui): carry lyrics translation state and render bilingual lines
LyricsLineItem gains translation-text (empty when the doc has none) and LyricsState the translation props (translation-available, show-translation, toggle-translation callback) per spec section C. LyricsLinesView renders the translation under its original line while show-translation is on — smaller (~72%) and dimmer, static text that follows the line; the karaoke fill stays keyed on the original timings/text. Secondary surfaces share the model, so the field flows through untouched. km
This commit is contained in:
2 files changed
+45
No files matched your search
@@ -70,6 +70,9 @@ import "../../../../static/fonts/SourceSans3-VariableFont_wght.ttf";
|
||||
component LyricsLineRow inherits VerticalLayout {
|
||||
in property <int> index;
|
||||
in property <string> line-text;
|
||||
// Translation of this line (Qobuz v10); empty when the doc has none.
|
||||
// Rendered under the original line while LyricsState.show-translation.
|
||||
in property <string> translation-text;
|
||||
in property <length> size-inactive;
|
||||
in property <length> size-active;
|
||||
// 0 = off, 1 = soft, 2 = strong (Tauri default) — pref-driven (S5).
|
||||
@@ -86,6 +89,10 @@ component LyricsLineRow inherits VerticalLayout {
|
||||
// eager init already saw it active.
|
||||
in property <int> recenter-epoch;
|
||||
|
||||
// Gap between the original line and its translation (no effect while
|
||||
// the translation is hidden — a single child has no spacing).
|
||||
spacing: 3px;
|
||||
|
||||
// Tauri renders uppercase via CSS text-transform; Slint transforms the
|
||||
// string (display only — the engine still matches on the raw text).
|
||||
property <string> display-text:
|
||||
@@ -255,6 +262,26 @@ component LyricsLineRow inherits VerticalLayout {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Translation (Qobuz v10): static text UNDER the original line — smaller
|
||||
// (~72%, mobile reference) and dimmer. The karaoke fill above stays keyed
|
||||
// on the ORIGINAL timings/text; this follows its line statically and
|
||||
// joins the same dimming ladder (never uppercased — it's a translation,
|
||||
// not the lyric). No-op while the toggle is off or the doc has none.
|
||||
if LyricsState.show-translation && root.translation-text != "": Text {
|
||||
text: root.translation-text;
|
||||
wrap: word-wrap;
|
||||
opacity: root.line-opacity();
|
||||
color: root.is-past ? Theme.text-muted : Theme.text-secondary.with-alpha(0.7);
|
||||
font-family: root.font-name;
|
||||
font-size: (root.is-active ? root.size-active : root.size-inactive) * 0.72;
|
||||
font-weight: Typography.regular;
|
||||
letter-spacing: 0.2px;
|
||||
animate opacity {
|
||||
duration: 220ms;
|
||||
easing: ease-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export component LyricsLinesView inherits Rectangle {
|
||||
@@ -386,6 +413,7 @@ export component LyricsLinesView inherits Rectangle {
|
||||
for line[idx] in LyricsState.lines: LyricsLineRow {
|
||||
index: idx;
|
||||
line-text: line.text;
|
||||
translation-text: line.translation-text;
|
||||
size-inactive: root.size-inactive;
|
||||
size-active: root.size-active;
|
||||
dimming-mode: root.dimming-mode;
|
||||
|
||||
@@ -4454,6 +4454,11 @@ export struct LyricsLineItem {
|
||||
time-ms: int,
|
||||
end-ms: int,
|
||||
has-words: bool,
|
||||
// Translation of this line (Qobuz v10), aligned 1:1 with `text` by the
|
||||
// Rust glue; empty when the doc has no translation (or it wasn't
|
||||
// requested). Rendered under the original line only while
|
||||
// `LyricsState.show-translation` is on.
|
||||
translation-text: string,
|
||||
}
|
||||
|
||||
// One VISUAL line of the ACTIVE logical line, for the per-visual-line karaoke
|
||||
@@ -4488,6 +4493,18 @@ export global LyricsState {
|
||||
// Playing-track meta for the header ("{title} - {artist}").
|
||||
in property <string> track-title;
|
||||
in property <string> track-artist;
|
||||
|
||||
// ---- Translation (Qobuz API v10) ---------------------------------------
|
||||
// The current doc advertises at least one translation language
|
||||
// (`translation_langs` non-empty) — gates the sidebar's floating toggle.
|
||||
// False on every other state, so the sidebar renders exactly as before.
|
||||
in property <bool> translation-available: false;
|
||||
// User intent: render the translation under each original line. Driven by
|
||||
// Rust (`toggle-translation`): ON refetches with the target language and
|
||||
// flips this only when the translation actually arrived; OFF is pure UI.
|
||||
in-out property <bool> show-translation: false;
|
||||
// Sidebar floating-button action (wired in main.rs).
|
||||
callback toggle-translation();
|
||||
// Surfaced backend error text (status == 4) — dynamic content, not chrome.
|
||||
in property <string> error;
|
||||
// Active line index; -1 = none. The S4 sync engine drives it.
|
||||
|
||||
Reference in new issue
Block a user