================================================================================ DETAILED ANALYSIS: Config Struct Usage - cargo run vs Standalone EXE ================================================================================ EXECUTIVE SUMMARY ================================================================================ Config modifications don't work in either cargo run OR standalone EXE after startup. They only appear to work in cargo run because each run is a NEW process that reloads the config file from disk. ROOT CAUSE: One-time initialization with frozen values, no reload mechanism. KEY FINDINGS: 1. config.toml is loaded ONCE in main.rs:54 2. Config values COPIED into BackgroundRenderer (immutable Arc) 3. Config STORED in AzureAppManager (never updated) 4. Every frame reads SAME frozen values 5. ZERO reload/hot-update mechanism exists 6. Working directory only affects WHERE file is looked for, not HOW changes propagate ================================================================================ 1. CONFIG LOADING - src/main.rs Lines 35-80 ================================================================================ #[tokio::main] async fn main() -> Result<(), Box> { // LINE 54: LOADED ONCE let app_config = config::load_config(); // LINES 60-64: VALUES APPLIED TO EFRAME let viewport_builder = egui::ViewportBuilder::default() .with_inner_size([app_config.window.width, app_config.window.height]) .with_min_inner_size([app_config.window.min_width, app_config.window.min_height]) .with_transparent(app_config.window.use_transparency) .with_icon(icon_data); // LINES 73-77: MOVED INTO MANAGER (ownership transferred, never accessed in main again) eframe::run_native( "Create App Secret", options, Box::new(move |cc| Ok(Box::new(AzureAppManager::new(cc, auth, app_config)))), )?; Ok(()) } CRITICAL: The 'move' keyword transfers ownership to the closure. After this point, no other code in main can access app_config. ================================================================================ 2. CONFIG FILE PATH RESOLUTION - src/config/window_config.rs Lines 232-295 ================================================================================ pub fn load_config() -> Config { let config_path = if let Ok(exe_path) = std::env::current_exe() { exe_path .parent() .map(|p| p.join("config.toml")) .unwrap_or_else(|| Path::new("./config.toml").to_path_buf()) } else { Path::new("./config.toml").to_path_buf() }; if config_path.exists() { // Load and parse TOML } else { // Create defaults } Config::default() // Fallback } CARGO RUN PATH: current_exe() = C:\...\I-SecretUpdate\target\debug\create-app-secret.exe parent = C:\...\I-SecretUpdate\target\debug\ looks for C:\...\I-SecretUpdate\target\debug\config.toml ❌ NOT FOUND falls back to ./config.toml resolves to C:\...\I-SecretUpdate\config.toml ✓ FOUND STANDALONE EXE PATH: current_exe() = C:\wherever\user\puts\Create-App-Secret.exe parent = C:\wherever\user\puts\ looks for C:\wherever\user\puts\config.toml ✓ OR ❌ if not found, falls back to ./config.toml KEY POINT: Both use identical code and path resolution logic. The file location is the ONLY difference, not how changes are handled. ================================================================================ 3. FIRST INITIALIZATION - FREEZE POINT 1 & 2 - src/app.rs Lines 20-52 ================================================================================ pub struct AzureAppManager { state: AppState, auth: Arc, graph_client: GraphApiClient, keyvault_client: KeyVaultClient, vault_discovery: VaultDiscovery, config: Config, // ← FREEZE POINT 2: Stored, never mutated position_applied: bool, } impl AzureAppManager { pub fn new(cc: &eframe::CreationContext<'_>, auth: Arc, config: Config) -> Self { let mut state = AppState::new(); // FREEZE POINT 1: Create BackgroundRenderer with COPIED config values let background_renderer = Some(Arc::new(BackgroundRenderer::new( &cc.egui_ctx, &config.appearance.background_image, config.appearance.background_opacity, // ← COPIED HERE config.appearance.fallback_color, // ← COPIED HERE config.appearance.fallback_color_opacity, // ← COPIED HERE ))); state.background_renderer = background_renderer; Self { state, auth, graph_client, keyvault_client, vault_discovery, config, // ← FREEZE POINT 2: Stored as owned, never updated position_applied: false, } } } WHAT GETS FROZEN: FREEZE POINT 1 (BackgroundRenderer): - opacity value COPIED - fallback_color value COPIED - fallback_color_opacity value COPIED - These are stored in Arc (immutable) - Used every frame via render_fullscreen() - NEVER RECALCULATED OR UPDATED FREEZE POINT 2 (Config Field): - Entire Config struct stored - Not mutable reference, not Arc> - Owned field that cannot be changed - Read every frame in update() - NEVER RELOADED FROM DISK ================================================================================ 4. BACKGROUND RENDERING - EVERY FRAME - src/ui/background.rs Lines 106-152 ================================================================================ pub fn render_fullscreen(&self, ctx: &Context) { let screen_rect = ctx.screen_rect(); let painter = ctx.layer_painter(egui::LayerId::background()); // Render with FROZEN fallback_color_opacity let fallback_with_opacity = Color32::from_rgba_unmultiplied( self.fallback_color.r(), self.fallback_color.g(), self.fallback_color.b(), (self.fallback_color_opacity * 255.0) as u8, // ← FROZEN VALUE ); painter.rect_filled(screen_rect, 0.0, fallback_with_opacity); if let Some(texture) = &self.texture { // Render with FROZEN opacity let tint = Color32::from_rgba_unmultiplied( 255, 255, 255, (self.opacity * 255.0) as u8 // ← FROZEN VALUE ); painter.image(texture.id(), screen_rect, uv_rect, tint); } } CALLED EVERY FRAME FROM: src/app.rs Line 506 if let Some(bg) = &self.state.background_renderer { bg.render_fullscreen(ctx); // Uses same frozen values } RESULT: Background renders with identical opacity and colors every frame. User modifies config.toml, but application still renders with original values. ================================================================================ 5. COLOR APPLICATION - EVERY FRAME - src/app.rs Lines 375-457 ================================================================================ fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { let mut visuals = egui::Visuals::dark(); // LINE 380: READ FROM FROZEN CONFIG let colors = self.config.colors.as_ref(); if let Some(color_config) = colors { let slider = &color_config.slider; let text = &color_config.text; // Apply colors visuals.widgets.noninteractive.fg_stroke.color = egui::Color32::from_rgb(text.normal[0], text.normal[1], text.normal[2]); visuals.widgets.inactive.fg_stroke.color = egui::Color32::from_rgb(text.inactive[0], text.inactive[1], text.inactive[2]); visuals.widgets.hovered.fg_stroke.color = egui::Color32::from_rgb(text.hover[0], text.hover[1], text.hover[2]); // ... more color applications ... // Apply slider colors visuals.widgets.noninteractive.bg_fill = egui::Color32::from_rgb(slider.inactive[0], slider.inactive[1], slider.inactive[2]); // ... more slider col