feat(app): bundled Inter and Phosphor icons, window icon

Headings are set in Inter SemiBold, everything else in Inter Regular, so a
title sets the same on every desktop. Media and navigation glyphs (⏭, 🔊, ⛶,
★) come from the Phosphor icon font instead of whichever fallback font had
them. The CJK fallback still loads on its own thread and now joins the
bundled fonts instead of replacing them. The window carries the app icon
on X11 and Windows.
This commit is contained in:
nvrl committed 2026-10-02 23:07:05 +02:00
1 parent 6518aef5ee
commit 558446b9e4
17 files changed
+256 -45

No files matched your search

+6 -4
View File
@@ -211,9 +211,11 @@ fn hero(ui: &mut egui::Ui, art: &mut Art<'_>, title: &Title, actions: &mut Vec<A
ui.horizontal(|ui| {
if let Some(episode) = title.next_up() {
let label = match (title.resume(), episode.numbering()) {
(Some(_), Some(numbering)) => format!("▶ Resume {numbering}"),
(Some(_), None) => "▶ Resume".to_owned(),
(None, _) => "▶ Play".to_owned(),
(Some(_), Some(numbering)) => {
format!("{} Resume {numbering}", egui_phosphor::regular::PLAY)
}
(Some(_), None) => format!("{} Resume", egui_phosphor::regular::PLAY),
(None, _) => format!("{} Play", egui_phosphor::regular::PLAY),
};
if ui::accent_button(ui, &label).clicked() {
actions.push(Action::Play(PlaybackTarget::new(title, episode)));
@@ -239,7 +241,7 @@ fn hero(ui: &mut egui::Ui, art: &mut Art<'_>, title: &Title, actions: &mut Vec<A
let mut facts = vec![subtitle(title)];
if let Some(found) = &found {
if let Some(rating) = found.rating {
facts.push(format!("★ {rating:.1}"));
facts.push(format!("{} {rating:.1}", egui_phosphor::regular::STAR));
}
facts.extend(found.genres.iter().take(3).cloned());
}
+1 -1
View File
@@ -279,7 +279,7 @@ fn facts(found: &TitleMetadata) -> String {
parts.push(ui::library::plural(episodes as usize, "episode"));
}
if let Some(rating) = found.rating {
parts.push(format!("★ {rating:.1}"));
parts.push(format!("{} {rating:.1}", egui_phosphor::regular::STAR));
}
parts.join(" · ")
}
+14 -5
View File
@@ -657,14 +657,23 @@ fn chrome(
});
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
let fullscreen = ui.input(|input| input.viewport().fullscreen.unwrap_or(false));
if ui
.add(
egui::Button::new(theme::Role::Subhead.rich("⛶"))
.fill(Color32::from_black_alpha(160))
.corner_radius(CornerRadius::same(8))
.min_size(Vec2::new(38.0, 32.0)),
egui::Button::new(theme::Role::Subhead.rich(if fullscreen {
egui_phosphor::regular::CORNERS_IN
} else {
egui_phosphor::regular::CORNERS_OUT
}))
.fill(Color32::from_black_alpha(160))
.corner_radius(CornerRadius::same(8))
.min_size(Vec2::new(38.0, 32.0)),
)
.on_hover_text("Fullscreen (F)")
.on_hover_text(if fullscreen {
"Leave fullscreen (F)"
} else {
"Fullscreen (F)"
})
.clicked()
{
actions.push(Action::ToggleFullscreen);
+2 -2
View File
@@ -128,7 +128,7 @@ fn poster(ui: &mut egui::Ui, texture: &egui::TextureHandle, progress: Option<f64
/// "← Library", as a link rather than a button: it is navigation, and a
/// filled button beside the page's real actions competed with them.
fn back_link(ui: &mut egui::Ui) -> egui::Response {
let text = theme::Role::Label.rich("← Library");
let text = theme::Role::Label.rich(format!("{} Library", egui_phosphor::regular::ARROW_LEFT));
let response = ui.add(egui::Label::new(text.color(theme::muted())).sense(egui::Sense::click()));
if response.hovered() {
ui.painter().hline(
@@ -532,7 +532,7 @@ fn meta_line(title: &Title, found: Option<&TitleMetadata>) -> String {
if let Some(found) = found {
if let Some(rating) = found.rating {
parts.push(format!("★ {rating:.1}"));
parts.push(format!("{} {rating:.1}", egui_phosphor::regular::STAR));
}
// Only when it disagrees with what is on disk: "25 episodes · 25
// episodes" tells nobody anything, but "12 episodes · 25 on AniList"
+27 -11
View File
@@ -65,7 +65,7 @@ pub fn full(
// hand reaches for it.
step_button(
ui,
"⏮",
egui_phosphor::regular::SKIP_BACK,
"Previous (P)",
neighbours.previous,
actions,
@@ -76,9 +76,16 @@ pub fn full(
skip_button(ui, "−10s", "Back ten seconds (←)", -10.0, actions);
play_pause(ui, playback, actions, 44.0);
skip_button(ui, "+30s", "Forward thirty seconds (→)", 30.0, actions);
step_button(ui, "⏭", "Next (N)", neighbours.next, actions, |a| {
a.push(Action::PlayAdjacent(Adjacent::Next));
});
step_button(
ui,
egui_phosphor::regular::SKIP_FORWARD,
"Next (N)",
neighbours.next,
actions,
|a| {
a.push(Action::PlayAdjacent(Adjacent::Next));
},
);
ui.add_space(12.0);
if playback.seeking || playback.buffering || !playback.loaded {
@@ -202,13 +209,20 @@ pub fn mini(
actions.push(Action::Player(Command::Stop));
}
ui.add_space(6.0);
step_button(ui, "⏭", "Next (N)", neighbours.next, actions, |a| {
a.push(Action::PlayAdjacent(Adjacent::Next));
});
step_button(
ui,
egui_phosphor::regular::SKIP_FORWARD,
"Next (N)",
neighbours.next,
actions,
|a| {
a.push(Action::PlayAdjacent(Adjacent::Next));
},
);
play_pause(ui, playback, actions, 34.0);
step_button(
ui,
"⏮",
egui_phosphor::regular::SKIP_BACK,
"Previous (P)",
neighbours.previous,
actions,
@@ -414,12 +428,14 @@ fn volume(ui: &mut egui::Ui, playback: &Playback, actions: &mut Vec<Action>) {
}
let icon = if playback.muted || playback.volume <= 0.0 {
"🔇"
egui_phosphor::regular::SPEAKER_SLASH
} else if playback.volume < 50.0 {
egui_phosphor::regular::SPEAKER_LOW
} else {
"🔊"
egui_phosphor::regular::SPEAKER_HIGH
};
if ui
.button(icon)
.button(theme::Role::Subhead.rich(icon))
.on_hover_text(format!("Mute (M) — {:.0}%", playback.volume))
.clicked()
{