Create initial interface app
This commit is contained in:
parent
5210c0a5e6
commit
2e1cd7dc16
9 changed files with 3100 additions and 6 deletions
1
app/.gitignore
vendored
Normal file
1
app/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/target
|
||||
2762
app/Cargo.lock
generated
Normal file
2762
app/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
12
app/Cargo.toml
Normal file
12
app/Cargo.toml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "jukebox"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.22.1"
|
||||
maud = "0.27.0"
|
||||
serde = "1.0.228"
|
||||
serde_json = "1.0.145"
|
||||
tao = { version = "0.34.3", default-features = false, features = ["rwh_06"] }
|
||||
wry = { version = "0.53.3", default-features = false, features = ["os-webview", "protocol"] }
|
||||
19
app/src/app.js
Normal file
19
app/src/app.js
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
function removePlaying() {
|
||||
for (const el of document.getElementsByClassName("playing")) {
|
||||
el.classList.add("fadeout");
|
||||
el.addEventListener("transitionend", () => el.remove());
|
||||
}
|
||||
}
|
||||
|
||||
function showPlaying({ cover, title, artist, album }) {
|
||||
removePlaying();
|
||||
const container = document.createElement("div");
|
||||
container.classList.add("playing");
|
||||
container.innerHTML = `
|
||||
<img class="cover" src="${cover}">
|
||||
<span class="title">${title}</span>
|
||||
<span class="artist">${artist}</span>
|
||||
<span class="album">${album}</span>
|
||||
`;
|
||||
document.body.appendChild(container);
|
||||
}
|
||||
134
app/src/main.rs
Normal file
134
app/src/main.rs
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
use std::{thread, time::Duration};
|
||||
|
||||
use base64::prelude::*;
|
||||
use maud::{Markup, PreEscaped, html};
|
||||
use serde::Serialize;
|
||||
use tao::{
|
||||
event::Event,
|
||||
event_loop::{ControlFlow, EventLoopBuilder},
|
||||
platform::unix::WindowExtUnix,
|
||||
window::{Fullscreen, WindowBuilder},
|
||||
};
|
||||
use wry::{WebViewBuilder, WebViewBuilderExtUnix};
|
||||
|
||||
static STYLESHEET: &str = include_str!("stylesheet.css");
|
||||
static SCRIPT: &str = include_str!("app.js");
|
||||
static RALEWAY: &[u8] = include_bytes!("Raleway.ttf");
|
||||
static RALEWAY_ITALIC: &[u8] = include_bytes!("Raleway-Italic.ttf");
|
||||
static ALBUM_COVER_1: &[u8] = include_bytes!("album01.jpg");
|
||||
static ALBUM_COVER_2: &[u8] = include_bytes!("album02.jpg");
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(untagged)]
|
||||
enum JukeboxEvent {
|
||||
ShowPlaying {
|
||||
cover: String,
|
||||
title: String,
|
||||
artist: String,
|
||||
album: String,
|
||||
},
|
||||
RemovePlaying,
|
||||
}
|
||||
|
||||
impl JukeboxEvent {
|
||||
fn get_invocation_name(&self) -> &'static str {
|
||||
match self {
|
||||
JukeboxEvent::ShowPlaying { .. } => "showPlaying",
|
||||
JukeboxEvent::RemovePlaying => "removePlaying",
|
||||
}
|
||||
}
|
||||
|
||||
fn get_invocation(&self) -> String {
|
||||
return format!(
|
||||
"{}({});",
|
||||
self.get_invocation_name(),
|
||||
serde_json::to_string(&self).unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let event_loop = EventLoopBuilder::<JukeboxEvent>::with_user_event().build();
|
||||
let proxy = event_loop.create_proxy();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_fullscreen(Some(Fullscreen::Borderless(None)))
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
let vbox = window.default_vbox().unwrap();
|
||||
|
||||
let webview = WebViewBuilder::new()
|
||||
.with_html(webapp())
|
||||
.with_focused(true)
|
||||
.build_gtk(vbox)
|
||||
.unwrap();
|
||||
|
||||
thread::spawn(move || {
|
||||
thread::sleep(Duration::from_secs(2));
|
||||
loop {
|
||||
proxy
|
||||
.send_event(JukeboxEvent::ShowPlaying {
|
||||
cover: build_data_url("image/jpeg", ALBUM_COVER_1),
|
||||
title: String::from("The Great Gig in the Sky"),
|
||||
artist: String::from("Pink Floyd"),
|
||||
album: String::from("The Dark Side of the Moon"),
|
||||
})
|
||||
.unwrap();
|
||||
thread::sleep(Duration::from_secs(3));
|
||||
proxy.send_event(JukeboxEvent::RemovePlaying).unwrap();
|
||||
thread::sleep(Duration::from_secs(3));
|
||||
proxy
|
||||
.send_event(JukeboxEvent::ShowPlaying {
|
||||
cover: build_data_url("image/jpeg", ALBUM_COVER_2),
|
||||
title: String::from("Boulevard of Broken Dreams"),
|
||||
artist: String::from("Green Day"),
|
||||
album: String::from("American Idiot"),
|
||||
})
|
||||
.unwrap();
|
||||
thread::sleep(Duration::from_secs(3));
|
||||
}
|
||||
});
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
match event {
|
||||
Event::UserEvent(jukebox_event) => {
|
||||
let js = jukebox_event.get_invocation();
|
||||
webview.evaluate_script(&js).unwrap();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn build_data_url(mimetype: &str, input: impl AsRef<[u8]>) -> String {
|
||||
format!(
|
||||
"data:{};base64,{}",
|
||||
mimetype,
|
||||
BASE64_STANDARD.encode(&input)
|
||||
)
|
||||
}
|
||||
|
||||
fn webapp() -> Markup {
|
||||
let b64 = build_data_url("font/ttf;charset=utf-8", RALEWAY);
|
||||
let b64_italic = build_data_url("font/ttf;charset=utf-8", RALEWAY_ITALIC);
|
||||
let fontface_defs = format!(
|
||||
r#"
|
||||
@font-face {{
|
||||
font-family: 'Raleway';
|
||||
src: url('{b64}') format('truetype');
|
||||
font-style: normal;
|
||||
}}
|
||||
@font-face {{
|
||||
font-family: 'Raleway';
|
||||
src: url('{b64_italic}') format('truetype');
|
||||
font-style: italic;
|
||||
}}
|
||||
"#
|
||||
);
|
||||
html! {
|
||||
style { (PreEscaped(fontface_defs)) }
|
||||
style { (PreEscaped(STYLESHEET)) }
|
||||
script { (PreEscaped(SCRIPT)) }
|
||||
}
|
||||
}
|
||||
71
app/src/stylesheet.css
Normal file
71
app/src/stylesheet.css
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
:root {
|
||||
background-color: #456798;
|
||||
color: white;
|
||||
font-family: 'Raleway';
|
||||
user-select: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@keyframes show-playing {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes remove-playing {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translateX(-20px);
|
||||
}
|
||||
}
|
||||
|
||||
.playing {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
animation: show-playing 0.3s ease-out forwards;
|
||||
&.fadeout {
|
||||
animation: remove-playing 0.3s ease-out forwards;
|
||||
}
|
||||
span {
|
||||
text-shadow: 4px 4px 4px #0000001a;
|
||||
}
|
||||
.cover {
|
||||
width: 400px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 8px 8px 8px #0000003a;
|
||||
margin-bottom: 0.6rem;
|
||||
}
|
||||
.title {
|
||||
font-size: 2rem;
|
||||
font-weight: 450;
|
||||
}
|
||||
.artist {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
.album {
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
61
flake.lock
generated
Normal file
61
flake.lock
generated
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1759831965,
|
||||
"narHash": "sha256-vgPm2xjOmKdZ0xKA6yLXPJpjOtQPHfaZDRtH+47XEBo=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "c9b6fb798541223bbb396d287d16f43520250518",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
40
flake.nix
Normal file
40
flake.nix
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
description = "Jukebox";
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
outputs = { nixpkgs, flake-utils, ... }:
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let pkgs = nixpkgs.legacyPackages.${system};
|
||||
in {
|
||||
# nixosConfigurations.jukebox = nixpkgs.lib.nixosSystem {
|
||||
# inherit system;
|
||||
# modules = [ ./system/configuration.nix ];
|
||||
# };
|
||||
packages.jukebox = pkgs.rustPlatform.buildRustPackage {
|
||||
name = "jukebox";
|
||||
src = ./app;
|
||||
cargoLock.lockFile = ./app/Cargo.lock;
|
||||
};
|
||||
devShells.default = pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
atkmm
|
||||
cairo
|
||||
cargo
|
||||
gdk-pixbuf
|
||||
glib
|
||||
gtk3
|
||||
just
|
||||
openscad-unstable
|
||||
openssl
|
||||
pango
|
||||
pkg-config
|
||||
rustc
|
||||
rustPlatform.bindgenHook
|
||||
webkitgtk_4_1
|
||||
xdotool
|
||||
];
|
||||
};
|
||||
});
|
||||
}
|
||||
6
justfile
6
justfile
|
|
@ -1,6 +0,0 @@
|
|||
mod models
|
||||
|
||||
default: build
|
||||
|
||||
build:
|
||||
@just models/build
|
||||
Loading…
Add table
Add a link
Reference in a new issue