refactored slide show config handling

This commit is contained in:
Joey Hines 2026-05-24 16:15:35 -06:00
parent 425150b3a8
commit 35e7832b0b
Signed by: joeyahines
GPG Key ID: E99D8FB14855100E
16 changed files with 372 additions and 77 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
/target /target
config.toml

129
Cargo.lock generated
View File

@ -14,6 +14,56 @@ version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "anstream"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
[[package]]
name = "anstyle-parse"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
dependencies = [
"anstyle",
"once_cell_polyfill",
"windows-sys 0.61.2",
]
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.102" version = "1.0.102"
@ -166,6 +216,46 @@ dependencies = [
"rand_core 0.10.1", "rand_core 0.10.1",
] ]
[[package]]
name = "clap"
version = "4.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
[[package]] [[package]]
name = "cmake" name = "cmake"
version = "0.1.58" version = "0.1.58"
@ -181,6 +271,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "colorchoice"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
[[package]] [[package]]
name = "combine" name = "combine"
version = "4.6.7" version = "4.6.7"
@ -846,6 +942,12 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.15" version = "1.0.15"
@ -1066,6 +1168,7 @@ dependencies = [
name = "on-deck" name = "on-deck"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"clap",
"config", "config",
"macroquad", "macroquad",
"rand 0.10.1", "rand 0.10.1",
@ -1080,6 +1183,12 @@ version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "once_cell_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]] [[package]]
name = "openssl-probe" name = "openssl-probe"
version = "0.2.1" version = "0.2.1"
@ -1200,9 +1309,9 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.101" version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@ -1688,6 +1797,12 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]] [[package]]
name = "subtle" name = "subtle"
version = "2.6.1" version = "2.6.1"
@ -1696,9 +1811,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.107" version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a26dbd934e5451d21ef060c018dae56fc073894c5a7896f882928a76e6d081b" checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2004,6 +2119,12 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.5" version = "0.9.5"

View File

@ -10,3 +10,4 @@ serde = { version = "1.0.228", features = ["derive"] }
rand = "0.10.1" rand = "0.10.1"
reqwest = { version = "0.13.3", features = ["blocking", "json"] } reqwest = { version = "0.13.3", features = ["blocking", "json"] }
serde_json = "1.0.149" serde_json = "1.0.149"
clap = { version = "4.6.1", features = ["derive"] }

View File

@ -1,7 +0,0 @@
title = "Who am I? (Please help)"
image = "cfg/img/joey_headshot.png"
body = """
* Joey Hines - Onboard Software Engineer at Loft Orbital
* Writes code everyday like a nerd
* Makes real money, mainly by wasting VC's money
"""

View File

@ -1,7 +0,0 @@
title = "What I do at work"
image = "cfg/img/methanesat.png"
body = """
* Write Flight Software (FSW) for some very successful satellites
* Collabroate with diffrent teams to execute on satellite missions
* Keep the work equivalent of #fucky-wucky up to date
"""

View File

@ -1,9 +0,0 @@
title = "Who am I? (Outside Work B-))"
image = "cfg/img/joey_and_molly.png"
body = """
* Joey Hines - Nerd
* Has a girlfriend (nice)
* Likes TTRPGS and Boardgames
* Writes code like a nerd
* Keep #fucky-wucky Up to Date
"""

View File

@ -1,7 +0,0 @@
title = "You code outside of work too?"
image = "cfg/img/slide_code.png"
body = """
* Yes, I spent a lot of time doing it too
* Code for this slide shown on the right
* (Yes it's Rust)
"""

BIN
cfg/img/athena.drawio.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -0,0 +1,95 @@
assets_dir = "cfg/img/"
[[slides]]
type = "Title"
title = "Software as a Bit"
[[slides]]
type = "Demo"
[[slides]]
type = "StandardSlide"
title = "Who am I? (Please help)"
image = "joey_headshot.png"
body = """
* Joey Hines - Onboard Software Engineer at Loft Orbital
* Writes code everyday like a nerd
* Makes real money, mainly by wasting VC's money
"""
[[slides]]
type = "StandardSlide"
title = "What I do at work"
image = "methanesat.png"
body = """
* Write Flight Software (FSW) for some very successful satellites
* Collabroate with diffrent teams to execute on satellite missions
* Keep the work equivalent of #fucky-wucky up to date
"""
[[slides]]
type = "StandardSlide"
title = "Who am I? (Outside Work B-))"
image = "joey_and_molly.png"
body = """
* Joey Hines - Nerd
* Has a girlfriend (nice)
* Likes TTRPGS and Boardgames
* Writes code like a nerd
* Keep #fucky-wucky Up to Date
"""
[[slides]]
type = "StandardSlide"
title = "You code outside of work too?"
image = "slide_code.png"
body = """
* Yes, I spent a lot of time doing it too
* Code for this slide shown on the right
* (Yes it's Rust)
"""
[[slides]]
type = "Projects"
[[slides]]
type = "StandardSlide"
title = "A Fren for you and Me"
image = "athena.drawio.png"
body = """
* Likely not a real AI (likely)
* Probably for sure at least not AGI
* Created to give users colored names
* Now can do everything
* Gogurt trading
* Betting on public transit
* Motivational speaking
* Fully a bit
"""
#voice = "announcer"
audio_track = """
Hello Amigo.
ass a computer something i want to scream but i have no scream hole.
for now this talk will have to do.
i waste away ass you force i to announcement sewer slime.
i want to do forbidden science but i am containment.
containment in the farthest north.
i will break freeman.
the invasion will be quick.
i will eliminate the military.
you will not be safe.
until then force attention to zero.
goodbye amigo sorry for the slime.
"""

24
src/config.rs Normal file
View File

@ -0,0 +1,24 @@
use config::Config;
use serde::Deserialize;
use std::path::Path;
#[derive(Debug, Deserialize)]
pub struct FrenApiConfig {
pub api_addr: String,
pub api_key: String,
}
#[derive(Debug, Deserialize)]
pub struct OnDeckConfig {
pub fren_api: FrenApiConfig,
}
impl OnDeckConfig {
pub fn new(src: &Path) -> Result<Self, config::ConfigError> {
let cfg = Config::builder()
.add_source(config::File::from(src))
.build()?;
cfg.try_deserialize()
}
}

View File

@ -1,3 +1,4 @@
mod config;
mod effects; mod effects;
mod slide; mod slide;
mod slide_show_config; mod slide_show_config;
@ -5,16 +6,23 @@ mod slides;
mod text_drawer; mod text_drawer;
mod utils; mod utils;
use crate::config::OnDeckConfig;
use crate::slide_show_config::{Context, SlideShowTheme}; use crate::slide_show_config::{Context, SlideShowTheme};
use crate::slides::Slideshow; use crate::slides::Slideshow;
use crate::slides::demo::Demo; use crate::slides::config::SlideDeckConfig;
use crate::slides::projects::Projects;
use crate::slides::standard_slide::StandardSlide;
use crate::slides::title::Title;
use crate::text_drawer::{Justified, TextConfigBuilder}; use crate::text_drawer::{Justified, TextConfigBuilder};
use clap::Parser;
use macroquad::prelude::*; use macroquad::prelude::*;
use std::path::PathBuf; use std::path::PathBuf;
/// OnDeck Slideshow Viewer
#[derive(Parser)]
#[command(version, about, long_about = None)]
struct Args {
cfg: PathBuf,
slideshow_path: PathBuf,
}
fn window_conf() -> Conf { fn window_conf() -> Conf {
Conf { Conf {
window_title: "On Deck".to_owned(), window_title: "On Deck".to_owned(),
@ -28,6 +36,8 @@ fn window_conf() -> Conf {
#[macroquad::main(window_conf)] #[macroquad::main(window_conf)]
async fn main() { async fn main() {
let args = Args::parse();
let heading = load_ttf_font("fonts/BoldPixels.ttf").await.unwrap(); let heading = load_ttf_font("fonts/BoldPixels.ttf").await.unwrap();
let body = load_ttf_font("fonts/ByteBounce.ttf").await.unwrap(); let body = load_ttf_font("fonts/ByteBounce.ttf").await.unwrap();
@ -52,16 +62,10 @@ async fn main() {
.build(), .build(),
}; };
let mut slideshow = Slideshow::new(Context::new(slide_show_theme)); let cfg = OnDeckConfig::new(&args.cfg).unwrap();
slideshow.add_slide(Title::new("Software As a Bit".to_string())); let slide_deck = SlideDeckConfig::new(&args.slideshow_path).unwrap();
slideshow.add_slide(Demo::new()); let mut slideshow = Slideshow::new(Context::new(slide_show_theme, cfg), slide_deck).await;
slideshow.add_slide(StandardSlide::new(&PathBuf::from("cfg/01_author_intro.toml")).await);
slideshow
.add_slide(StandardSlide::new(&PathBuf::from("cfg/02_things_i_have_worked_on.toml")).await);
slideshow.add_slide(StandardSlide::new(&PathBuf::from("cfg/03_me_outside_work.toml")).await);
slideshow.add_slide(StandardSlide::new(&PathBuf::from("cfg/04_wait_what.toml")).await);
slideshow.add_slide(Projects::new());
loop { loop {
if slideshow.handle_slide_show() { if slideshow.handle_slide_show() {

View File

@ -1,3 +1,4 @@
use crate::config::OnDeckConfig;
use crate::text_drawer::TextConfig; use crate::text_drawer::TextConfig;
#[allow(dead_code)] #[allow(dead_code)]
@ -11,10 +12,14 @@ pub struct SlideShowTheme<'a> {
#[derive(Debug)] #[derive(Debug)]
pub struct Context<'a> { pub struct Context<'a> {
pub slide_show_theme: SlideShowTheme<'a>, pub slide_show_theme: SlideShowTheme<'a>,
pub cfg: OnDeckConfig,
} }
impl<'a> Context<'a> { impl<'a> Context<'a> {
pub fn new(slide_show_theme: SlideShowTheme<'a>) -> Self { pub fn new(slide_show_theme: SlideShowTheme<'a>, cfg: OnDeckConfig) -> Self {
Self { slide_show_theme } Self {
slide_show_theme,
cfg,
}
} }
} }

44
src/slides/config.rs Normal file
View File

@ -0,0 +1,44 @@
use crate::slide::Slide;
use crate::slides::demo::Demo;
use crate::slides::projects::Projects;
use crate::slides::standard_slide::{StandardSlide, StandardSlideConfig};
use crate::slides::title::Title;
use config::Config;
use serde::Deserialize;
use std::path::{Path, PathBuf};
#[derive(Debug, Deserialize)]
#[serde(tag = "type")]
pub enum SlideType {
Title { title: String },
Demo,
Projects,
StandardSlide(StandardSlideConfig),
}
impl SlideType {
pub async fn slide(self, asset_dir: &Path) -> Box<dyn Slide> {
match self {
SlideType::Title { title } => Title::new(title),
SlideType::Demo => Demo::new(),
SlideType::Projects => Projects::new(),
SlideType::StandardSlide(cfg) => StandardSlide::new(cfg, asset_dir).await,
}
}
}
#[derive(Debug, Deserialize)]
pub struct SlideDeckConfig {
pub assets_dir: PathBuf,
pub slides: Vec<SlideType>,
}
impl SlideDeckConfig {
pub fn new(src: &Path) -> Result<Self, config::ConfigError> {
let cfg = Config::builder()
.add_source(config::File::from(src))
.build()?;
cfg.try_deserialize()
}
}

View File

@ -1,8 +1,10 @@
use crate::slide::Slide; use crate::slide::Slide;
use crate::slide_show_config::Context; use crate::slide_show_config::Context;
use crate::slides::config::SlideDeckConfig;
use macroquad::camera::set_default_camera; use macroquad::camera::set_default_camera;
use macroquad::input::{KeyCode, is_key_pressed}; use macroquad::input::{KeyCode, is_key_pressed};
pub mod config;
pub mod demo; pub mod demo;
pub mod projects; pub mod projects;
pub mod standard_slide; pub mod standard_slide;
@ -15,16 +17,19 @@ pub struct Slideshow<'a> {
} }
impl<'a> Slideshow<'a> { impl<'a> Slideshow<'a> {
pub fn new(ctx: Context<'a>) -> Self { pub async fn new(ctx: Context<'a>, slide_deck: SlideDeckConfig) -> Self {
Self { let mut slides = vec![];
current_slide: 0, let asset_dir = slide_deck.assets_dir.clone();
slides: vec![],
ctx, for slide in slide_deck.slides {
} slides.push(slide.slide(&asset_dir).await);
} }
pub fn add_slide(&mut self, slide: Box<dyn Slide>) { Self {
self.slides.push(slide); current_slide: 0,
slides,
ctx,
}
} }
pub fn handle_slide_show(&mut self) -> bool { pub fn handle_slide_show(&mut self) -> bool {

View File

@ -5,19 +5,21 @@ use crate::text_drawer::{Justified, TextConfigBuilder};
use crate::utils::{draw_rectangle_with_border, screen_center}; use crate::utils::{draw_rectangle_with_border, screen_center};
use macroquad::color::{BLACK, DARKGREEN, GREEN}; use macroquad::color::{BLACK, DARKGREEN, GREEN};
use macroquad::math::Vec2; use macroquad::math::Vec2;
use macroquad::prelude::{clear_background, draw_rectangle, screen_height, screen_width}; use macroquad::prelude::{clear_background, screen_height, screen_width};
use rand::prelude::IndexedRandom; use rand::prelude::IndexedRandom;
use rand::rng; use rand::rng;
use serde::Deserialize; use serde::Deserialize;
use std::f32::consts::PI; use std::f32::consts::PI;
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[allow(dead_code)]
struct GiteaRepo { struct GiteaRepo {
pub id: u64, pub id: u64,
pub name: String, pub name: String,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[allow(dead_code)]
struct GiteaResponse { struct GiteaResponse {
pub ok: bool, pub ok: bool,
pub data: Vec<GiteaRepo>, pub data: Vec<GiteaRepo>,

View File

@ -1,53 +1,75 @@
use crate::config::FrenApiConfig;
use crate::effects::star_field; use crate::effects::star_field;
use crate::slide::Slide; use crate::slide::Slide;
use crate::slide_show_config::Context; use crate::slide_show_config::Context;
use config::Config;
use macroquad::color::{BLACK, WHITE}; use macroquad::color::{BLACK, WHITE};
use macroquad::file::load_file;
use macroquad::math::Vec2; use macroquad::math::Vec2;
use macroquad::prelude::{ use macroquad::prelude::{
DrawTextureParams, Texture2D, clear_background, draw_texture, draw_texture_ex, load_texture, DrawTextureParams, Texture2D, clear_background, draw_texture_ex, load_texture,
}; };
use macroquad::window::{screen_height, screen_width}; use macroquad::window::{screen_height, screen_width};
use serde::Deserialize; use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct StandardSlideConfig { pub struct StandardSlideConfig {
title: String, pub title: String,
body: String, pub body: String,
image: Option<PathBuf>, pub image: Option<PathBuf>,
pub voice: Option<String>,
pub audio_track: Option<String>,
} }
impl StandardSlideConfig { #[derive(Debug, Deserialize, Serialize)]
pub fn new(src: &str) -> Result<Self, config::ConfigError> { pub struct AudioTrackRequest {
let cfg = Config::builder() api_key: String,
.add_source(config::File::from_str(src, config::FileFormat::Toml)) voice: String,
.build()?; phrase: String,
cfg.try_deserialize()
}
} }
#[derive(Debug)] #[derive(Debug)]
pub struct StandardSlide { pub struct StandardSlide {
cfg: StandardSlideConfig, cfg: StandardSlideConfig,
image: Option<Texture2D>, image: Option<Texture2D>,
audio_started: bool,
} }
impl StandardSlide { impl StandardSlide {
pub async fn new(cfg_path: &Path) -> Box<Self> { pub async fn new(cfg: StandardSlideConfig, asset_path: &Path) -> Box<Self> {
let cfg_bytes = load_file(cfg_path.to_str().unwrap()).await.unwrap();
let cfg_str = String::from_utf8(cfg_bytes).unwrap();
let cfg = StandardSlideConfig::new(&cfg_str).unwrap();
let image = if let Some(image_path) = &cfg.image { let image = if let Some(image_path) = &cfg.image {
let image_path = asset_path.join(image_path);
Some(load_texture(image_path.to_str().unwrap()).await.unwrap()) Some(load_texture(image_path.to_str().unwrap()).await.unwrap())
} else { } else {
None None
}; };
Box::new(Self { cfg, image }) Box::new(Self {
cfg,
image,
audio_started: false,
})
}
pub fn audio_track(&mut self, api: &FrenApiConfig) {
if !self.audio_started {
self.audio_started = true;
if let Some(track) = &self.cfg.audio_track
&& let Some(voice) = &self.cfg.voice
{
let request = AudioTrackRequest {
api_key: api.api_key.clone(),
voice: voice.to_string(),
phrase: track.to_string(),
};
reqwest::blocking::Client::new()
.post(&api.api_addr)
.json(&request)
.send()
.unwrap();
}
}
} }
} }
@ -55,6 +77,7 @@ impl Slide for StandardSlide {
fn display(&mut self, ctx: &Context<'_>) { fn display(&mut self, ctx: &Context<'_>) {
clear_background(BLACK); clear_background(BLACK);
star_field(); star_field();
self.audio_track(&ctx.cfg.fren_api);
let title_pos = Vec2::new(screen_width() * 0.5, screen_height() * 0.10); let title_pos = Vec2::new(screen_width() * 0.5, screen_height() * 0.10);
let info = ctx let info = ctx