use html_to_string_macro::html; use once_cell::sync::Lazy; use puppeteer::{ async_trait::{self}, smol::lock::Mutex, tracing::{self, Level}, ActiveAppEnv, ContextMenu, ModifyView, Puppeteer, PuppeteerApp, Shell, StaticAsset, DEFAULT_WINDOW_ACTIONS, DEFAULT_WINDOW_ACTIONS_SCRIPT, DEFAULT_WINDOW_ACTIONS_STYLE, }; use std::collections::HashMap; use tracing_subscriber::FmtSubscriber; static MEM_STORE: Lazy>> = Lazy::new(|| { let storage = HashMap::new(); Mutex::new(storage) }); fn main() { let subscriber = FmtSubscriber::builder() // all spans/events with a level higher than TRACE (e.g, debug, info, warn, etc.) // will be written to stdout. .with_max_level(Level::TRACE) // completes the builder. .finish(); tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); const FONTS: [StaticAsset; 2] = puppeteer::assets_from_manifest_dir!( ("centauri", "examples/assets/fonts/centauri.woff2"), ("warteg", "examples/assets/fonts/warteg.woff2"), ); smol::block_on(async { PuppeteerApp::::init("Puppeteer Test App") .with_fonts(&FONTS) .start() .await .unwrap() }) } #[derive(Debug)] pub enum AppTest { Root, CloseWindow, RecvUserEmail(String), SubmitEmail, Inc, GetPassphrase, } const PUPPETEER_LOGO: &str = include_str!("../../Documentation/Puppeteer-Logo.svg"); const PUPPETEER_ICON: &str = include_str!("../../Documentation/Puppeteer-Logo-Icon.svg"); const CONTEXT_MENU: &str = r#" Menu Item-1 Menu Item-2 Menu Item-3 Menu Item-4 Menu Item-5 Menu Item-6 "#; const CONTEXT_MENU_STYLE: &str = r#" #context-menu-identifier { position: absolute; background-color: #84abb5; color: white; height: 150px; width: 100px; text-align: center; } .menuItems { list-style: none; font-size: 12px; padding: 0; margin: 0; } .menuItems .items { padding: 5px; border-bottom: 1px solid #e6d4b6;} .menuItems .items:last-child { border: none;} .menuItems .items a {text-decoration: none; color: white;} "#; const EMAIL_HANDLER: &str = r#" "#; #[async_trait::async_trait] impl Puppeteer for AppTest { fn shell() -> Shell { let context_menu_script = ContextMenu::new() .add_id("context-menu-identifier") .build_script(); Shell::new() // The order in which styles are added matters since CSS is cascading .add_style(include_str!("assets/frow.min.css")) .add_style("body {background-color: #1a1a1a; color: #FFFFFF;}") .add_style("#logo-icon svg{width: 30px}") .add_style(".splash-icon>svg{width: 50vw}") .add_style(CONTEXT_MENU_STYLE) .add_style(DEFAULT_WINDOW_ACTIONS_STYLE) .add_scripts([ DEFAULT_WINDOW_ACTIONS_SCRIPT.into(), context_menu_script, EMAIL_HANDLER.into(), ]) } fn splashscreen() -> ModifyView { let splash_html = html!( { PUPPETEER_LOGO } ); ModifyView::ReplaceApp(splash_html) } async fn init(app_env: &ActiveAppEnv) -> ModifyView { dbg!(app_env); smol::Timer::after(std::time::Duration::from_secs(3)).await; let title_bar = html!( {CONTEXT_MENU} { PUPPETEER_ICON } { DEFAULT_WINDOW_ACTIONS } "HELLO from PUPPETEER" "Nice Font :)" "SUBMIT" "INC" 0 ); ModifyView::ReplaceApp(title_bar) } fn parse(message: &str) -> Self { if message.starts_with("PuppeteerErrorĀ»") { // Handle this error into `Self` panic!("Encountered error: {}", message) } if message.starts_with("event:user_mail>") { let user_email = message.to_string().replace("event:user_mail>", ""); Self::RecvUserEmail(user_email) } else if message.starts_with("event:submit_mail>") { Self::SubmitEmail } else if message.starts_with("inc") { Self::Inc } else { match message { "get_passphrase" => Self::GetPassphrase, _ => todo!(), } } } async fn event_handler(&mut self, app_env: &ActiveAppEnv) -> ModifyView { match self { Self::RecvUserEmail(data) => { MEM_STORE.lock().await.insert("user_email", data.clone()); ModifyView::Skip } Self::Root => { println!("ACTIVE_ENV: {:?}", app_env); ModifyView::ReplaceApp("EVENT RECV".into()) } Self::SubmitEmail => { println!( "THE USER EMAIL IS: {:?}", MEM_STORE.lock().await.get("user_email") ); ModifyView::ReplaceApp("USER EMAIL SUBMITTED".into()) } Self::Inc => ModifyView::compute_with_data("inc", get_by_id), Self::GetPassphrase => { ModifyView::compute_input_with_data("passphrase", print_passphrase) } _ => ModifyView::Skip, } } async fn error_handler(_error: impl std::error::Error + Send) -> ModifyView { ModifyView::ReplaceApp("ERROR RECV".into()) } } fn get_by_id(value: &str) -> ModifyView { let value = value.replace('"', ""); let content = (value.parse::().unwrap() + 1).to_string(); ModifyView::ReplaceNodeWithId { id: "inc".into(), content, } } fn print_passphrase(value: &str) -> ModifyView { let value = value.replace('"', ""); dbg!(value); ModifyView::Skip }