From 09b2e20df9fe930605695ee77f19fad3738db5b4 Mon Sep 17 00:00:00 2001 From: h0lybyte <5599058+h0lybyte@users.noreply.github.com> Date: Wed, 11 Jun 2025 11:07:14 -0400 Subject: [PATCH 01/14] refactor: preparing to migrate the concept back into erust. --- src/app.rs | 41 ++++++++++++++++------------------------ src/core/mod.rs | 1 - src/core/uiux/mod.rs | 1 - src/core/uiux/search.rs | 41 ---------------------------------------- src/erust/mod.rs | 2 ++ src/{ => erust}/state.rs | 0 src/erust/uiux/mod.rs | 1 + src/erust/uiux/search.rs | 41 ++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 ++- src/mod.rs | 2 ++ 10 files changed, 64 insertions(+), 69 deletions(-) create mode 100644 src/erust/mod.rs rename src/{ => erust}/state.rs (100%) create mode 100644 src/erust/uiux/mod.rs create mode 100644 src/erust/uiux/search.rs create mode 100644 src/mod.rs diff --git a/src/app.rs b/src/app.rs index 08cd25c..239cd8d 100644 --- a/src/app.rs +++ b/src/app.rs @@ -3,15 +3,10 @@ use crate::utility::show_loading_spinner_custom; use egui::Id; use crate::db::github::{GithubDb, Repository}; use crate::db::idb::LANGUAGES; +use crate::erust::uiux::search::SearchWidget; +use crate::erust::state::AppState; #[derive(serde::Deserialize, serde::Serialize, Debug, Clone, PartialEq, Eq)] -pub enum AppState { - Init, - Normal, - Empty, -} - -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] pub enum LoadingState { Idle, Loading { @@ -61,6 +56,8 @@ pub struct TemplateApp { filtered_repos: Option>, #[serde(skip)] filter_loading: bool, + #[serde(skip)] + search_widget: Option, } impl Default for TemplateApp { @@ -79,6 +76,7 @@ impl Default for TemplateApp { pending_app_state: None, filtered_repos: None, filter_loading: false, + search_widget: Some(SearchWidget::new()), } } } @@ -141,17 +139,10 @@ impl TemplateApp { pub fn filter_repos_async(&mut self, query: &str, ctx: &egui::Context) { self.filter_loading = true; self.filtered_repos = None; - let query = query.to_string(); - let language = self.db.get_language(); - let ctx = ctx.clone(); // keep this, as it is used in the async block - wasm_bindgen_futures::spawn_local(async move { - let result = match crate::db::idb::open_waffle_db().await { - Ok(db_conn) => crate::db::idb::filter_repos_in_idb::(&db_conn, &language, &query).await.unwrap_or_default(), - Err(_) => vec![], - }; - ctx.data_mut(|d| d.insert_temp(Id::new("waffle_filtered_repos"), result)); - ctx.request_repaint(); - }); + if let Some(widget) = &mut self.search_widget { + widget.query = query.to_string(); + widget.search(&self.db.get_language(), ctx); + } } pub fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { @@ -338,14 +329,14 @@ impl TemplateApp { }); // Update filtered_repos from egui context temp data if available - if let Some(repos) = ctx.data(|d| d.get_temp::>(Id::new("waffle_filtered_repos"))) { - let is_empty = repos.is_empty(); - self.filtered_repos = Some(repos.clone()); - // Set app_state based on whether there is data - if is_empty { - self.app_state = AppState::Empty; - } else { + if let Some(widget) = &mut self.search_widget { + widget.update_results_from_ctx(ctx); + if !widget.results.is_empty() { + self.filtered_repos = Some(widget.results.clone()); self.app_state = AppState::Normal; + } else { + self.filtered_repos = Some(vec![]); + self.app_state = AppState::Empty; } } diff --git a/src/core/mod.rs b/src/core/mod.rs index 037eb02..e69de29 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1 +0,0 @@ -pub mod uiux; \ No newline at end of file diff --git a/src/core/uiux/mod.rs b/src/core/uiux/mod.rs index e092c24..e69de29 100644 --- a/src/core/uiux/mod.rs +++ b/src/core/uiux/mod.rs @@ -1 +0,0 @@ -pub mod search; \ No newline at end of file diff --git a/src/core/uiux/search.rs b/src/core/uiux/search.rs index b8e494d..e69de29 100644 --- a/src/core/uiux/search.rs +++ b/src/core/uiux/search.rs @@ -1,41 +0,0 @@ -use crate::db::github::Repository; -use crate::db::idb; -use egui::{Context, Id}; - -pub struct SearchWidget { - pub query: String, - pub results: Vec, - pub loading: bool, -} - -impl SearchWidget { - pub fn new() -> Self { - Self { - query: String::new(), - results: Vec::new(), - loading: false, - } - } - - pub fn search(&mut self, language: &str, ctx: &Context) { - let query = self.query.clone(); - let language = language.to_string(); - let ctx = ctx.clone(); - self.loading = true; - wasm_bindgen_futures::spawn_local(async move { - let result = match idb::open_waffle_db().await { - Ok(db_conn) => idb::filter_repos_in_idb::(&db_conn, &language, &query).await.unwrap_or_default(), - Err(_) => vec![], - }; - ctx.data_mut(|d| d.insert_temp(Id::new("waffle_search_results"), result)); - ctx.request_repaint(); - }); - } - - pub fn update_results_from_ctx(&mut self, ctx: &Context) { - if let Some(results) = ctx.data(|d| d.get_temp::>(Id::new("waffle_search_results"))) { - self.results = results.clone(); - self.loading = false; - } - } -} diff --git a/src/erust/mod.rs b/src/erust/mod.rs new file mode 100644 index 0000000..43dc6d8 --- /dev/null +++ b/src/erust/mod.rs @@ -0,0 +1,2 @@ +pub mod uiux; +pub mod state; \ No newline at end of file diff --git a/src/state.rs b/src/erust/state.rs similarity index 100% rename from src/state.rs rename to src/erust/state.rs diff --git a/src/erust/uiux/mod.rs b/src/erust/uiux/mod.rs new file mode 100644 index 0000000..e092c24 --- /dev/null +++ b/src/erust/uiux/mod.rs @@ -0,0 +1 @@ +pub mod search; \ No newline at end of file diff --git a/src/erust/uiux/search.rs b/src/erust/uiux/search.rs new file mode 100644 index 0000000..b8e494d --- /dev/null +++ b/src/erust/uiux/search.rs @@ -0,0 +1,41 @@ +use crate::db::github::Repository; +use crate::db::idb; +use egui::{Context, Id}; + +pub struct SearchWidget { + pub query: String, + pub results: Vec, + pub loading: bool, +} + +impl SearchWidget { + pub fn new() -> Self { + Self { + query: String::new(), + results: Vec::new(), + loading: false, + } + } + + pub fn search(&mut self, language: &str, ctx: &Context) { + let query = self.query.clone(); + let language = language.to_string(); + let ctx = ctx.clone(); + self.loading = true; + wasm_bindgen_futures::spawn_local(async move { + let result = match idb::open_waffle_db().await { + Ok(db_conn) => idb::filter_repos_in_idb::(&db_conn, &language, &query).await.unwrap_or_default(), + Err(_) => vec![], + }; + ctx.data_mut(|d| d.insert_temp(Id::new("waffle_search_results"), result)); + ctx.request_repaint(); + }); + } + + pub fn update_results_from_ctx(&mut self, ctx: &Context) { + if let Some(results) = ctx.data(|d| d.get_temp::>(Id::new("waffle_search_results"))) { + self.results = results.clone(); + self.loading = false; + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 1d6e2f3..6a6e0f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,4 +4,5 @@ mod app; mod db; pub use app::TemplateApp; pub use db::*; -pub mod utility; \ No newline at end of file +pub mod utility; +pub mod erust; \ No newline at end of file diff --git a/src/mod.rs b/src/mod.rs new file mode 100644 index 0000000..3a21617 --- /dev/null +++ b/src/mod.rs @@ -0,0 +1,2 @@ +pub mod erust; +pub mod state; \ No newline at end of file From b6842c6ff15de8dff4d0403fbca7db4f58c412e5 Mon Sep 17 00:00:00 2001 From: h0lybyte <5599058+h0lybyte@users.noreply.github.com> Date: Wed, 11 Jun 2025 12:19:31 -0400 Subject: [PATCH 02/14] we are keeping track of the app state twice, once under "app_state" and another as waffle_state, shouldnt we just have a single instance of operation for the state management. Lets just keep it as app state and have it be a new WaffleState{} structure. --- src/app.rs | 22 +++++++++++++++++----- src/erust/state.rs | 24 +++++++++++++++++++----- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/app.rs b/src/app.rs index 239cd8d..86d1590 100644 --- a/src/app.rs +++ b/src/app.rs @@ -4,7 +4,7 @@ use egui::Id; use crate::db::github::{GithubDb, Repository}; use crate::db::idb::LANGUAGES; use crate::erust::uiux::search::SearchWidget; -use crate::erust::state::AppState; +use crate::erust::state::{AppState, WaffleState}; #[derive(serde::Deserialize, serde::Serialize, Debug, Clone, PartialEq, Eq)] pub enum LoadingState { @@ -53,6 +53,8 @@ pub struct TemplateApp { #[serde(skip)] pending_app_state: Option, #[serde(skip)] + waffle_state: WaffleState, + #[serde(skip)] filtered_repos: Option>, #[serde(skip)] filter_loading: bool, @@ -74,6 +76,7 @@ impl Default for TemplateApp { toast_timer: 0.0, app_state: AppState::Init, pending_app_state: None, + waffle_state: WaffleState::new(), filtered_repos: None, filter_loading: false, search_widget: Some(SearchWidget::new()), @@ -291,6 +294,14 @@ impl TemplateApp { let filtered = self.filtered_repos.as_ref().cloned().unwrap_or_default(); ui.separator(); ui.label(format!("Results: {}", filtered.len())); + // --- Show app state at the bottom --- + ui.separator(); + ui.label(format!("App State: {:?}", self.waffle_state.app_state)); + if !self.waffle_state.log.is_empty() { + ui.separator(); + ui.label("App Log:"); + ui.label(&self.waffle_state.log); + } }); egui::CentralPanel::default().show(ctx, |ui| { // --- Logo image loading and display using egui_extras loader system --- @@ -331,13 +342,14 @@ impl TemplateApp { // Update filtered_repos from egui context temp data if available if let Some(widget) = &mut self.search_widget { widget.update_results_from_ctx(ctx); + // Use WaffleState to manage app state if !widget.results.is_empty() { - self.filtered_repos = Some(widget.results.clone()); - self.app_state = AppState::Normal; + self.waffle_state.set_ready(widget.results.clone()); } else { - self.filtered_repos = Some(vec![]); - self.app_state = AppState::Empty; + self.waffle_state.set_empty(); } + self.filtered_repos = Some(self.waffle_state.filtered_repos.clone()); + self.app_state = self.waffle_state.app_state.clone(); } // Show welcome dialog if DB is empty diff --git a/src/erust/state.rs b/src/erust/state.rs index afc8bec..ab82a5c 100644 --- a/src/erust/state.rs +++ b/src/erust/state.rs @@ -3,8 +3,10 @@ use crate::db::github::Repository; #[derive(Debug, Clone, PartialEq, Eq)] pub enum AppState { Init, - Normal, Empty, + Syncing, + Normal, + Ready, Error(String), } @@ -12,6 +14,7 @@ pub enum AppState { pub struct WaffleState { pub app_state: AppState, pub filtered_repos: Vec, + pub log: String, } impl WaffleState { @@ -19,6 +22,7 @@ impl WaffleState { Self { app_state: AppState::Init, filtered_repos: Vec::new(), + log: String::new(), } } @@ -27,21 +31,31 @@ impl WaffleState { self.filtered_repos.clear(); } - pub fn set_normal(&mut self, repos: Vec) { + pub fn set_syncing(&mut self) { + self.app_state = AppState::Syncing; + } + + pub fn set_ready(&mut self, repos: Vec) { if repos.is_empty() { self.set_empty(); } else { - self.app_state = AppState::Normal; + self.app_state = AppState::Ready; self.filtered_repos = repos; } } pub fn set_error(&mut self, msg: String) { - self.app_state = AppState::Error(msg); + self.app_state = AppState::Error(msg.clone()); + self.log.push_str(&format!("Error: {}\n", msg)); self.filtered_repos.clear(); } + pub fn log(&mut self, msg: &str) { + self.log.push_str(msg); + self.log.push('\n'); + } + pub fn is_ready(&self) -> bool { - matches!(self.app_state, AppState::Normal) + matches!(self.app_state, AppState::Ready) } } From a4f03ba4fdf36e3d734c05467edcdc14c10bbedf Mon Sep 17 00:00:00 2001 From: h0lybyte <5599058+h0lybyte@users.noreply.github.com> Date: Wed, 11 Jun 2025 13:54:40 -0400 Subject: [PATCH 03/14] feat: added captcha support. --- index.html | 53 +++++++++++++++++++++++++++++++++++++- src/erust/uiux/hcaptcha.rs | 42 ++++++++++++++++++++++++++++++ src/erust/uiux/mod.rs | 3 ++- 3 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 src/erust/uiux/hcaptcha.rs diff --git a/index.html b/index.html index c60e235..5f112a3 100644 --- a/index.html +++ b/index.html @@ -7,7 +7,7 @@ - eframe template + Waffle - Template @@ -141,6 +141,57 @@ }); } + + + + + + diff --git a/src/erust/uiux/hcaptcha.rs b/src/erust/uiux/hcaptcha.rs new file mode 100644 index 0000000..02ac35f --- /dev/null +++ b/src/erust/uiux/hcaptcha.rs @@ -0,0 +1,42 @@ +// hcaptcha.rs - hCaptcha integration for WASM/egui +use wasm_bindgen::prelude::*; +use wasm_bindgen::JsValue; +use web_sys::js_sys; +use std::cell::RefCell; + +thread_local! { + static LAST_TOKEN: RefCell> = RefCell::new(None); +} + +#[wasm_bindgen] +pub fn pass_captcha_token(token: String) { + LAST_TOKEN.with(|t| t.replace(Some(token))); +} + +pub fn get_captcha_token() -> Option { + LAST_TOKEN.with(|t| t.borrow().clone()) +} + +pub fn clear_captcha_token() { + LAST_TOKEN.with(|t| t.replace(None)); +} + +/// Call this from Rust to open the captcha overlay +pub fn open_captcha() { + let _ = js_sys::Reflect::get(&js_sys::global(), &JsValue::from_str("JSRust")) + .and_then(|f| if f.is_function() { + let func = js_sys::Function::from(f); + func.call1(&JsValue::NULL, &JsValue::from_str("openCaptcha")).ok(); + Ok(()) + } else { Ok(()) }); +} + +/// Call this from Rust to close the captcha overlay +pub fn close_captcha() { + let _ = js_sys::Reflect::get(&js_sys::global(), &JsValue::from_str("JSRust")) + .and_then(|f| if f.is_function() { + let func = js_sys::Function::from(f); + func.call1(&JsValue::NULL, &JsValue::from_str("closeCaptcha")).ok(); + Ok(()) + } else { Ok(()) }); +} diff --git a/src/erust/uiux/mod.rs b/src/erust/uiux/mod.rs index e092c24..3c6712e 100644 --- a/src/erust/uiux/mod.rs +++ b/src/erust/uiux/mod.rs @@ -1 +1,2 @@ -pub mod search; \ No newline at end of file +pub mod search; +pub mod hcaptcha; \ No newline at end of file From 8318dfc8ad89ff7cc5eccefac6c4218f479eb71a Mon Sep 17 00:00:00 2001 From: h0lybyte <5599058+h0lybyte@users.noreply.github.com> Date: Wed, 11 Jun 2025 15:23:14 -0400 Subject: [PATCH 04/14] feat: adding basic auth --- src/app.rs | 13 ++++++++++ src/erust/uiux/auth.rs | 54 ++++++++++++++++++++++++++++++++++++++++++ src/erust/uiux/mod.rs | 3 ++- 3 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 src/erust/uiux/auth.rs diff --git a/src/app.rs b/src/app.rs index 86d1590..d84b367 100644 --- a/src/app.rs +++ b/src/app.rs @@ -5,6 +5,7 @@ use crate::db::github::{GithubDb, Repository}; use crate::db::idb::LANGUAGES; use crate::erust::uiux::search::SearchWidget; use crate::erust::state::{AppState, WaffleState}; +use crate::erust::uiux::auth::AuthWidget; #[derive(serde::Deserialize, serde::Serialize, Debug, Clone, PartialEq, Eq)] pub enum LoadingState { @@ -60,6 +61,8 @@ pub struct TemplateApp { filter_loading: bool, #[serde(skip)] search_widget: Option, + #[serde(skip)] + auth_widget: AuthWidget, } impl Default for TemplateApp { @@ -80,6 +83,7 @@ impl Default for TemplateApp { filtered_repos: None, filter_loading: false, search_widget: Some(SearchWidget::new()), + auth_widget: AuthWidget::new(false), } } } @@ -339,6 +343,15 @@ impl TemplateApp { } }); + // --- Authentication Widget (Login/Register) --- + egui::Window::new("Authentication") + .collapsible(false) + .resizable(false) + .anchor(egui::Align2::CENTER_TOP, egui::Vec2::new(0.0, 40.0)) + .show(ctx, |ui| { + self.auth_widget.show(ctx, ui); + }); + // Update filtered_repos from egui context temp data if available if let Some(widget) = &mut self.search_widget { widget.update_results_from_ctx(ctx); diff --git a/src/erust/uiux/auth.rs b/src/erust/uiux/auth.rs new file mode 100644 index 0000000..37703b6 --- /dev/null +++ b/src/erust/uiux/auth.rs @@ -0,0 +1,54 @@ +// erust/uiux/auth.rs +use egui::{Context, Ui}; +use crate::erust::uiux::hcaptcha; + +pub struct AuthWidget { + pub email: String, + pub password: String, + pub captcha_token: Option, + pub error: Option, + pub is_register: bool, +} + +impl AuthWidget { + pub fn new(is_register: bool) -> Self { + Self { + email: String::new(), + password: String::new(), + captcha_token: None, + error: None, + is_register, + } + } + + pub fn show(&mut self, ctx: &Context, ui: &mut Ui) { + ui.heading(if self.is_register { "Register" } else { "Login" }); + ui.label("Email:"); + ui.text_edit_singleline(&mut self.email); + ui.label("Password:"); + ui.add(egui::TextEdit::singleline(&mut self.password).password(true)); + if ui.button("Solve Captcha").clicked() { + hcaptcha::open_captcha(); + } + if let Some(token) = hcaptcha::get_captcha_token() { + self.captcha_token = Some(token); + ui.label("Captcha solved!"); + } else { + ui.label("Captcha required"); + } + if ui.button(if self.is_register { "Register" } else { "Login" }).clicked() { + if self.email.is_empty() || self.password.is_empty() || self.captcha_token.is_none() { + self.error = Some("All fields and captcha are required".to_string()); + } else { + // Here you would call your Supabase API with email, password, and captcha_token + // Example: supabase_auth(self.email.clone(), self.password.clone(), self.captcha_token.clone()) + self.error = Some("(Stub) Would call Supabase here".to_string()); + } + } + if let Some(err) = &self.error { + ui.colored_label(egui::Color32::RED, err); + } + } +} + +// Make AuthWidget public for use in other modules diff --git a/src/erust/uiux/mod.rs b/src/erust/uiux/mod.rs index 3c6712e..538d608 100644 --- a/src/erust/uiux/mod.rs +++ b/src/erust/uiux/mod.rs @@ -1,2 +1,3 @@ pub mod search; -pub mod hcaptcha; \ No newline at end of file +pub mod hcaptcha; +pub mod auth; \ No newline at end of file From 64634e5cf208d600fd29a7abd699eb90e300c563 Mon Sep 17 00:00:00 2001 From: h0lybyte <5599058+h0lybyte@users.noreply.github.com> Date: Wed, 11 Jun 2025 15:28:46 -0400 Subject: [PATCH 05/14] fix: updating the auth view --- src/erust/uiux/auth.rs | 109 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 101 insertions(+), 8 deletions(-) diff --git a/src/erust/uiux/auth.rs b/src/erust/uiux/auth.rs index 37703b6..9a4af34 100644 --- a/src/erust/uiux/auth.rs +++ b/src/erust/uiux/auth.rs @@ -2,27 +2,45 @@ use egui::{Context, Ui}; use crate::erust::uiux::hcaptcha; +pub enum AuthView { + Login, + Register, + Help, +} + pub struct AuthWidget { pub email: String, pub password: String, + pub confirm_password: String, // Only used for Register pub captcha_token: Option, pub error: Option, - pub is_register: bool, + pub view: AuthView, + pub is_registered: bool, } impl AuthWidget { - pub fn new(is_register: bool) -> Self { + pub fn new(is_registered: bool) -> Self { Self { email: String::new(), password: String::new(), + confirm_password: String::new(), captcha_token: None, error: None, - is_register, + view: if is_registered { AuthView::Login } else { AuthView::Register }, + is_registered, } } pub fn show(&mut self, ctx: &Context, ui: &mut Ui) { - ui.heading(if self.is_register { "Register" } else { "Login" }); + match self.view { + AuthView::Login => self.show_login(ctx, ui), + AuthView::Register => self.show_register(ctx, ui), + AuthView::Help => self.show_help(ctx, ui), + } + } + + fn show_login(&mut self, _ctx: &Context, ui: &mut Ui) { + ui.heading("Login"); ui.label("Email:"); ui.text_edit_singleline(&mut self.email); ui.label("Password:"); @@ -36,18 +54,93 @@ impl AuthWidget { } else { ui.label("Captcha required"); } - if ui.button(if self.is_register { "Register" } else { "Login" }).clicked() { + if ui.button("Login").clicked() { if self.email.is_empty() || self.password.is_empty() || self.captcha_token.is_none() { self.error = Some("All fields and captcha are required".to_string()); } else { - // Here you would call your Supabase API with email, password, and captcha_token - // Example: supabase_auth(self.email.clone(), self.password.clone(), self.captcha_token.clone()) - self.error = Some("(Stub) Would call Supabase here".to_string()); + self.error = Some("(Stub) Would call Supabase login here".to_string()); + } + } + if let Some(err) = &self.error { + ui.colored_label(egui::Color32::RED, err); + } + ui.horizontal(|ui| { + if ui.button("Register").clicked() { + self.view = AuthView::Register; + self.error = None; + } + if ui.button("Help").clicked() { + self.view = AuthView::Help; + self.error = None; + } + }); + } + + fn show_register(&mut self, _ctx: &Context, ui: &mut Ui) { + ui.heading("Register"); + ui.label("Email:"); + ui.text_edit_singleline(&mut self.email); + ui.label("Password:"); + ui.add(egui::TextEdit::singleline(&mut self.password).password(true)); + ui.label("Confirm Password:"); + ui.add(egui::TextEdit::singleline(&mut self.confirm_password).password(true)); + if ui.button("Solve Captcha").clicked() { + hcaptcha::open_captcha(); + } + if let Some(token) = hcaptcha::get_captcha_token() { + self.captcha_token = Some(token); + ui.label("Captcha solved!"); + } else { + ui.label("Captcha required"); + } + if ui.button("Register").clicked() { + if self.email.is_empty() || self.password.is_empty() || self.confirm_password.is_empty() || self.captcha_token.is_none() { + self.error = Some("All fields and captcha are required".to_string()); + } else if self.password != self.confirm_password { + self.error = Some("Passwords do not match".to_string()); + } else { + self.error = Some("(Stub) Would call Supabase register here".to_string()); } } if let Some(err) = &self.error { ui.colored_label(egui::Color32::RED, err); } + ui.horizontal(|ui| { + if ui.button("Back to Login").clicked() { + self.view = AuthView::Login; + self.error = None; + } + if ui.button("Help").clicked() { + self.view = AuthView::Help; + self.error = None; + } + }); + } + + fn show_help(&mut self, _ctx: &Context, ui: &mut Ui) { + ui.heading("Forgot your password?"); + ui.label("Enter your email to receive a reset link:"); + ui.text_edit_singleline(&mut self.email); + if ui.button("Send Reset Link").clicked() { + if self.email.is_empty() { + self.error = Some("Email is required".to_string()); + } else { + self.error = Some("(Stub) Would call Supabase password reset here".to_string()); + } + } + if let Some(err) = &self.error { + ui.colored_label(egui::Color32::RED, err); + } + ui.horizontal(|ui| { + if ui.button("Back to Login").clicked() { + self.view = AuthView::Login; + self.error = None; + } + if ui.button("Register").clicked() { + self.view = AuthView::Register; + self.error = None; + } + }); } } From 16bb65865ad5f0109fa71eeae69fafbae8299a3d Mon Sep 17 00:00:00 2001 From: h0lybyte <5599058+h0lybyte@users.noreply.github.com> Date: Wed, 11 Jun 2025 15:32:26 -0400 Subject: [PATCH 06/14] style: adding some very basic tailwindcss and styling up some of the less major content. --- index.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index 5f112a3..bcaeba6 100644 --- a/index.html +++ b/index.html @@ -144,10 +144,12 @@ + + + + + + + + + + + + + + + + - + - +
-

- Loading… -

-
+

Loading…

+
-