git.lucas.co / cce-mail
mail client (IMAP/SMTP)
git clone https://git.lucas.co/cce-mail.git

commit4392b57449f7a8fb686816edea6fc9ec21c4e9fb
parent199bc2d68a
authorLucas Galante <[email protected]>
date2026-08-21 10:37
feat: account switcher is a real Dropdown; the Accounts page retires

The bar's "Accounts" Button (which swapped the list pane into an
accounts view) becomes a Dropdown beside the folder switcher: options
are the account emails plus a trailing "Manage Accounts…" pseudo-entry
(index == accounts.len()) that spawns cce-system-interface and restores
the trigger to the active account. Both bar controls now wear the
DE-wide closed-dropdown trough.

The accounts.json re-read moves from Accounts-page entry to dropdown
open, keeping selection by email with fall-to-default when the selected
account vanished — same semantics, new trigger point. SelectAccount
keeps dropdown.selected in sync for programmatic switches.

Folder::Accounts is deleted outright rather than left unreachable: the
`== Accounts` gates are invisible to the compiler, but deleting the
variant makes every one a compile error, which is how the ~20 dead
sites (accounts list rows, detail-pane account card, manage button,
routing and keyboard gates) were found and excised.

Live-verified: open re-reads accounts.json (3 accounts listed), select
switches account + syncs + persists the sidecar, Manage Accounts…
spawns the settings page without relabeling the trigger.

Co-Authored-By: Claude <[email protected]>

 src/main.rs | 407 +++++++++++++++++++++++-------------------------------------
 1 file changed, 155 insertions(+), 252 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index e8addc6..5373b10 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -18,7 +18,6 @@ enum Folder {
     Sent,
     Drafts,
     Trash,
-    Accounts,
 }
 
 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
@@ -134,7 +133,11 @@ struct ClearEmailApp {
     // Navigation / Sidebar
     btn_compose: cce_ui::widget::Adapted<cce_ui::widget::Button>,
     menubar: cce_ui::widget::Adapted<MenuBar>,
-    btn_accounts: cce_ui::widget::Adapted<cce_ui::widget::Button>,
+    /// Account switcher beside the folder dropdown: options are the account
+    /// emails plus a trailing "Manage Accounts…" pseudo-entry (management
+    /// lives in cce-system-interface). Options refresh from accounts.json on
+    /// every open — the job the retired Accounts page did on entry.
+    account_dropdown: cce_ui::widget::Adapted<cce_ui::widget::Dropdown>,
 
     // Search and List View
     search_box: cce_ui::widget::Adapted<TextBox>,
@@ -159,10 +162,9 @@ struct ClearEmailApp {
     btn_compose_cancel: cce_ui::widget::Adapted<cce_ui::widget::Button>,
     btn_compose_attach: cce_ui::widget::Adapted<cce_ui::widget::Button>,
 
-    // Accounts (view/switch only — management lives in cce-system-interface)
+    // Accounts (switch via the bar dropdown — management lives in cce-system-interface)
     accounts: Vec<AccountInfo>,
     selected_account_idx: usize,
-    btn_manage_accounts: cce_ui::widget::Adapted<cce_ui::widget::Button>,
 
     // Application state
     emails: Vec<Email>,
@@ -211,6 +213,19 @@ fn get_accounts_path() -> std::path::PathBuf {
     p.join("accounts.json")
 }
 
+/// The bar account-switcher's option list: one row per account email, plus the
+/// trailing management pseudo-entry (index == accounts.len()), which opens
+/// cce-system-interface instead of switching.
+const MANAGE_ACCOUNTS_OPTION: &str = "Manage Accounts…";
+
+fn account_dropdown_options(accounts: &[AccountInfo]) -> Vec<String> {
+    accounts
+        .iter()
+        .map(|a| a.email.clone())
+        .chain(std::iter::once(MANAGE_ACCOUNTS_OPTION.to_string()))
+        .collect()
+}
+
 fn load_accounts() -> Vec<AccountInfo> {
     let path = get_accounts_path();
     if path.exists() {
@@ -1749,7 +1764,7 @@ impl ClearEmailApp {
         self.ui_context.register_widget(id, ptr);
         let (id, ptr) = (self.menubar.id(), self.menubar.as_ptr_mut());
         self.ui_context.register_widget(id, ptr);
-        let (id, ptr) = (self.btn_accounts.id(), self.btn_accounts.as_ptr_mut());
+        let (id, ptr) = (self.account_dropdown.id(), self.account_dropdown.as_ptr_mut());
         self.ui_context.register_widget(id, ptr);
         let (id, ptr) = (self.btn_compose_send.id(), self.btn_compose_send.as_ptr_mut());
         self.ui_context.register_widget(id, ptr);
@@ -1763,8 +1778,6 @@ impl ClearEmailApp {
         self.ui_context.register_widget(id, ptr);
         let (id, ptr) = (self.btn_unread.id(), self.btn_unread.as_ptr_mut());
         self.ui_context.register_widget(id, ptr);
-        let (id, ptr) = (self.btn_manage_accounts.id(), self.btn_manage_accounts.as_ptr_mut());
-        self.ui_context.register_widget(id, ptr);
         for btn in self.email_buttons.iter_mut() {
             let (id, ptr) = (btn.id(), btn.as_ptr_mut());
             self.ui_context.register_widget(id, ptr);
@@ -1866,33 +1879,12 @@ impl ClearEmailApp {
         // modal panel (the popover-occlusion clamp only knows registered popovers).
         let modal_open = self.compose_open;
         if modal_open {
-        } else if self.current_folder == Folder::Accounts {
-            for (idx, acc) in self.accounts.iter().enumerate() {
-                if let Some(draw_y) = self.email_list.get_item_draw_y(idx, 0.0) {
-                    labels.push(TextLabel {
-                        text: acc.email.clone(),
-                        x: list_x + 20.0,
-                        y: draw_y + 12.0,
-                        font_size: 11.0,
-                        color: [0xff, 0xff, 0xff],
-                    });
-
-                    labels.push(TextLabel {
-                        text: if acc.is_default { "Default Account".to_string() } else { "Secondary Account".to_string() },
-                        x: list_x + 20.0,
-                        y: draw_y + 28.0,
-                        font_size: 9.0,
-                        color: if acc.is_default { [0x3a, 0xff, 0x80] } else { [0x70, 0x70, 0x75] },
-                    });
-                }
-            }
         } else {
             let current_folder_str = match self.current_folder {
                 Folder::Inbox => "inbox",
                 Folder::Sent => "sent",
                 Folder::Drafts => "drafts",
                 Folder::Trash => "trash",
-                _ => "inbox",
             };
             let search_text = if self.search_box.editing { &self.search_box.edit_buffer } else { &self.search_box.text };
             let search_lower = search_text.to_lowercase();
@@ -1963,36 +1955,6 @@ impl ClearEmailApp {
 
         // 4. Detail View Content (same modal gate as the list labels above)
         if modal_open {
-        } else if self.current_folder == Folder::Accounts {
-            if self.selected_account_idx < self.accounts.len() {
-                let acc = &self.accounts[self.selected_account_idx];
-
-                // Subject Header (Account email)
-                labels.push(TextLabel {
-                    text: acc.email.clone(),
-                    x: detail_x,
-                    y: 60.0 + MENUBAR_H,
-                    font_size: 15.0,
-                    color: [0xff, 0xff, 0xff],
-                });
-
-                // Settings details
-                labels.push(TextLabel { text: format!("Incoming Server (IMAP): {}", acc.imap), x: detail_x, y: 95.0 + MENUBAR_H, font_size: 11.0, color: [0xb0, 0xb0, 0xb8] });
-                labels.push(TextLabel { text: format!("Outgoing Server (SMTP): {}", acc.smtp), x: detail_x, y: 120.0 + MENUBAR_H, font_size: 11.0, color: [0xb0, 0xb0, 0xb8] });
-                let auth_text = if acc.is_oauth {
-                    "Authentication:          OAuth2 (Google)"
-                } else {
-                    "Authentication:          SSL/TLS, Normal Password"
-                };
-                labels.push(TextLabel { text: auth_text.to_string(), x: detail_x, y: 145.0 + MENUBAR_H, font_size: 11.0, color: [0x83, 0x83, 0x8a] });
-                labels.push(TextLabel {
-                    text: format!("Default Account:         {}", if acc.is_default { "Yes" } else { "No" }),
-                    x: detail_x,
-                    y: 170.0 + MENUBAR_H,
-                    font_size: 11.0,
-                    color: if acc.is_default { [0x3a, 0xff, 0x80] } else { [0x83, 0x83, 0x8a] },
-                });
-            }
         } else if let Some(selected_id) = self.selected_email_id {
             if let Some(email) = self.emails.iter().find(|e| e.id == selected_id) {
                 // Subject Header
@@ -2122,7 +2084,8 @@ impl Application for ClearEmailApp {
         let btn_compose = Button::new(10.0, 15.0, 26.0, 26.0).with_label("+");
 
         // Folder selection is the bar's right-aligned title dropdown (the
-        // designer's pane-switcher idiom); Accounts is a plain button beside it.
+        // designer's pane-switcher idiom); the account switcher is a real
+        // Dropdown beside it.
         let menubar = MenuBar::new(0.0, 0.0, 800.0, MENUBAR_H)
             .with_recess(true)
             .with_title("Inbox")
@@ -2131,8 +2094,6 @@ impl Application for ClearEmailApp {
             .with_item("Message", &["Reply", "Delete", "Mark Read/Unread"])
             .with_context_options(vec!["Inbox".to_string(), "Sent".to_string(), "Drafts".to_string(), "Trash".to_string()], 0);
 
-        let btn_accounts = Button::new(0.0, 5.0, 90.0, 26.0).with_label("Accounts");
-
         let mut search_box = TextBox::new(String::new()).with_multiline(false).with_draw_bg_border(true);
         search_box.font_size = 11.0;
 
@@ -2151,7 +2112,11 @@ impl Application for ClearEmailApp {
             .or_else(|| accounts.iter().position(|a| a.is_default))
             .unwrap_or(0);
 
-        let btn_manage_accounts = Button::new(66.0, 15.0, 300.0, 26.0).with_label("Manage Accounts...");
+        let account_dropdown = cce_ui::widget::Dropdown::new(
+            account_dropdown_options(&accounts),
+            selected_account_idx,
+        )
+        .with_font_family(&cce_ui::layout::parse_font_string(&cce_ui::layout::menubar_font()).0);
 
         let mut detail_body = TextBox::new(String::new()).with_multiline(true).with_draw_bg_border(false);
         detail_body.font_size = 12.0;
@@ -2215,7 +2180,7 @@ impl Application for ClearEmailApp {
             keys: EmailKeys::load(),
             btn_compose,
             menubar,
-            btn_accounts,
+            account_dropdown,
             search_box,
             email_list,
             email_buttons: Vec::new(),
@@ -2234,7 +2199,6 @@ impl Application for ClearEmailApp {
             btn_compose_attach,
             accounts,
             selected_account_idx,
-            btn_manage_accounts,
             emails,
             current_folder: Folder::Inbox,
             selected_email_id: None,
@@ -2274,37 +2238,8 @@ impl Application for ClearEmailApp {
                 self.selected_email_id = None;
                 self.email_list.set_scroll_y(0.0);
                 self.body_scroll = 0.0;
-                if f == Folder::Accounts {
-                    // Accounts are managed by cce-system-interface — re-read the
-                    // shared accounts.json on every entry so its changes appear
-                    // without an app restart. Keep the selection by email; if
-                    // that account is gone, fall to the default.
-                    let prev_email = self
-                        .accounts
-                        .get(self.selected_account_idx)
-                        .map(|a| a.email.clone());
-                    self.accounts = load_accounts();
-                    let new_idx = prev_email
-                        .as_deref()
-                        .and_then(|e| self.accounts.iter().position(|a| a.email == e))
-                        .or_else(|| self.accounts.iter().position(|a| a.is_default))
-                        .unwrap_or(0);
-                    let changed_account =
-                        self.accounts.get(new_idx).map(|a| a.email.as_str()) != prev_email.as_deref();
-                    self.selected_account_idx = new_idx;
-                    if changed_account {
-                        if let Some(email) = self.accounts.get(new_idx).map(|a| a.email.clone()) {
-                            self.emails = load_emails_for_account(&email);
-                            save_selected_account_email(&email);
-                            self.start_sync(true);
-                        } else {
-                            self.emails = Vec::new();
-                        }
-                    }
-                } else {
-                    // Mail folders refresh from the server on entry (throttled).
-                    self.start_sync(false);
-                }
+                // Folders refresh from the server on entry (throttled).
+                self.start_sync(false);
                 *needs_rebuild = true;
                 self.needs_rebuild = true;
             }
@@ -2585,6 +2520,7 @@ impl Application for ClearEmailApp {
             }
             AppMessage::SelectAccount(idx) => {
                 self.selected_account_idx = idx;
+                self.account_dropdown.selected = idx.min(self.accounts.len().saturating_sub(1));
                 if let Some(email) = self.accounts.get(idx).map(|a| a.email.clone()) {
                     self.emails = load_emails_for_account(&email);
                     save_selected_account_email(&email);
@@ -2725,7 +2661,6 @@ impl Application for ClearEmailApp {
             Folder::Sent => "sent",
             Folder::Drafts => "drafts",
             Folder::Trash => "trash",
-            Folder::Accounts => "accounts",
         };
 
         let list_x = 10.0;
@@ -2743,6 +2678,9 @@ impl Application for ClearEmailApp {
             if self.menubar.popover_rect().is_some() {
                 self.ui_context.register_popover(&mut self.menubar);
             }
+            if self.account_dropdown.popover_rect().is_some() {
+                self.ui_context.register_popover(&mut self.account_dropdown);
+            }
             // Bar title = current folder (the context-dropdown trigger), with
             // the inbox unread count folded in where the sidebar badge lived.
             let inbox_unread = self.emails.iter().filter(|e| e.folder == "inbox" && !e.read).count();
@@ -2752,47 +2690,38 @@ impl Application for ClearEmailApp {
                 Folder::Sent => "Sent".to_string(),
                 Folder::Drafts => "Drafts".to_string(),
                 Folder::Trash => "Trash".to_string(),
-                Folder::Accounts => "Accounts".to_string(),
             };
-            if let Some(ci) = match self.current_folder {
-                Folder::Inbox => Some(0),
-                Folder::Sent => Some(1),
-                Folder::Drafts => Some(2),
-                Folder::Trash => Some(3),
-                Folder::Accounts => None,
-            } {
-                self.menubar.set_context_selected(ci);
-            }
-            // Fixed offset from the right edge — anchoring to the title would
-            // make the button drift as the folder name changes length.
-            self.btn_accounts.set_rect(w_f32 - 250.0, 5.0, 90.0, 26.0);
+            self.menubar.set_context_selected(match self.current_folder {
+                Folder::Inbox => 0,
+                Folder::Sent => 1,
+                Folder::Drafts => 2,
+                Folder::Trash => 3,
+            });
+            // Fixed offset from the right edge — anchoring to the folder title
+            // would make the switcher drift as the folder name changes length.
+            self.account_dropdown.set_rect(w_f32 - 360.0, 5.0, 210.0, 26.0);
 
             cce_ui::scale::set_scale_factor(scale as f32);
             self.btn_compose.set_rect(list_x, 15.0 + MENUBAR_H, 26.0, 26.0);
 
-            // Search box / Add Account and Scrolling list
-            let list_count = if self.current_folder == Folder::Accounts {
-                self.btn_manage_accounts.set_rect(list_x + 36.0, 15.0 + MENUBAR_H, 264.0, 26.0);
-                self.accounts.len()
-            } else {
-                self.search_box.set_rect(list_x + 36.0, 15.0 + MENUBAR_H, 264.0, 26.0);
-                
-                // Get filtered emails count for bounds setup
-                self.emails.iter()
-                    .filter(|e| e.folder == current_folder_str)
-                    .filter(|e| {
-                        let search_text = if self.search_box.editing { &self.search_box.edit_buffer } else { &self.search_box.text };
-                        let search_lower = search_text.to_lowercase();
-                        if search_lower.is_empty() {
-                            true
-                        } else {
-                            e.from.to_lowercase().contains(&search_lower) ||
-                            e.subject.to_lowercase().contains(&search_lower) ||
-                            e.body.to_lowercase().contains(&search_lower)
-                        }
-                    })
-                    .count()
-            };
+            // Search box and Scrolling list
+            self.search_box.set_rect(list_x + 36.0, 15.0 + MENUBAR_H, 264.0, 26.0);
+
+            // Get filtered emails count for bounds setup
+            let list_count = self.emails.iter()
+                .filter(|e| e.folder == current_folder_str)
+                .filter(|e| {
+                    let search_text = if self.search_box.editing { &self.search_box.edit_buffer } else { &self.search_box.text };
+                    let search_lower = search_text.to_lowercase();
+                    if search_lower.is_empty() {
+                        true
+                    } else {
+                        e.from.to_lowercase().contains(&search_lower) ||
+                        e.subject.to_lowercase().contains(&search_lower) ||
+                        e.body.to_lowercase().contains(&search_lower)
+                    }
+                })
+                .count();
 
             self.email_list.set_rect(list_x, 55.0 + MENUBAR_H, 300.0, h_f32 - 70.0 - MENUBAR_H);
             self.email_list.update_bounds(list_count, 55.0 + MENUBAR_H, h_f32 - 70.0 - MENUBAR_H);
@@ -2828,9 +2757,7 @@ impl Application for ClearEmailApp {
             };
 
             // Get filtered email IDs and selection states
-            let filtered_email_ids: Vec<(usize, bool)> = if self.current_folder == Folder::Accounts {
-                Vec::new()
-            } else {
+            let filtered_email_ids: Vec<(usize, bool)> = {
                 let search_text = if self.search_box.editing { &self.search_box.edit_buffer } else { &self.search_box.text };
                 let search_lower = search_text.to_lowercase();
                 self.emails.iter()
@@ -2848,37 +2775,25 @@ impl Application for ClearEmailApp {
                     .collect()
             };
 
-            if self.current_folder == Folder::Accounts {
-                for idx in 0..self.accounts.len() {
-                    self.email_buttons[idx].selected = idx == self.selected_account_idx;
-                    if let Some(draw_y) = self.email_list.get_item_draw_y(idx, 0.0) {
-                        self.email_buttons[idx].set_rect(list_x, draw_y, 300.0, 54.0);
-                    } else {
-                        self.email_buttons[idx].set_rect(-9999.0, -9999.0, 0.0, 0.0);
-                    }
-                }
-
-            } else {
-                for (idx, &(_email_id, is_selected)) in filtered_email_ids.iter().enumerate() {
-                    self.email_buttons[idx].selected = is_selected;
-                    if let Some(draw_y) = self.email_list.get_item_draw_y(idx, 0.0) {
-                        self.email_buttons[idx].set_rect(list_x, draw_y, 300.0, 54.0);
-                    } else {
-                        self.email_buttons[idx].set_rect(-9999.0, -9999.0, 0.0, 0.0);
-                    }
+            for (idx, &(_email_id, is_selected)) in filtered_email_ids.iter().enumerate() {
+                self.email_buttons[idx].selected = is_selected;
+                if let Some(draw_y) = self.email_list.get_item_draw_y(idx, 0.0) {
+                    self.email_buttons[idx].set_rect(list_x, draw_y, 300.0, 54.0);
+                } else {
+                    self.email_buttons[idx].set_rect(-9999.0, -9999.0, 0.0, 0.0);
                 }
+            }
 
-                // Detail View
-                if let Some((read, body)) = selected_email_state {
-                    self.btn_reply.set_rect(detail_x, 8.0 + MENUBAR_H, 70.0, 26.0);
-                    self.btn_delete.set_rect(detail_x + 80.0, 8.0 + MENUBAR_H, 80.0, 26.0);
-                    self.btn_unread.set_rect(detail_x + 170.0, 8.0 + MENUBAR_H, 110.0, 26.0);
-                    self.btn_unread.set_label(if read { "Mark Unread" } else { "Mark Read" });
+            // Detail View
+            if let Some((read, body)) = selected_email_state {
+                self.btn_reply.set_rect(detail_x, 8.0 + MENUBAR_H, 70.0, 26.0);
+                self.btn_delete.set_rect(detail_x + 80.0, 8.0 + MENUBAR_H, 80.0, 26.0);
+                self.btn_unread.set_rect(detail_x + 170.0, 8.0 + MENUBAR_H, 110.0, 26.0);
+                self.btn_unread.set_label(if read { "Mark Unread" } else { "Mark Read" });
 
-                    let detail_w = (w_f32 - (detail_x + 15.0)).max(100.0);
-                    self.detail_body.set_rect(detail_x, 170.0 + MENUBAR_H, detail_w, (h_f32 - 190.0 - MENUBAR_H).max(100.0));
-                    self.detail_body.text = body;
-                }
+                let detail_w = (w_f32 - (detail_x + 15.0)).max(100.0);
+                self.detail_body.set_rect(detail_x, 170.0 + MENUBAR_H, detail_w, (h_f32 - 190.0 - MENUBAR_H).max(100.0));
+                self.detail_body.text = body;
             }
 
             // Compose inputs layout
@@ -2902,9 +2817,7 @@ impl Application for ClearEmailApp {
         }
 
         // Now compute `filtered` only for rendering (immutable borrow of self)
-        let filtered: Vec<&Email> = if self.current_folder == Folder::Accounts {
-            Vec::new()
-        } else {
+        let filtered: Vec<&Email> = {
             let search_text = if self.search_box.editing { &self.search_box.edit_buffer } else { &self.search_box.text };
             let search_lower = search_text.to_lowercase();
             self.emails.iter()
@@ -2928,7 +2841,7 @@ impl Application for ClearEmailApp {
         // hover/selected states) AND text in one pass. The legacy extra_quads bridge only
         // forwarded plain Prim::Quads, so every widget's rounded chrome was dropped.
         cce_ui::scene::painter::paint_root_into(&self.ui_context, &self.btn_compose, &mut *quads.pc);
-        cce_ui::scene::painter::paint_root_into(&self.ui_context, &self.btn_accounts, &mut *quads.pc);
+        cce_ui::scene::painter::paint_root_into(&self.ui_context, &self.account_dropdown, &mut *quads.pc);
         // The menubar itself paints at the END of display_list — its dropdown
         // must overlay every pane beneath.
 
@@ -2936,13 +2849,9 @@ impl Application for ClearEmailApp {
         quads.push((separator_x, MENUBAR_H, 1.0, h_f32 - MENUBAR_H, [0.18, 0.18, 0.22, 1.0]));
 
         // Search box / Add Account and List
-        if self.current_folder == Folder::Accounts {
-            cce_ui::scene::painter::paint_root_into(&self.ui_context, &self.btn_manage_accounts, &mut *quads.pc);
-        } else {
-            self.search_box.prepare_text(&mut self.font_system);
-            cce_ui::widget::WidgetHost::prepare_text(&mut self.menubar, &mut self.font_system);
-            cce_ui::scene::painter::paint_root_into(&self.ui_context, &self.search_box, &mut *quads.pc);
-        }
+        self.search_box.prepare_text(&mut self.font_system);
+        cce_ui::widget::WidgetHost::prepare_text(&mut self.menubar, &mut self.font_system);
+        cce_ui::scene::painter::paint_root_into(&self.ui_context, &self.search_box, &mut *quads.pc);
         {
             let mut list_quads = Vec::new();
             self.email_list.push_quads(&mut list_quads);
@@ -2950,17 +2859,12 @@ impl Application for ClearEmailApp {
         }
 
         // Visible List Item Buttons
-        let list_len = if self.current_folder == Folder::Accounts {
-            self.accounts.len()
-        } else {
-            filtered.len()
-        };
-        for idx in 0..list_len {
+        for idx in 0..filtered.len() {
             if self.email_list.get_item_draw_y(idx, 0.0).is_some() {
                 cce_ui::scene::painter::paint_root_into(&self.ui_context, &self.email_buttons[idx], &mut *quads.pc);
 
-                // Blue dot/unread indicator for this row (emails only)
-                if self.current_folder != Folder::Accounts && !filtered[idx].read {
+                // Blue dot/unread indicator for this row
+                if !filtered[idx].read {
                     if let Some(draw_y) = self.email_list.get_item_draw_y(idx, 0.0) {
                         quads.push((list_x + 8.0, draw_y + 12.0, 6.0, 6.0, [0.20, 0.45, 0.85, 1.0]));
                     }
@@ -2976,14 +2880,7 @@ impl Application for ClearEmailApp {
         }
 
         // 4. Detail View Area
-        if self.current_folder == Folder::Accounts {
-            if self.selected_account_idx < self.accounts.len() {
-                // Top action toolbar background
-                quads.push((detail_panel_x, MENUBAR_H, w_f32 - detail_panel_x, 42.0, [0.08, 0.08, 0.12, 1.0]));
-                quads.push((detail_panel_x, MENUBAR_H + 42.0, w_f32 - detail_panel_x, 1.0, [0.18, 0.18, 0.22, 1.0]));
-
-            }
-        } else if let Some(selected_id) = self.selected_email_id {
+        if let Some(selected_id) = self.selected_email_id {
             if self.emails.iter().any(|e| e.id == selected_id) {
                 // Top action toolbar background
                 quads.push((detail_panel_x, MENUBAR_H, w_f32 - detail_panel_x, 42.0, [0.08, 0.08, 0.12, 1.0]));
@@ -3170,14 +3067,10 @@ impl Application for ClearEmailApp {
         } else {
             // Sidebar buttons
             if ctx.propagate_event(&mv, self.btn_compose.id()) { changed = true; }
-            if ctx.propagate_event(&mv, self.btn_accounts.id()) { changed = true; }
+            if ctx.propagate_event(&mv, self.account_dropdown.id()) { changed = true; }
 
-            // Search / Add account and lists
-            if self.current_folder == Folder::Accounts {
-                if ctx.propagate_event(&mv, self.btn_manage_accounts.id()) { changed = true; }
-            } else {
-                if ctx.propagate_event(&mv, self.search_box.id()) { changed = true; }
-            }
+            // Search and lists
+            if ctx.propagate_event(&mv, self.search_box.id()) { changed = true; }
             if self.email_list.cursor_moved(px, py) { changed = true; }
             // Hover scope for the detail-pane body scroll (wheel + keys).
             self.detail_hovered = px > 10.0 + 310.0;
@@ -3189,7 +3082,7 @@ impl Application for ClearEmailApp {
             }
 
             // Detail view buttons
-            if self.current_folder != Folder::Accounts && self.selected_email_id.is_some() {
+            if self.selected_email_id.is_some() {
                 if ctx.propagate_event(&mv, self.btn_reply.id()) { changed = true; }
                 if ctx.propagate_event(&mv, self.btn_delete.id()) { changed = true; }
                 if ctx.propagate_event(&mv, self.btn_unread.id()) { changed = true; }
@@ -3212,10 +3105,7 @@ impl Application for ClearEmailApp {
 
         // Detail-pane body scrollbar drag — before the ui_context borrow (the
         // sb helpers take &mut self).
-        if !self.compose_open
-            && button == MouseButton::Left
-            && self.current_folder != Folder::Accounts
-        {
+        if !self.compose_open && button == MouseButton::Left {
             match state {
                 ElementState::Pressed => {
                     // A fresh press always supersedes a stale drag — a lost
@@ -3254,18 +3144,59 @@ impl Application for ClearEmailApp {
 
         let ctx = &mut self.ui_context;
 
-        // Accounts button first: it sits inside the bar band, and the menubar
+        // Account dropdown first: it sits inside the bar band, and the menubar
         // consumes any press within its rect — routed after, it would never
         // see the click.
-        if ctx.propagate_event(&ev, self.btn_accounts.id()) {
-            if state == ElementState::Released && self.btn_accounts.take_click() {
+        {
+            let was_open = self.account_dropdown.open;
+            if ctx.propagate_event(&ev, self.account_dropdown.id()) {
+                if !was_open && self.account_dropdown.open {
+                    // Freshly opened: re-read the shared accounts.json so
+                    // cce-system-interface edits appear without a restart (the
+                    // job the retired Accounts page did on entry). Keep the
+                    // selection by email; if that account is gone, fall to the
+                    // default. Field-level accesses only — `ctx` still borrows
+                    // self.ui_context.
+                    let prev_email = self
+                        .accounts
+                        .get(self.selected_account_idx)
+                        .map(|a| a.email.clone());
+                    self.accounts = load_accounts();
+                    let new_idx = prev_email
+                        .as_deref()
+                        .and_then(|e| self.accounts.iter().position(|a| a.email == e))
+                        .or_else(|| self.accounts.iter().position(|a| a.is_default))
+                        .unwrap_or(0);
+                    let changed_account =
+                        self.accounts.get(new_idx).map(|a| a.email.as_str()) != prev_email.as_deref();
+                    self.account_dropdown.options = account_dropdown_options(&self.accounts);
+                    self.account_dropdown.selected = new_idx;
+                    if changed_account {
+                        // The account under the selection vanished — switch to
+                        // what the trigger now shows.
+                        *needs_rebuild = true;
+                        self.needs_rebuild = true;
+                        return Some(AppMessage::SelectAccount(new_idx));
+                    }
+                }
+                let mut msg_out = None;
+                if self.account_dropdown.take_change() {
+                    let idx = self.account_dropdown.selected;
+                    if idx < self.accounts.len() {
+                        if idx != self.selected_account_idx {
+                            msg_out = Some(AppMessage::SelectAccount(idx));
+                        }
+                    } else {
+                        // The trailing "Manage Accounts…" pseudo-entry: not an
+                        // account — restore the trigger to the active one.
+                        self.account_dropdown.selected = self.selected_account_idx;
+                        msg_out = Some(AppMessage::ManageAccounts);
+                    }
+                }
                 *needs_rebuild = true;
                 self.needs_rebuild = true;
-                return Some(AppMessage::SwitchFolder(Folder::Accounts));
+                return msg_out;
             }
-            *needs_rebuild = true;
-            self.needs_rebuild = true;
-            return None;
         }
 
         // Menubar next — an open dropdown overlays the panes, so a handled
@@ -3373,26 +3304,17 @@ impl Application for ClearEmailApp {
                 }
             }
 
-            if self.current_folder == Folder::Accounts {
-                if ctx.propagate_event(&ev, self.btn_manage_accounts.id()) {
-                    changed = true;
-                    if state == ElementState::Released && self.btn_manage_accounts.take_click() {
-                        msg_out = Some(AppMessage::ManageAccounts);
-                    }
-                }
-            } else {
-                // Search input
-                if ctx.propagate_event(&ev, self.search_box.id()) {
-                    changed = true;
-                    if state == ElementState::Pressed { ctx.set_focused(&mut self.search_box); }
-                    if self.search_box.take_change() {
-                        msg_out = Some(AppMessage::SearchChanged);
-                    }
-                } else if state == ElementState::Pressed && button == MouseButton::Left {
-                    ctx.clear_focus();
-                    self.search_box.unfocus();
-                    changed = true;
+            // Search input
+            if ctx.propagate_event(&ev, self.search_box.id()) {
+                changed = true;
+                if state == ElementState::Pressed { ctx.set_focused(&mut self.search_box); }
+                if self.search_box.take_change() {
+                    msg_out = Some(AppMessage::SearchChanged);
                 }
+            } else if state == ElementState::Pressed && button == MouseButton::Left {
+                ctx.clear_focus();
+                self.search_box.unfocus();
+                changed = true;
             }
 
             // The scrollbar strip sits inside the row rects (rows span x 10..310, the strip
@@ -3416,20 +3338,6 @@ impl Application for ClearEmailApp {
 
             if scrollbar_took_press {
                 // fall through to the rest of the handler, but not to the rows
-            } else if self.current_folder == Folder::Accounts {
-                for (idx, _) in self.accounts.iter().enumerate() {
-                    if idx < self.email_buttons.len() {
-                        let btn = &mut self.email_buttons[idx];
-                        if btn.rect().0 > -9000.0 {
-                            if ctx.propagate_event(&ev, btn.id()) {
-                                changed = true;
-                                if state == ElementState::Released && btn.take_click() {
-                                    msg_out = Some(AppMessage::SelectAccount(idx));
-                                }
-                            }
-                        }
-                    }
-                }
             } else {
                 // Mirrors the paint pass's folder filter — this decides which
                 // email a row click lands on. No wildcard: a new folder
@@ -3440,7 +3348,6 @@ impl Application for ClearEmailApp {
                     Folder::Sent => "sent",
                     Folder::Drafts => "drafts",
                     Folder::Trash => "trash",
-                    Folder::Accounts => "accounts",
                 };
                 let search_text = if self.search_box.editing { &self.search_box.edit_buffer } else { &self.search_box.text };
                 let search_lower = search_text.to_lowercase();
@@ -3473,7 +3380,7 @@ impl Application for ClearEmailApp {
             }
 
             // Detail View action buttons
-            if self.current_folder != Folder::Accounts && self.selected_email_id.is_some() {
+            if self.selected_email_id.is_some() {
                 if ctx.propagate_event(&ev, self.btn_reply.id()) {
                     changed = true;
                     if state == ElementState::Released && self.btn_reply.take_click() {
@@ -3520,10 +3427,7 @@ impl Application for ClearEmailApp {
         }
 
         // Detail-pane body scroll.
-        if !self.compose_open
-            && self.current_folder != Folder::Accounts
-            && self.selected_email_id.is_some()
-        {
+        if !self.compose_open && self.selected_email_id.is_some() {
             let separator_x = 10.0 + 310.0;
             if px > separator_x {
                 let dy = match delta {
@@ -3577,7 +3481,7 @@ impl Application for ClearEmailApp {
                     msg_out = Some(AppMessage::ComposeNew);
                     handled = true;
                 } else if cce_ui::widget::match_key_shortcut(event, &self.keys.open_search) {
-                    if self.current_folder != Folder::Accounts {
+                    {
                         ctx.set_focused(&mut self.search_box);
                         self.search_box.focus();
                         handled = true;
@@ -3589,7 +3493,6 @@ impl Application for ClearEmailApp {
             if !handled
                 && self.detail_hovered
                 && self.selected_email_id.is_some()
-                && self.current_folder != Folder::Accounts
                 && !self.search_box.editing
                 && event.state == ElementState::Pressed
             {
@@ -3618,7 +3521,7 @@ impl Application for ClearEmailApp {
                 handled = true;
             }
 
-            if !handled && self.current_folder != Folder::Accounts && self.search_box.editing {
+            if !handled && self.search_box.editing {
                 if ctx.propagate_event(&kev, self.search_box.id()) {
                     handled = true;
                     if self.search_box.take_change() {
@@ -3629,7 +3532,7 @@ impl Application for ClearEmailApp {
 
             // Escape unfocuses search
             if !handled && event.state == ElementState::Pressed && event.logical_key == Key::Named(cce_ui::widget::NamedKey::Escape) {
-                if self.current_folder != Folder::Accounts && self.search_box.editing {
+                if self.search_box.editing {
                     ctx.clear_focus();
                     self.search_box.unfocus();
                     handled = true;