Эх сурвалжийг харах

app/wallet: integrate themes

x 1 долоо хоног өмнө
parent
commit
f285a28cbf

+ 4 - 0
bin/app/src/app/schema/wallet/data.rs

@@ -50,6 +50,8 @@ mod android_ui_consts {
     pub const ROW_HEIGHT: f32 = 80.;
     pub const COPY_WIDTH: f32 = 200.;
     pub const COPY_SCALE: f32 = 30.;
+    pub const PASTE_WIDTH: f32 = 200.;
+    pub const PASTE_SCALE: f32 = 30.;
 
     pub const CHAT_BTN_LOGO_SCALE: f32 = 80.;
     pub const CHAT_BTN_SIZE: f32 = 200.;
@@ -110,6 +112,8 @@ mod ui_consts {
     pub const ROW_HEIGHT: f32 = 80.;
     pub const COPY_WIDTH: f32 = 100.;
     pub const COPY_SCALE: f32 = 15.;
+    pub const PASTE_WIDTH: f32 = 100.;
+    pub const PASTE_SCALE: f32 = 15.;
 
     pub const CHAT_BTN_LOGO_SCALE: f32 = 40.;
     pub const CHAT_BTN_SIZE: f32 = 100.;

+ 14 - 14
bin/app/src/app/schema/wallet/main.rs

@@ -210,7 +210,8 @@ pub async fn make(
     node.set_property_u32(atom, Role::App, "z_index", 3).unwrap();
     node.set_property_bool(atom, Role::App, "is_visible", true).unwrap();
     let shape = shape::create_darkfi_logo([1., 1., 1., 1.]).scaled(DARKFI_LOGO_SCALE);
-    node.set_property_shape(atom, Role::App, "shape", shape).unwrap();
+    let prop = node.get_property("shape").unwrap();
+    prop.set_default_shape(0, shape).unwrap();
     let node =
         node.setup(|me| VectorArt::new(me, app.renderer.clone(), app.redraw_trigger.clone())).await;
     wallet_layer.link(node.clone());
@@ -283,8 +284,9 @@ pub async fn make(
     prop.set_default_f32(3, MAIN_BALANCE_FONTSIZE).unwrap();
     node.get_property("font_size").unwrap().set_default_f32(0, MAIN_BALANCE_FONTSIZE).unwrap();
     node.set_property_str(atom, Role::App, "text", "DRK").unwrap();
-    let prop = node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&[0., 0.94, 1., 1.]).unwrap();
+    // Class wiring: the DRK label follows the shared wallet accent token
+    let theme = app.sg_root.lookup_node("/theme").unwrap();
+    wire_color(&node, "text_color", &theme, "wallet.accent_color").unwrap();
     node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     let node = node
         .setup(|me| {
@@ -307,8 +309,7 @@ pub async fn make(
     prop.set_default_f32(3, MAIN_BALANCE_FONTSIZE).unwrap();
     node.get_property("font_size").unwrap().set_default_f32(0, MAIN_BALANCE_FONTSIZE).unwrap();
     node.set_property_str(atom, Role::App, "text", "0").unwrap();
-    // Class wiring: the wallet title follows the shared text token.
-    let theme = app.sg_root.lookup_node("/theme").unwrap();
+    // Class wiring: the wallet title follows the shared text token
     wire_color(&node, "text_color", &theme, "text_color").unwrap();
     node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     let node = node
@@ -339,7 +340,8 @@ pub async fn make(
         expr::load_var("h"),
         [0., 0.0157, 0.1569, 0.6],
     );
-    node.set_property_shape(atom, Role::App, "shape", shape).unwrap();
+    let prop = node.get_property("shape").unwrap();
+    prop.set_default_shape(0, shape).unwrap();
     let node =
         node.setup(|me| VectorArt::new(me, app.renderer.clone(), app.redraw_trigger.clone())).await;
     main_layer.link(node);
@@ -438,9 +440,8 @@ pub async fn make(
     node.get_property("font_size").unwrap().set_default_f32(0, BUTTON_FONTSIZE).unwrap();
     node.set_property_str(atom, Role::App, "text", "receive").unwrap();
     node.set_property_enum(atom, Role::App, "text_align", "center").unwrap();
-    let prop = node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&[COLOR_CYAN[0], COLOR_CYAN[1], COLOR_CYAN[2], COLOR_CYAN[3]])
-        .unwrap();
+    // Class wiring: the receive label follows the shared wallet accent token
+    wire_color(&node, "text_color", &theme, "wallet.accent_color").unwrap();
     node.set_property_u32(atom, Role::App, "z_index", 3).unwrap();
     let node = node
         .setup(|me| {
@@ -522,9 +523,8 @@ pub async fn make(
     node.get_property("font_size").unwrap().set_default_f32(0, BUTTON_FONTSIZE).unwrap();
     node.set_property_str(atom, Role::App, "text", "send").unwrap();
     node.set_property_enum(atom, Role::App, "text_align", "center").unwrap();
-    let prop = node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&[COLOR_CYAN[0], COLOR_CYAN[1], COLOR_CYAN[2], COLOR_CYAN[3]])
-        .unwrap();
+    // Class wiring: the send label follows the shared wallet accent token
+    wire_color(&node, "text_color", &theme, "wallet.accent_color").unwrap();
     node.set_property_u32(atom, Role::App, "z_index", 3).unwrap();
     let node = node
         .setup(|me| {
@@ -572,8 +572,8 @@ pub async fn make(
     tokens_table.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     tokens_table.set_property_u32(atom, Role::App, "priority", 0).unwrap();
 
-    let prop = tokens_table.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&[1., 1., 1., 1.]).unwrap();
+    // Class wiring: the token table follows the shared wallet text token
+    wire_color(&tokens_table, "text_color", &theme, "wallet.text_color").unwrap();
 
     let prop = tokens_table.get_property("separator_color").unwrap();
     prop.set_default_f32_multi(&[0.2, 0.2745, 0.2784, 1.]).unwrap();

+ 3 - 0
bin/app/src/app/schema/wallet/mod.rs

@@ -63,6 +63,9 @@ pub async fn make(app: &App, content: SceneNodePtr, i18n_fish: &I18nBabelFish) {
         .await;
     content.link(wallet_layer.clone());
 
+    // Single background mesh shared by all wallet screens
+    util::create_bg_mesh(app, atom, &wallet_layer, "bg").await;
+
     // Create main wallet layer
     let _ = main::make(app, wallet_layer.clone(), i18n_fish, window_scale.clone()).await;
 

+ 4 - 2
bin/app/src/app/schema/wallet/netstatus.rs

@@ -31,6 +31,7 @@ use crate::{
     prop::{PropertyAtomicGuard, PropertyFloat32, Role},
     scene::SceneNodePtr,
     shape,
+    theme::wire_color,
     ui::{Layer, Text, VectorArt},
     util::i18n::I18nBabelFish,
 };
@@ -72,8 +73,9 @@ pub async fn make(
     node.set_property_u32(atom, Role::App, "z_index", 3).unwrap();
     node.get_property("font_size").unwrap().set_default_f32(0, PROGRESS_FONTSIZE).unwrap();
     node.set_property_enum(atom, Role::App, "text_align", "end").unwrap();
-    let prop = node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&[1., 1., 1., 1.]).unwrap();
+    // Class wiring: the scan progress follows the shared wallet text token
+    let theme = app.sg_root.lookup_node("/theme").unwrap();
+    wire_color(&node, "text_color", &theme, "wallet.text_color").unwrap();
     node.set_property_u32(atom, Role::App, "z_index", 3).unwrap();
     let node = node
         .setup(|me| {

+ 10 - 8
bin/app/src/app/schema/wallet/receive.rs

@@ -22,10 +22,11 @@ use crate::{
         App,
     },
     expr,
-    mesh::{COLOR_CYAN, COLOR_MINT_OP, COLOR_TRANSAQUA},
+    mesh::COLOR_CYAN,
     prop::{PropertyAtomicGuard, PropertyFloat32, PropertyStr, Role},
     scene::{SceneNodePtr, Slot},
     shape,
+    theme::wire_color,
     ui::{Button, Layer, Text, VectorArt, VectorShape},
     util::{clipboard, i18n::I18nBabelFish},
 };
@@ -60,8 +61,6 @@ pub async fn make(
 
     create_back_shortcut(app, atom, &receive_layer).await;
 
-    create_bg_mesh(app, atom, &receive_layer, "receive_bg").await;
-
     let mut y = 0.;
 
     y += HEADER_HEIGHT;
@@ -96,8 +95,9 @@ pub async fn make(
     node.get_property("font_size").unwrap().set_default_f32(0, BUTTON_FONTSIZE).unwrap();
     node.set_property_str(atom, Role::App, "text", "").unwrap();
     node.set_property_enum(atom, Role::App, "overflow_wrap", "anywhere").unwrap();
-    let prop = node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&COLOR_MINT_OP).unwrap();
+    // Class wiring: the address value follows the shared wallet value token
+    let theme = app.sg_root.lookup_node("/theme").unwrap();
+    wire_color(&node, "text_color", &theme, "wallet.value_color").unwrap();
     node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     let node = node
         .setup(|me| {
@@ -128,9 +128,10 @@ pub async fn make(
         expr::const_f32(0.),
         expr::load_var("w"),
         expr::load_var("h"),
-        COLOR_TRANSAQUA,
+        [0.0353, 0.1294, 0.1333, 0.60],
     );
-    addr_bg_node.set_property_shape(atom, Role::App, "shape", shape).unwrap();
+    let prop = addr_bg_node.get_property("shape").unwrap();
+    prop.set_default_shape(0, shape).unwrap();
     let addr_bg_node = addr_bg_node
         .setup(|me| VectorArt::new(me, app.renderer.clone(), app.redraw_trigger.clone()))
         .await;
@@ -148,7 +149,8 @@ pub async fn make(
     prop.add_depend(Role::App, &addr_h_prop, 0, "addr_height");
     node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     let shape = shape::create_copy(COLOR_CYAN).scaled(COPY_SCALE);
-    node.set_property_shape(atom, Role::App, "shape", shape).unwrap();
+    let prop = node.get_property("shape").unwrap();
+    prop.set_default_shape(0, shape).unwrap();
     let node =
         node.setup(|me| VectorArt::new(me, app.renderer.clone(), app.redraw_trigger.clone())).await;
     receive_layer.link(node);

+ 4 - 4
bin/app/src/app/schema/wallet/send_step1.rs

@@ -29,6 +29,7 @@ use crate::{
     gfx::gfxtag,
     prop::{PropertyAtomicGuard, PropertyBool, PropertyFloat32, Role},
     scene::{SceneNodePtr, Slot},
+    theme::wire_color,
     ui::{Layer, TokenRow, TokenTable},
     util::i18n::I18nBabelFish,
 };
@@ -75,8 +76,6 @@ pub async fn make(
     let step1_is_visible =
         PropertyBool::wrap(&send_step1_layer, Role::App, "is_visible", 0).unwrap();
 
-    create_bg_mesh(app, atom, &send_step1_layer, "send_bg").await;
-
     let mut y = 0.;
 
     y += HEADER_HEIGHT;
@@ -110,8 +109,9 @@ pub async fn make(
     send_tokens_table.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     send_tokens_table.set_property_u32(atom, Role::App, "priority", 0).unwrap();
 
-    let prop = send_tokens_table.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&[1., 1., 1., 1.]).unwrap();
+    // Class wiring: the token table follows the shared wallet text token
+    let theme = app.sg_root.lookup_node("/theme").unwrap();
+    wire_color(&send_tokens_table, "text_color", &theme, "wallet.text_color").unwrap();
 
     let prop = send_tokens_table.get_property("separator_color").unwrap();
     prop.set_default_f32_multi(&[0.2, 0.2745, 0.2784, 1.]).unwrap();

+ 19 - 57
bin/app/src/app/schema/wallet/send_step2.rs

@@ -28,43 +28,17 @@ use crate::{
     },
     expr,
     gfx::gfxtag,
-    mesh::{COLOR_CYAN, COLOR_DARKTEAL, COLOR_PALECYAN, COLOR_TRANSTEAL},
+    mesh::COLOR_CYAN,
     prop::{PropertyAtomicGuard, PropertyBool, PropertyFloat32, Role},
-    scene::{Pimpl, SceneNodePtr, Slot},
+    scene::{SceneNodePtr, Slot},
     shape,
+    theme::wire_color,
     ui::{BaseEdit, BaseEditType, Button, Layer, Text, VectorArt, VectorShape},
     util::{clipboard, i18n::I18nBabelFish},
 };
 
 use super::{data::*, util::*};
 
-#[cfg(any(target_os = "android", feature = "emulate-android"))]
-mod android_ui_consts {
-    pub const PASTE_WIDTH: f32 = 200.;
-    pub const PASTE_SCALE: f32 = 30.;
-}
-
-#[cfg(target_os = "android")]
-mod ui_consts {
-    pub use super::android_ui_consts::*;
-}
-
-#[cfg(feature = "emulate-android")]
-mod ui_consts {
-    pub use super::android_ui_consts::*;
-}
-
-#[cfg(all(
-    any(target_os = "linux", target_os = "macos", target_os = "windows"),
-    not(feature = "emulate-android")
-))]
-mod ui_consts {
-    pub const PASTE_WIDTH: f32 = 100.;
-    pub const PASTE_SCALE: f32 = 15.;
-}
-
-pub use ui_consts::*;
-
 pub async fn make(
     app: &App,
     wallet_layer: SceneNodePtr,
@@ -106,8 +80,6 @@ pub async fn make(
     let step2_is_visible =
         PropertyBool::wrap(&send_step2_layer, Role::App, "is_visible", 0).unwrap();
 
-    create_bg_mesh(app, atom, &send_step2_layer, "send_bg2").await;
-
     let mut y = 0.;
 
     y += HEADER_HEIGHT;
@@ -124,8 +96,9 @@ pub async fn make(
     prop.set_default_f32(3, BASE_FONTSIZE).unwrap();
     node.get_property("font_size").unwrap().set_default_f32(0, BASE_FONTSIZE).unwrap();
     node.set_property_str(atom, Role::App, "text", "Token").unwrap();
-    let prop = node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&[1., 1., 1., 1.]).unwrap();
+    // Class wiring: the selected token follows the shared wallet text token
+    let theme = app.sg_root.lookup_node("/theme").unwrap();
+    wire_color(&node, "text_color", &theme, "wallet.text_color").unwrap();
     node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     let node = node
         .setup(|me| {
@@ -153,8 +126,8 @@ pub async fn make(
     prop.set_default_f32(3, SUBTITLE_FONTSIZE).unwrap();
     node.get_property("font_size").unwrap().set_default_f32(0, SUBTITLE_FONTSIZE).unwrap();
     node.set_property_str(atom, Role::App, "text", "Recipient").unwrap();
-    let prop = node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&COLOR_PALECYAN).unwrap();
+    // Class wiring: the recipient label follows the shared wallet label token
+    wire_color(&node, "text_color", &theme, "wallet.label_color").unwrap();
     node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     let node = node
         .setup(|me| {
@@ -181,7 +154,8 @@ pub async fn make(
     prop.set_default_f32(3, 500.).unwrap();
     node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     let shape = shape::create_paste(COLOR_CYAN).scaled(PASTE_SCALE);
-    node.set_property_shape(atom, Role::App, "shape", shape).unwrap();
+    let prop = node.get_property("shape").unwrap();
+    prop.set_default_shape(0, shape).unwrap();
     let node =
         node.setup(|me| VectorArt::new(me, app.renderer.clone(), app.redraw_trigger.clone())).await;
     send_step2_layer.link(node);
@@ -236,16 +210,13 @@ pub async fn make(
     recipient_input
         .set_property_str(atom, Role::App, "placeholder_text", "recipient address")
         .unwrap();
+    // Class wiring: the recipient input follows the shared edit tokens
+    wire_color(&recipient_input, "text_color", &theme, "edit.text_color").unwrap();
+    wire_color(&recipient_input, "text_hi_color", &theme, "edit.text_hi_color").unwrap();
+    wire_color(&recipient_input, "cursor_color", &theme, "edit.cursor_color").unwrap();
+    wire_color(&recipient_input, "hi_bg_color", &theme, "edit.hi_bg_color").unwrap();
     let prop = recipient_input.get_property("placeholder_color").unwrap();
-    prop.set_default_f32_multi(&COLOR_DARKTEAL).unwrap();
-    let prop = recipient_input.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&[1., 1., 1., 1.]).unwrap();
-    let prop = recipient_input.get_property("hi_bg_color").unwrap();
-    prop.set_default_f32_multi(&[0.5, 0.5, 0.5, 1.]).unwrap();
-    let prop = recipient_input.get_property("text_hi_color").unwrap();
-    prop.set_default_f32_multi(&[0.44, 0.96, 1., 1.]).unwrap();
-    let prop = recipient_input.get_property("cursor_color").unwrap();
-    prop.set_default_f32_multi(&[0.816, 0.627, 1., 1.]).unwrap();
+    prop.set_default_f32_multi(&[0.3686, 0.6, 0.5451, 1.]).unwrap();
     recipient_input.set_property_f32(atom, Role::App, "cursor_ascent", 0.).unwrap();
     recipient_input
         .set_property_f32(atom, Role::App, "cursor_descent", RECIPIENT_INPUT_FONTSIZE * 1.3)
@@ -289,17 +260,7 @@ pub async fn make(
         expr::const_f32(0.),
         expr::load_var("w"),
         expr::load_var("h"),
-        COLOR_TRANSTEAL,
-    );
-    shape.add_radial_glow(
-        cc.compile("w / 2").unwrap(),
-        expr::load_var("h"),
-        cc.compile("w * 1.2").unwrap(),
-        cc.compile("h / 2").unwrap(),
-        8,
-        std::f32::consts::PI,
-        2. * std::f32::consts::PI,
-        [0.3, 0.5, 1., 0.25],
+        [0.0902, 0.1333, 0.1333, 0.60],
     );
     shape.add_outline(
         expr::const_f32(0.),
@@ -309,7 +270,8 @@ pub async fn make(
         1.,
         [0.2, 0.2745, 0.2784, 1.],
     );
-    node.set_property_shape(atom, Role::App, "shape", shape).unwrap();
+    let prop = node.get_property("shape").unwrap();
+    prop.set_default_shape(0, shape).unwrap();
     let node =
         node.setup(|me| VectorArt::new(me, app.renderer.clone(), app.redraw_trigger.clone())).await;
     send_step2_layer.link(node);

+ 25 - 31
bin/app/src/app/schema/wallet/send_step3.rs

@@ -31,9 +31,9 @@ use crate::{
     },
     expr,
     gfx::gfxtag,
-    mesh::{COLOR_CYAN, COLOR_NEONGREEN, COLOR_PALECYAN, COLOR_TRANSAQUA},
     prop::{PropertyAtomicGuard, PropertyBool, PropertyFloat32, PropertyRect, Role},
     scene::{SceneNodePtr, Slot},
+    theme::wire_color,
     ui::{BaseEdit, BaseEditType, Layer, Text, VectorArt, VectorShape},
     util::i18n::I18nBabelFish,
 };
@@ -80,8 +80,6 @@ pub async fn make(
     let step3_is_visible =
         PropertyBool::wrap(&send_step3_layer, Role::App, "is_visible", 0).unwrap();
 
-    create_bg_mesh(app, atom, &send_step3_layer, "send_bg3").await;
-
     let mut y = 0.;
 
     y += HEADER_HEIGHT;
@@ -101,8 +99,9 @@ pub async fn make(
     let token_symbol_text =
         send_tx_data.lock().unwrap().token_symbol.clone().unwrap_or_else(|| "".to_string());
     node.set_property_str(atom, Role::App, "text", &token_symbol_text).unwrap();
-    let prop = node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&[1., 1., 1., 1.]).unwrap();
+    // Class wiring: the selected token follows the shared wallet text token
+    let theme = app.sg_root.lookup_node("/theme").unwrap();
+    wire_color(&node, "text_color", &theme, "wallet.text_color").unwrap();
     node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     let node = node
         .setup(|me| {
@@ -128,8 +127,8 @@ pub async fn make(
     let token_name_text =
         send_tx_data.lock().unwrap().token_name.clone().unwrap_or_else(|| "".to_string());
     node.set_property_str(atom, Role::App, "text", &token_name_text).unwrap();
-    let prop = node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&[1., 1., 1., 1.]).unwrap();
+    // Class wiring: the token name follows the shared wallet text token
+    wire_color(&node, "text_color", &theme, "wallet.text_color").unwrap();
     node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     let selected_token_text3 = node
         .setup(|me| {
@@ -157,8 +156,8 @@ pub async fn make(
     prop.set_default_f32(3, SUBTITLE_FONTSIZE).unwrap();
     node.get_property("font_size").unwrap().set_default_f32(0, SUBTITLE_FONTSIZE).unwrap();
     node.set_property_str(atom, Role::App, "text", "Recipient").unwrap();
-    let prop = node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&COLOR_PALECYAN).unwrap();
+    // Class wiring: the recipient label follows the shared wallet label token
+    wire_color(&node, "text_color", &theme, "wallet.label_color").unwrap();
     node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     let node = node
         .setup(|me| {
@@ -189,8 +188,8 @@ pub async fn make(
     node.get_property("font_size").unwrap().set_default_f32(0, SUBTITLE_FONTSIZE).unwrap();
     node.set_property_str(atom, Role::App, "text", "").unwrap();
     node.set_property_enum(atom, Role::App, "overflow_wrap", "anywhere").unwrap();
-    let prop = node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&COLOR_NEONGREEN).unwrap();
+    // Class wiring: the recipient address follows the wallet recipient token
+    wire_color(&node, "text_color", &theme, "wallet.recipient_color").unwrap();
     node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     let node = node
         .setup(|me| {
@@ -221,9 +220,10 @@ pub async fn make(
         expr::const_f32(0.),
         expr::load_var("w"),
         expr::load_var("h"),
-        COLOR_TRANSAQUA,
+        [0.0353, 0.1294, 0.1333, 0.60],
     );
-    addr_bg_node.set_property_shape(atom, Role::App, "shape", shape).unwrap();
+    let prop = addr_bg_node.get_property("shape").unwrap();
+    prop.set_default_shape(0, shape).unwrap();
     let addr_bg_node = addr_bg_node
         .setup(|me| VectorArt::new(me, app.renderer.clone(), app.redraw_trigger.clone()))
         .await;
@@ -261,8 +261,8 @@ pub async fn make(
         .set_default_f32(0, BASE_FONTSIZE)
         .unwrap();
     available_balance_node.set_property_str(atom, Role::App, "text", "").unwrap();
-    let prop = available_balance_node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&[1., 1., 1., 1.]).unwrap();
+    // Class wiring: the available balance follows the shared wallet text token
+    wire_color(&available_balance_node, "text_color", &theme, "wallet.text_color").unwrap();
     available_balance_node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     let available_balance_node = available_balance_node
         .setup(|me| {
@@ -348,14 +348,11 @@ pub async fn make(
     prop.set_default_expr(2, code).unwrap();
     prop.set_default_f32(3, AMOUNT_FONTSIZE).unwrap();
     input_node.get_property("font_size").unwrap().set_default_f32(0, AMOUNT_FONTSIZE).unwrap();
-    let prop = input_node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&[1., 1., 1., 1.]).unwrap();
-    let prop = input_node.get_property("hi_bg_color").unwrap();
-    prop.set_default_f32_multi(&[0.5, 0.5, 0.5, 1.]).unwrap();
-    let prop = input_node.get_property("text_hi_color").unwrap();
-    prop.set_default_f32_multi(&[0.44, 0.96, 1., 1.]).unwrap();
-    let prop = input_node.get_property("cursor_color").unwrap();
-    prop.set_default_f32_multi(&[0.816, 0.627, 1., 1.]).unwrap();
+    // Class wiring: the amount input follows the shared edit tokens
+    wire_color(&input_node, "text_color", &theme, "edit.text_color").unwrap();
+    wire_color(&input_node, "text_hi_color", &theme, "edit.text_hi_color").unwrap();
+    wire_color(&input_node, "cursor_color", &theme, "edit.cursor_color").unwrap();
+    wire_color(&input_node, "hi_bg_color", &theme, "edit.hi_bg_color").unwrap();
     input_node.set_property_f32(atom, Role::App, "cursor_ascent", 0.).unwrap();
     input_node.set_property_f32(atom, Role::App, "cursor_descent", AMOUNT_FONTSIZE * 1.3).unwrap();
     input_node.set_property_f32(atom, Role::App, "select_ascent", AMOUNT_FONTSIZE * 1.3).unwrap();
@@ -395,14 +392,11 @@ pub async fn make(
         .unwrap()
         .set_default_f32(0, AMOUNT_FONTSIZE)
         .unwrap();
-    let prop = token_symbol_node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&[1., 1., 1., 1.]).unwrap();
-    let prop = token_symbol_node.get_property("hi_bg_color").unwrap();
-    prop.set_default_f32_multi(&[0.5, 0.5, 0.5, 1.]).unwrap();
-    let prop = token_symbol_node.get_property("text_hi_color").unwrap();
-    prop.set_default_f32_multi(&[0.44, 0.96, 1., 1.]).unwrap();
-    let prop = token_symbol_node.get_property("cursor_color").unwrap();
-    prop.set_default_f32_multi(&[0.816, 0.627, 1., 1.]).unwrap();
+    // Class wiring: the amount token symbol follows the shared edit tokens
+    wire_color(&token_symbol_node, "text_color", &theme, "edit.text_color").unwrap();
+    wire_color(&token_symbol_node, "text_hi_color", &theme, "edit.text_hi_color").unwrap();
+    wire_color(&token_symbol_node, "cursor_color", &theme, "edit.cursor_color").unwrap();
+    wire_color(&token_symbol_node, "hi_bg_color", &theme, "edit.hi_bg_color").unwrap();
     token_symbol_node.set_property_f32(atom, Role::App, "cursor_ascent", 0.).unwrap();
     token_symbol_node
         .set_property_f32(atom, Role::App, "cursor_descent", AMOUNT_FONTSIZE * 1.3)

+ 22 - 21
bin/app/src/app/schema/wallet/send_step4.rs

@@ -28,9 +28,9 @@ use crate::{
     },
     expr,
     gfx::gfxtag,
-    mesh::{Color, COLOR_CYAN, COLOR_NEONGREEN, COLOR_PALECYAN, COLOR_TRANSAQUA},
     prop::{PropertyAtomicGuard, PropertyBool, PropertyFloat32, Role},
     scene::{SceneNodePtr, Slot},
+    theme::wire_color,
     ui::{Layer, Text, VectorArt, VectorShape},
     util::i18n::I18nBabelFish,
 };
@@ -77,8 +77,6 @@ pub async fn make(
     let step4_is_visible =
         PropertyBool::wrap(&send_step4_layer, Role::App, "is_visible", 0).unwrap();
 
-    create_bg_mesh(app, atom, &send_step4_layer, "send_bg4").await;
-
     let mut y = 0.;
 
     y += HEADER_HEIGHT;
@@ -95,8 +93,9 @@ pub async fn make(
     prop.set_default_f32(3, BASE_FONTSIZE).unwrap();
     node.get_property("font_size").unwrap().set_default_f32(0, BASE_FONTSIZE).unwrap();
     node.set_property_str(atom, Role::App, "text", "Token").unwrap();
-    let prop = node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&[1., 1., 1., 1.]).unwrap();
+    // Class wiring: the selected token follows the shared wallet text token
+    let theme = app.sg_root.lookup_node("/theme").unwrap();
+    wire_color(&node, "text_color", &theme, "wallet.text_color").unwrap();
     node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     let node = node
         .setup(|me| {
@@ -119,8 +118,8 @@ pub async fn make(
     prop.set_default_f32(3, BASE_FONTSIZE).unwrap();
     node.get_property("font_size").unwrap().set_default_f32(0, BASE_FONTSIZE).unwrap();
     node.set_property_str(atom, Role::App, "text", "").unwrap();
-    let prop = node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&[1., 1., 1., 1.]).unwrap();
+    // Class wiring: the token name follows the shared wallet text token
+    wire_color(&node, "text_color", &theme, "wallet.text_color").unwrap();
     node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     let node = node
         .setup(|me| {
@@ -148,8 +147,8 @@ pub async fn make(
     prop.set_default_f32(3, SUBTITLE_FONTSIZE).unwrap();
     node.get_property("font_size").unwrap().set_default_f32(0, SUBTITLE_FONTSIZE).unwrap();
     node.set_property_str(atom, Role::App, "text", "Recipient").unwrap();
-    let prop = node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&COLOR_PALECYAN).unwrap();
+    // Class wiring: the recipient label follows the shared wallet label token
+    wire_color(&node, "text_color", &theme, "wallet.label_color").unwrap();
     node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     let node = node
         .setup(|me| {
@@ -184,8 +183,8 @@ pub async fn make(
         .unwrap();
     recipient_addr_node.set_property_str(atom, Role::App, "text", "").unwrap();
     recipient_addr_node.set_property_enum(atom, Role::App, "overflow_wrap", "anywhere").unwrap();
-    let prop = recipient_addr_node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&COLOR_NEONGREEN).unwrap();
+    // Class wiring: the recipient address follows the wallet recipient token
+    wire_color(&recipient_addr_node, "text_color", &theme, "wallet.recipient_color").unwrap();
     recipient_addr_node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     let recipient_addr_node = recipient_addr_node
         .setup(|me| {
@@ -216,9 +215,10 @@ pub async fn make(
         expr::const_f32(0.),
         expr::load_var("w"),
         expr::load_var("h"),
-        COLOR_TRANSAQUA,
+        [0.0353, 0.1294, 0.1333, 0.60],
     );
-    addr_bg_node.set_property_shape(atom, Role::App, "shape", shape).unwrap();
+    let prop = addr_bg_node.get_property("shape").unwrap();
+    prop.set_default_shape(0, shape).unwrap();
     let addr_bg_node = addr_bg_node
         .setup(|me| VectorArt::new(me, app.renderer.clone(), app.redraw_trigger.clone()))
         .await;
@@ -271,8 +271,8 @@ pub async fn make(
         .unwrap()
         .set_default_f32(0, AMOUNT_FONTSIZE)
         .unwrap();
-    let prop = amount_text_node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&[0.5451, 1., 1., 1.]).unwrap();
+    // Class wiring: the amount text follows the shared wallet value token
+    wire_color(&amount_text_node, "text_color", &theme, "wallet.value_color").unwrap();
     amount_text_node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     amount_text_node.set_property_str(atom, Role::App, "text", "0").unwrap();
     let amount_text_node = amount_text_node
@@ -302,8 +302,8 @@ pub async fn make(
         .set_default_f32(0, AMOUNT_FONTSIZE)
         .unwrap();
     token_symbol_node.set_property_str(atom, Role::App, "text", "").unwrap();
-    let prop = token_symbol_node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&[0.5451, 1., 1., 1.]).unwrap();
+    // Class wiring: the token symbol follows the shared wallet value token
+    wire_color(&token_symbol_node, "text_color", &theme, "wallet.value_color").unwrap();
     token_symbol_node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     let token_symbol_node = token_symbol_node
         .setup(|me| {
@@ -329,8 +329,9 @@ pub async fn make(
     prop.set_default_f32(3, BASE_FONTSIZE).unwrap();
     tx_fee_label_node.get_property("font_size").unwrap().set_default_f32(0, BASE_FONTSIZE).unwrap();
     tx_fee_label_node.set_property_str(atom, Role::App, "text", "Transaction fee").unwrap();
-    let prop = tx_fee_label_node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&[1., 1., 1., 0.]).unwrap();
+    // Class wiring: the fee label follows the wallet fee label token. The
+    // schema flips text_color alpha to 1 once the tx is built (see send.rs).
+    wire_color(&tx_fee_label_node, "text_color", &theme, "wallet.fee_label_color").unwrap();
     tx_fee_label_node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     let tx_fee_label_node = tx_fee_label_node
         .setup(|me| {
@@ -359,8 +360,8 @@ pub async fn make(
     tx_fee_value_node.set_property_str(atom, Role::App, "text", "0 DRK").unwrap();
     tx_fee_value_node.set_property_enum(atom, Role::App, "text_align", "end").unwrap();
     tx_fee_value_node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
-    let prop = tx_fee_value_node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&[0.8157, 0.6275, 1., 0.]).unwrap();
+    // Class wiring: the fee value follows the wallet fee value token
+    wire_color(&tx_fee_value_node, "text_color", &theme, "wallet.fee_value_color").unwrap();
     let tx_fee_value_node = tx_fee_value_node
         .setup(|me| {
             Text::new(

+ 38 - 18
bin/app/src/app/schema/wallet/tx_status.rs

@@ -28,12 +28,13 @@ use crate::{
     },
     expr,
     gfx::gfxtag,
-    mesh::{COLOR_DARKTEAL, COLOR_GREEN, COLOR_RED},
+    mesh::{COLOR_GREEN, COLOR_RED},
     prop::{
-        Property, PropertyAtomicGuard, PropertyBool, PropertyFloat32, PropertyPermission,
-        PropertySubType, PropertyType, Role,
+        Property, PropertyAtomicGuard, PropertyBool, PropertyColor, PropertyFloat32,
+        PropertyPermission, PropertySubType, PropertyType, Role,
     },
     scene::{SceneNodePtr, Slot},
+    theme::wire_color,
     ui::{Layer, Text, VectorArt, VectorShape},
     util::i18n::I18nBabelFish,
 };
@@ -113,6 +114,26 @@ pub async fn make(
     prop.set_default_enum(0, "confirming").unwrap();
     tx_status_layer.add_property(prop).unwrap();
 
+    let mut prop = Property::new(
+        "glow_confirmed_color",
+        PropertyType::Float32,
+        PropertySubType::Color,
+        PropertyPermission { read: Role::ALL, write: Role::Theme },
+    );
+    prop.set_array_len(4);
+    prop.set_defaults_f32(vec![0., 0., 0., 0.]).unwrap();
+    tx_status_layer.add_property(prop).unwrap();
+
+    let mut prop = Property::new(
+        "glow_error_color",
+        PropertyType::Float32,
+        PropertySubType::Color,
+        PropertyPermission { read: Role::ALL, write: Role::Theme },
+    );
+    prop.set_array_len(4);
+    prop.set_defaults_f32(vec![0., 0., 0., 0.]).unwrap();
+    tx_status_layer.add_property(prop).unwrap();
+
     // Height of the hint zone
     const HINT_H: f32 = PADDING_X + PADDING_Y + HINT_FONTSIZE * 2.;
     let mut prop = Property::new(
@@ -140,8 +161,6 @@ pub async fn make(
     let tx_status_is_visible =
         PropertyBool::wrap(&tx_status_layer, Role::App, "is_visible", 0).unwrap();
 
-    create_bg_mesh(app, atom, &tx_status_layer, "tx_status_bg").await;
-
     // Status headline
     let node = create_text("status");
     let prop = node.get_property("rect").unwrap();
@@ -152,8 +171,9 @@ pub async fn make(
     node.get_property("font_size").unwrap().set_default_f32(0, BASE_FONTSIZE).unwrap();
     node.set_property_enum(atom, Role::App, "text_align", "center").unwrap();
     node.set_property_str(atom, Role::App, "text", "Transaction in process...").unwrap();
-    let prop = node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&[1., 1., 1., 1.]).unwrap();
+    // Class wiring: the status headline follows the shared wallet text token
+    let theme = app.sg_root.lookup_node("/theme").unwrap();
+    wire_color(&node, "text_color", &theme, "wallet.text_color").unwrap();
     node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     let node = node
         .setup(|me| {
@@ -200,7 +220,6 @@ pub async fn make(
         .await;
     tx_status_layer.link(tx_info_layer.clone());
 
-
     // "Sending" or "Sent"
     let node = create_text("action");
     let prop = node.get_property("rect").unwrap();
@@ -211,8 +230,8 @@ pub async fn make(
     node.get_property("font_size").unwrap().set_default_f32(0, BASE_FONTSIZE).unwrap();
     node.set_property_str(atom, Role::App, "text", "Sending").unwrap();
     node.set_property_enum(atom, Role::App, "text_align", "center").unwrap();
-    let prop = node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&[1., 1., 1., 1.]).unwrap();
+    // Class wiring: the action text follows the shared wallet text token
+    wire_color(&node, "text_color", &theme, "wallet.text_color").unwrap();
     node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     let node = node
         .setup(|me| {
@@ -238,8 +257,8 @@ pub async fn make(
     node.get_property("font_size").unwrap().set_default_f32(0, BASE_FONTSIZE).unwrap();
     node.set_property_str(atom, Role::App, "text", "").unwrap();
     node.set_property_enum(atom, Role::App, "text_align", "center").unwrap();
-    let prop = node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&[0.5451, 1., 1., 1.]).unwrap();
+    // Class wiring: the amount follows the wallet tx status value token
+    wire_color(&node, "text_color", &theme, "wallet.tx_status_value_color").unwrap();
     node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     let node = node
         .setup(|me| {
@@ -265,8 +284,8 @@ pub async fn make(
     node.get_property("font_size").unwrap().set_default_f32(0, BASE_FONTSIZE).unwrap();
     node.set_property_str(atom, Role::App, "text", "to").unwrap();
     node.set_property_enum(atom, Role::App, "text_align", "center").unwrap();
-    let prop = node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&[1., 1., 1., 1.]).unwrap();
+    // Class wiring: the "to" label follows the shared wallet text token
+    wire_color(&node, "text_color", &theme, "wallet.text_color").unwrap();
     node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     let node = node
         .setup(|me| {
@@ -293,8 +312,9 @@ pub async fn make(
     node.set_property_str(atom, Role::App, "text", "").unwrap();
     node.set_property_enum(atom, Role::App, "text_align", "center").unwrap();
     node.set_property_enum(atom, Role::App, "overflow_wrap", "anywhere").unwrap();
-    let prop = node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&[0., 1., 0.6392, 1.]).unwrap();
+    // Class wiring: the recipient address follows the wallet tx status
+    // recipient token (distinct from the send-flow recipient color)
+    wire_color(&node, "text_color", &theme, "wallet.tx_status_recipient_color").unwrap();
     node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     let node = node
         .setup(|me| {
@@ -344,8 +364,8 @@ pub async fn make(
             "You can close this screen while the transaction is confirming.",
         )
         .unwrap();
-    let prop = hint_node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&COLOR_DARKTEAL).unwrap();
+    // Class wiring: the hint follows the shared wallet muted token
+    wire_color(&hint_node, "text_color", &theme, "wallet.muted_color").unwrap();
     hint_node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     let hint_node = hint_node
         .setup(|me| {

+ 38 - 23
bin/app/src/app/schema/wallet/util.rs

@@ -30,10 +30,11 @@ use crate::{
     },
     expr::{self, Compiler},
     gfx::{gfxtag, Renderer},
-    mesh::{Color, COLOR_CYAN, COLOR_PALECYAN, COLOR_TEAL},
+    mesh::{Color, COLOR_CYAN, COLOR_TEAL},
     prop::{PropertyAtomicGuard, PropertyFloat32, Role},
-    scene::{Pimpl, SceneNodePtr, Slot},
+    scene::{SceneNodePtr, Slot},
     text,
+    theme::wire_color,
     ui::{Button, Layer, RedrawTrigger, Shortcut, Text, VectorArt, VectorShape},
     util::i18n::I18nBabelFish,
 };
@@ -199,7 +200,8 @@ pub async fn create_title(
         8,
         0.2,
     );
-    bg_node.set_property_shape(atom, Role::App, "shape", shape).unwrap();
+    let prop = bg_node.get_property("shape").unwrap();
+    prop.set_default_shape(0, shape).unwrap();
     let bg_node = bg_node
         .setup(|me| VectorArt::new(me, app.renderer.clone(), app.redraw_trigger.clone()))
         .await;
@@ -214,8 +216,9 @@ pub async fn create_title(
     prop.set_default_f32(3, TITLE_FONTSIZE).unwrap();
     node.get_property("font_size").unwrap().set_default_f32(0, TITLE_FONTSIZE).unwrap();
     node.set_property_str(atom, Role::App, "text", text).unwrap();
-    let prop = node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&[0.6471, 0.8667, 0.8157, 1.]).unwrap();
+    // Class wiring: the title follows the shared wallet title token
+    let theme = app.sg_root.lookup_node("/theme").unwrap();
+    wire_color(&node, "text_color", &theme, "wallet.title_color").unwrap();
     node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     node.set_property_f32(atom, Role::App, "letter_spacing", TITLE_FONTSIZE * 0.11).unwrap();
     let node = node
@@ -257,8 +260,9 @@ pub async fn create_subtitle(
     prop.set_default_f32(3, SUBTITLE_FONTSIZE).unwrap();
     node.get_property("font_size").unwrap().set_default_f32(0, SUBTITLE_FONTSIZE).unwrap();
     node.set_property_str(atom, Role::App, "text", text).unwrap();
-    let prop = node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&COLOR_PALECYAN).unwrap();
+    // Class wiring: subtitles follow the shared wallet label token
+    let theme = app.sg_root.lookup_node("/theme").unwrap();
+    wire_color(&node, "text_color", &theme, "wallet.label_color").unwrap();
     node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
     let node = node
         .setup(|me| {
@@ -288,10 +292,10 @@ pub async fn create_bg_mesh(
     let node = create_vector_art(name);
     let prop = node.get_property("rect").unwrap();
     prop.set_default_f32(0, 0.).unwrap();
-    prop.set_default_f32(1, HEADER_HEIGHT + 1.).unwrap();
+    prop.set_default_f32(1, HEADER_HEIGHT + 0.5).unwrap();
     prop.set_default_expr(2, expr::load_var("w")).unwrap();
     let cc = Compiler::new();
-    prop.set_default_expr(3, cc.compile(format!("h - {HEADER_HEIGHT} - 1")).unwrap()).unwrap();
+    prop.set_default_expr(3, cc.compile(format!("h - {HEADER_HEIGHT} - 0.5")).unwrap()).unwrap();
     node.set_property_u32(atom, Role::App, "z_index", 0).unwrap();
     let mut shape = VectorShape::new();
     shape.add_gradient_box(
@@ -301,7 +305,8 @@ pub async fn create_bg_mesh(
         expr::load_var("h"),
         [[0., 0., 0., 0.5], [0., 0., 0., 0.5], [0., 0., 0., 0.5], [0., 0., 0., 0.8]],
     );
-    node.set_property_shape(atom, Role::App, "shape", shape).unwrap();
+    let prop = node.get_property("shape").unwrap();
+    prop.set_default_shape(0, shape).unwrap();
     let node =
         node.setup(|me| VectorArt::new(me, app.renderer.clone(), app.redraw_trigger.clone())).await;
     layer.link(node);
@@ -338,7 +343,8 @@ pub async fn create_separator_expr(
         expr::const_f32(1.),
         color,
     );
-    sep_node.set_property_shape(atom, Role::App, "shape", shape).unwrap();
+    let prop = sep_node.get_property("shape").unwrap();
+    prop.set_default_shape(0, shape).unwrap();
     let sep_node = sep_node
         .setup(|me| VectorArt::new(me, app.renderer.clone(), app.redraw_trigger.clone()))
         .await;
@@ -375,7 +381,8 @@ pub async fn create_separator(
         expr::const_f32(1.),
         color,
     );
-    sep_node.set_property_shape(atom, Role::App, "shape", shape).unwrap();
+    let prop = sep_node.get_property("shape").unwrap();
+    prop.set_default_shape(0, shape).unwrap();
     let sep_node = sep_node
         .setup(|me| VectorArt::new(me, app.renderer.clone(), app.redraw_trigger.clone()))
         .await;
@@ -432,7 +439,8 @@ pub async fn create_bottom_button(
         1.,
         COLOR_TEAL,
     );
-    node.set_property_shape(atom, Role::App, "shape", shape).unwrap();
+    let prop = node.get_property("shape").unwrap();
+    prop.set_default_shape(0, shape).unwrap();
     let node =
         node.setup(|me| VectorArt::new(me, app.renderer.clone(), app.redraw_trigger.clone())).await;
     btn_layer.link(node);
@@ -450,9 +458,9 @@ pub async fn create_bottom_button(
         label_node.set_property_str(atom, Role::App, "text", text).unwrap();
         label_node.set_property_enum(atom, Role::App, "text_align", "center").unwrap();
 
-        let prop = label_node.get_property("text_color").unwrap();
-        prop.set_default_f32_multi(&[COLOR_CYAN[0], COLOR_CYAN[1], COLOR_CYAN[2], COLOR_CYAN[3]])
-            .unwrap();
+        // Class wiring: bottom-button labels follow the shared wallet accent token
+        let theme = app.sg_root.lookup_node("/theme").unwrap();
+        wire_color(&label_node, "text_color", &theme, "wallet.accent_color").unwrap();
         label_node.set_property_u32(atom, Role::App, "z_index", 3).unwrap();
         let label_node = label_node
             .setup(|me| {
@@ -590,7 +598,8 @@ async fn create_bottom_button_state(
         1.,
         outline_color,
     );
-    node.set_property_shape(atom, Role::App, "shape", shape).unwrap();
+    let prop = node.get_property("shape").unwrap();
+    prop.set_default_shape(0, shape).unwrap();
     let node =
         node.setup(|me| VectorArt::new(me, app.renderer.clone(), app.redraw_trigger.clone())).await;
     btn_layer.link(node);
@@ -606,8 +615,13 @@ async fn create_bottom_button_state(
     label_node.set_property_str(atom, Role::App, "text", label_text).unwrap();
     label_node.set_property_enum(atom, Role::App, "text_align", "center").unwrap();
 
-    let prop = label_node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&label_color).unwrap();
+    if is_active {
+        let theme = app.sg_root.lookup_node("/theme").unwrap();
+        wire_color(&label_node, "text_color", &theme, "wallet.accent_color").unwrap();
+    } else {
+        let prop = label_node.get_property("text_color").unwrap();
+        prop.set_default_f32_multi(&label_color).unwrap();
+    }
     label_node.set_property_u32(atom, Role::App, "z_index", 3).unwrap();
     let label_node = label_node
         .setup(|me| {
@@ -699,7 +713,8 @@ pub async fn create_tooltip(
         1.,
         text_color,
     );
-    node.set_property_shape(atom, Role::App, "shape", shape).unwrap();
+    let prop = node.get_property("shape").unwrap();
+    prop.set_default_shape(0, shape).unwrap();
     let node =
         node.setup(|me| VectorArt::new(me, app.renderer.clone(), app.redraw_trigger.clone())).await;
     tooltip_layer.link(node);
@@ -714,9 +729,9 @@ pub async fn create_tooltip(
     prop.set_default_f32(3, text_height).unwrap();
     node.get_property("font_size").unwrap().set_default_f32(0, HINT_FONTSIZE).unwrap();
 
-    let prop = node.get_property("text_color").unwrap();
-    prop.set_default_f32_multi(&[text_color[0], text_color[1], text_color[2], text_color[3]])
-        .unwrap();
+    // Class wiring: tooltip text follows the shared wallet accent token.
+    let theme = app.sg_root.lookup_node("/theme").unwrap();
+    wire_color(&node, "text_color", &theme, "wallet.accent_color").unwrap();
     node.set_property_u32(atom, Role::App, "z_index", 3).unwrap();
     let node = node
         .setup(|me| {

+ 0 - 10
bin/app/src/mesh.rs

@@ -68,16 +68,6 @@ pub const COLOR_BLACK: Color = [0., 0., 0., 1.];
 #[allow(dead_code)]
 pub const COLOR_GREY: Color = [0.5, 0.5, 0.5, 1.];
 #[allow(dead_code)]
-pub const COLOR_DARKTEAL: Color = [0.3686, 0.6, 0.5451, 1.];
-#[allow(dead_code)]
-pub const COLOR_PALECYAN: Color = [0.6902, 1., 1., 1.];
-#[allow(dead_code)]
-pub const COLOR_NEONGREEN: Color = [0.3647, 1., 0.6941, 1.];
-#[allow(dead_code)]
-pub const COLOR_TRANSAQUA: Color = [0.0353, 0.1294, 0.1333, 0.60];
-#[allow(dead_code)]
-pub const COLOR_TRANSTEAL: Color = [0.0902, 0.1333, 0.1333, 0.60];
-#[allow(dead_code)]
 pub const DONE_MENU_BTN_GRADIENT: [[f32; 4]; 4] =
     [[0., 0.1, 0.15, 1.], [0., 0.1, 0.15, 1.], [0., 0., 0., 1.], [0., 0., 0., 1.]];
 #[allow(dead_code)]

+ 196 - 37
bin/app/src/theme/light.rs

@@ -32,9 +32,10 @@ use crate::{
         schema::{
             chat::ui_consts::{BACKARROW_SCALE, EMOJI_CLOSE_SCALE, EMOJI_SCALE},
             menu::ui_consts::{MENU_ICON_SCALE, SETTINGS_ICON_SCALE},
+            wallet::data::{COPY_SCALE, DARKFI_LOGO_SCALE, PASTE_SCALE},
         },
     },
-    error::{Error, Result},
+    error::Result,
     expr,
     mesh::{rgba, COLOR_BLACK, COLOR_WHITE},
     prop::{PropertyAtomicGuard, PropertyValue, Role},
@@ -76,6 +77,17 @@ async fn light_apply(ctx: &ThemeCtx) -> Result<()> {
         ("chatview.hi_bg_color", [0.75, 0.75, 0.75, 1.]),
         ("chatview.action_text_color", [0.20, 0.20, 0.20, 1.]),
         ("chatview.url_text_color", [0.30, 0.20, 0.10, 1.]),
+        ("wallet.text_color", [0.08, 0.08, 0.08, 1.]),
+        ("wallet.title_color", [0.08, 0.08, 0.08, 1.]),
+        ("wallet.label_color", [0.20, 0.20, 0.20, 1.]),
+        ("wallet.accent_color", rgba!(0x3300ffff)),
+        ("wallet.value_color", [0.05, 0.30, 0.55, 1.]),
+        ("wallet.muted_color", [0.45, 0.45, 0.45, 1.]),
+        ("wallet.recipient_color", [0.05, 0.30, 0.55, 1.]),
+        ("wallet.tx_status_recipient_color", [0.05, 0.30, 0.55, 1.]),
+        ("wallet.tx_status_value_color", [0.05, 0.30, 0.55, 1.]),
+        ("wallet.fee_label_color", [0.08, 0.08, 0.08, 1.]),
+        ("wallet.fee_value_color", [0.35, 0.20, 0.55, 1.]),
     ];
     for (name, val) in tokens {
         ctx.set_shared_token_color(atom, name, *val)?;
@@ -85,6 +97,9 @@ async fn light_apply(ctx: &ThemeCtx) -> Result<()> {
 
     let sg_root = ctx.sg_root();
 
+    let accent = rgba!(0x3300ffff);
+
+    // Chat button on the wallet main screen
     let node = sg_root.lookup_node("/window/content/wallet/main_layer/chat_btn_shape").unwrap();
     let color = rgba!(0xF41740ff);
     let mut shape = shape::create_netlogo1(color);
@@ -93,65 +108,208 @@ async fn light_apply(ctx: &ThemeCtx) -> Result<()> {
     let prop = node.get_property("shape").unwrap();
     ctx.set_touched(atom, &prop, 0, PropertyValue::VectorShape(Arc::new(shape)))?;
 
-    let node =
-        sg_root.lookup_node("/window/content/chat/menu_layer/mainbtn_layer/wallet_icon").unwrap();
-    let mut shape = shape::create_blockchain_netlogo1(color);
-    shape.join(shape::create_blockchain_netlogo2(color));
-    shape.join(shape::create_blockchain_netlogo3(color));
-    shape.join(shape::create_blockchain_netlogo4(color));
+    // Main balance background
+    let node = sg_root.lookup_node("/window/content/wallet/main_layer/wallet_balance_bg").unwrap();
+    let mut shape = VectorShape::new();
+    shape.add_filled_box(
+        expr::const_f32(0.),
+        expr::const_f32(0.),
+        expr::load_var("w"),
+        expr::load_var("h"),
+        [0.72, 0.82, 0.97, 0.6],
+    );
     let prop = node.get_property("shape").unwrap();
     ctx.set_touched(atom, &prop, 0, PropertyValue::VectorShape(Arc::new(shape)))?;
 
-    let node =
-        sg_root.lookup_node("/window/content/chat/menu_layer/mainbtn_layer/settings_icon").unwrap();
-    let shape = shape::create_settings(color).scaled(SETTINGS_ICON_SCALE);
+    // Main-menu darkfi logo
+    let node = sg_root.lookup_node("/window/content/wallet/main_menu_btn_bg").unwrap();
+    let color = rgba!(0x3300ffff);
+    let shape = shape::create_darkfi_logo(color).scaled(DARKFI_LOGO_SCALE);
     let prop = node.get_property("shape").unwrap();
     ctx.set_touched(atom, &prop, 0, PropertyValue::VectorShape(Arc::new(shape)))?;
 
-    let node =
-        sg_root.lookup_node("/window/content/chat/menu_layer/mainbtn_layer/write_icon").unwrap();
-    let shape = shape::create_menu_icon(color).scaled(MENU_ICON_SCALE);
+    // Recipient input outline
+    let node = sg_root
+        .lookup_node("/window/content/wallet/send_step2_layer/send_recipient_input_outline")
+        .unwrap();
     let prop = node.get_property("shape").unwrap();
-    ctx.set_touched(atom, &prop, 0, PropertyValue::VectorShape(Arc::new(shape)))?;
-
-    let color = rgba!(0x3300ffff);
-    let node = sg_root.lookup_node("/window/content/wallet/main_layer/receive_label").unwrap();
-    let prop = node.get_property("text_color").unwrap();
-    ctx.set_touched_color(atom, &prop, color)?;
-    let node = sg_root.lookup_node("/window/content/wallet/main_layer/receive_btn_bg").unwrap();
     let mut shape = VectorShape::new();
-    shape.add_outline(
+    shape.add_filled_box(
         expr::const_f32(0.),
         expr::const_f32(0.),
         expr::load_var("w"),
         expr::load_var("h"),
-        1.,
-        color,
+        [0.90, 0.93, 0.97, 0.60],
     );
-    let prop = node.get_property("shape").unwrap();
-    ctx.set_touched(atom, &prop, 0, PropertyValue::VectorShape(Arc::new(shape)))?;
-    let node = sg_root.lookup_node("/window/content/wallet/main_layer/wallet_send_btn_bg").unwrap();
-    let mut shape = VectorShape::new();
     shape.add_outline(
         expr::const_f32(0.),
         expr::const_f32(0.),
         expr::load_var("w"),
         expr::load_var("h"),
         1.,
-        color,
+        [0.78, 0.82, 0.85, 1.],
     );
+    ctx.set_touched(atom, &prop, 0, PropertyValue::VectorShape(Arc::new(shape)))?;
+
+    // Wallet bg
+    let node = sg_root.lookup_node("/window/content/wallet/bg").unwrap();
     let prop = node.get_property("shape").unwrap();
+    let shape = VectorShape::new();
     ctx.set_touched(atom, &prop, 0, PropertyValue::VectorShape(Arc::new(shape)))?;
-    let node = sg_root.lookup_node("/window/content/wallet/main_layer/wallet_send_label").unwrap();
-    let prop = node.get_property("text_color").unwrap();
-    ctx.set_touched_color(atom, &prop, color)?;
-    let node = sg_root.lookup_node("/window/content/wallet/main_layer/tokens_label").unwrap();
-    let prop = node.get_property("text_color").unwrap();
-    ctx.set_touched_color(atom, &prop, COLOR_BLACK)?;
-    let node = sg_root.lookup_node("/window/content/wallet/main_layer/tokens_table").unwrap();
-    let prop = node.get_property("text_color").unwrap();
-    ctx.set_touched_color(atom, &prop, COLOR_BLACK)?;
 
+    // Copy/paste button icons follow the wallet accent
+    let node =
+        sg_root.lookup_node("/window/content/wallet/receive_layer/receive_copy_btn_bg").unwrap();
+    let shape = shape::create_copy(accent).scaled(COPY_SCALE);
+    let prop = node.get_property("shape").unwrap();
+    ctx.set_touched(atom, &prop, 0, PropertyValue::VectorShape(Arc::new(shape)))?;
+
+    let node =
+        sg_root.lookup_node("/window/content/wallet/send_step2_layer/send_paste_btn_bg").unwrap();
+    let shape = shape::create_paste(accent).scaled(PASTE_SCALE);
+    let prop = node.get_property("shape").unwrap();
+    ctx.set_touched(atom, &prop, 0, PropertyValue::VectorShape(Arc::new(shape)))?;
+
+    // Wallet menu icon (chat menu bar) keeps its theme-side recolor.
+    let node =
+        sg_root.lookup_node("/window/content/chat/menu_layer/mainbtn_layer/wallet_icon").unwrap();
+    let color = rgba!(0xF41740ff);
+    let mut shape = shape::create_blockchain_netlogo1(color);
+    shape.join(shape::create_blockchain_netlogo2(color));
+    shape.join(shape::create_blockchain_netlogo3(color));
+    shape.join(shape::create_blockchain_netlogo4(color));
+    let prop = node.get_property("shape").unwrap();
+    ctx.set_touched(atom, &prop, 0, PropertyValue::VectorShape(Arc::new(shape)))?;
+
+    let node =
+        sg_root.lookup_node("/window/content/chat/menu_layer/mainbtn_layer/settings_icon").unwrap();
+    let shape = shape::create_settings(color).scaled(SETTINGS_ICON_SCALE);
+    let prop = node.get_property("shape").unwrap();
+    ctx.set_touched(atom, &prop, 0, PropertyValue::VectorShape(Arc::new(shape)))?;
+
+    let node =
+        sg_root.lookup_node("/window/content/chat/menu_layer/mainbtn_layer/write_icon").unwrap();
+    let shape = shape::create_menu_icon(color).scaled(MENU_ICON_SCALE);
+    let prop = node.get_property("shape").unwrap();
+    ctx.set_touched(atom, &prop, 0, PropertyValue::VectorShape(Arc::new(shape)))?;
+
+    // Wallet receive/send button outlines
+    let outline_overrides = [
+        "/window/content/wallet/main_layer/receive_btn_bg",
+        "/window/content/wallet/main_layer/wallet_send_btn_bg",
+    ];
+    for path in outline_overrides {
+        let node = sg_root.lookup_node(path).unwrap();
+        let prop = node.get_property("shape").unwrap();
+        let mut shape = VectorShape::new();
+        shape.add_outline(
+            expr::const_f32(0.),
+            expr::const_f32(0.),
+            expr::load_var("w"),
+            expr::load_var("h"),
+            1.,
+            accent,
+        );
+        ctx.set_touched(atom, &prop, 0, PropertyValue::VectorShape(Arc::new(shape)))?;
+    }
+
+    // Address boxes
+    let addr_box_paths = [
+        "/window/content/wallet/receive_layer/receive_address_bg",
+        "/window/content/wallet/send_step3_layer/send_recipient_bg",
+        "/window/content/wallet/send_step4_layer/send_recipient_bg",
+    ];
+    for path in addr_box_paths {
+        let node = sg_root.lookup_node(path).unwrap();
+        let prop = node.get_property("shape").unwrap();
+        let mut shape = VectorShape::new();
+        shape.add_filled_box(
+            expr::const_f32(0.),
+            expr::const_f32(0.),
+            expr::load_var("w"),
+            expr::load_var("h"),
+            [0.88, 0.92, 0.99, 0.75],
+        );
+        ctx.set_touched(atom, &prop, 0, PropertyValue::VectorShape(Arc::new(shape)))?;
+    }
+
+    // Wallet bottom button outlines
+    let bottom_btn_outlines = [
+        "/window/content/wallet/receive_layer/receive_copy_tooltip/bg",
+        "/window/content/wallet/send_step2_layer/send_add_recipient_btn_layer/send_add_recipient_btn_bg",
+        "/window/content/wallet/send_step3_layer/send_add_amount_btn_layer/send_add_amount_btn_bg",
+        "/window/content/wallet/send_step4_layer/send_btn_layer/send_btn_bg",
+        "/window/content/wallet/tx_status_layer/close_btn_layer/close_btn_bg",
+    ];
+    for path in bottom_btn_outlines {
+        let node = sg_root.lookup_node(path).unwrap();
+        let prop = node.get_property("shape").unwrap();
+        let mut shape = VectorShape::new();
+        shape.add_outline(
+            expr::const_f32(0.),
+            expr::const_f32(0.),
+            expr::load_var("w"),
+            expr::load_var("h"),
+            1.,
+            accent,
+        );
+        ctx.set_touched(atom, &prop, 0, PropertyValue::VectorShape(Arc::new(shape)))?;
+    }
+
+    // Wallet separators
+    let separator_color = [0.2, 0.2745, 0.2784, 1.];
+    let separator_paths = [
+        "/window/content/wallet/main_layer/wallet_balance_separator",
+        "/window/content/wallet/main_layer/wallet_buttons_separator",
+        "/window/content/wallet/main_layer/tokens_label_separator",
+        "/window/content/wallet/receive_layer/receive_separator",
+        "/window/content/wallet/receive_layer/address_separator",
+        "/window/content/wallet/send_step1_layer/send_separator",
+        "/window/content/wallet/send_step1_layer/pick_label_separator",
+        "/window/content/wallet/send_step2_layer/send_separator",
+        "/window/content/wallet/send_step2_layer/send_token_separator",
+        "/window/content/wallet/send_step3_layer/send_separator",
+        "/window/content/wallet/send_step3_layer/send_token_separator3",
+        "/window/content/wallet/send_step3_layer/send_amount_label_separator",
+        "/window/content/wallet/send_step3_layer/recipient_separator",
+        "/window/content/wallet/send_step3_layer/available_balance_separator",
+        "/window/content/wallet/send_step4_layer/send_separator",
+        "/window/content/wallet/send_step4_layer/send_token_separator4",
+        "/window/content/wallet/send_step4_layer/send_amount_label_separator4",
+        "/window/content/wallet/send_step4_layer/recipient_separator",
+        "/window/content/wallet/send_step4_layer/tx_fee_separator",
+        "/window/content/wallet/tx_status_layer/hint_separator",
+    ];
+    for path in separator_paths {
+        let node = sg_root.lookup_node(path).unwrap();
+        let prop = node.get_property("shape").unwrap();
+        let mut shape = VectorShape::new();
+        shape.add_filled_box(
+            expr::const_f32(0.),
+            expr::const_f32(0.),
+            expr::load_var("w"),
+            expr::const_f32(1.),
+            separator_color,
+        );
+        ctx.set_touched(atom, &prop, 0, PropertyValue::VectorShape(Arc::new(shape)))?;
+    }
+
+    // Wallet titles
+    let title_bg_paths = [
+        "/window/content/wallet/main_layer/tokens_label_bg",
+        "/window/content/wallet/receive_layer/receive_bg",
+        "/window/content/wallet/send_step1_layer/send_bg",
+        "/window/content/wallet/send_step2_layer/send_bg",
+        "/window/content/wallet/send_step3_layer/send_bg",
+        "/window/content/wallet/send_step4_layer/send_bg",
+    ];
+    for path in title_bg_paths {
+        let node = sg_root.lookup_node(path).unwrap();
+        let prop = node.get_property("shape").unwrap();
+        ctx.set_touched(atom, &prop, 0, PropertyValue::VectorShape(Arc::new(VectorShape::new())))?;
+    }
+
+    // Chat labels
     let node = sg_root.lookup_node("/window/content/chat/main_chat_layer/channel_label").unwrap();
     let prop = node.get_property("text_color").unwrap();
     ctx.set_touched_color(atom, &prop, COLOR_BLACK)?;
@@ -159,6 +317,7 @@ async fn light_apply(ctx: &ThemeCtx) -> Result<()> {
     let prop = node.get_property("text_color").unwrap();
     ctx.set_touched_color(atom, &prop, COLOR_BLACK)?;
 
+    // Header
     let node = sg_root.lookup_node("/window/content/header_bg").unwrap();
     let mut shape = VectorShape::new();
     shape.add_filled_box(

+ 24 - 0
bin/app/src/theme/mod.rs

@@ -91,6 +91,18 @@ pub fn shared_token_props() -> Vec<Property> {
         color_prop("chatview.hi_bg_color", [0.25, 0.25, 0.25, 1.]),
         color_prop("chatview.action_text_color", [0.80, 0.80, 0.80, 1.]),
         color_prop("chatview.url_text_color", [0.70, 0.80, 0.90, 1.]),
+        // Wallet
+        color_prop("wallet.text_color", [1., 1., 1., 1.]),
+        color_prop("wallet.title_color", [0.6471, 0.8667, 0.8157, 1.]),
+        color_prop("wallet.label_color", [0.6902, 1., 1., 1.]),
+        color_prop("wallet.accent_color", [0., 1., 1., 1.]),
+        color_prop("wallet.value_color", [0.5451, 1., 1., 1.]),
+        color_prop("wallet.muted_color", [0.3686, 0.6, 0.5451, 1.]),
+        color_prop("wallet.recipient_color", [0.3647, 1., 0.6941, 1.]),
+        color_prop("wallet.tx_status_recipient_color", [0., 1., 0.6392, 1.]),
+        color_prop("wallet.tx_status_value_color", [0.5451, 1., 1., 1.]),
+        color_prop("wallet.fee_label_color", [1., 1., 1., 1.]),
+        color_prop("wallet.fee_value_color", [0.8157, 0.6275, 1., 1.]),
     ]
 }
 
@@ -650,6 +662,18 @@ mod tests {
             "chatview.hi_bg_color",
             "chatview.action_text_color",
             "chatview.url_text_color",
+            // wallet
+            "wallet.text_color",
+            "wallet.title_color",
+            "wallet.label_color",
+            "wallet.accent_color",
+            "wallet.value_color",
+            "wallet.muted_color",
+            "wallet.recipient_color",
+            "wallet.tx_status_recipient_color",
+            "wallet.tx_status_value_color",
+            "wallet.fee_label_color",
+            "wallet.fee_value_color",
         ];
         for name in referenced {
             assert!(theme.get_property(name).is_some(), "missing shared token: {name}");

+ 48 - 3
bin/app/src/theme/scifi.rs

@@ -27,12 +27,13 @@
 
 use darkfi::system::msleep;
 use indoc::indoc;
+use std::sync::Arc;
 
 use crate::{
     app::{
         node::{create_vector_art, create_video},
         schema::{
-            wallet::data::{CHAT_BTN_MARGIN, CHAT_BTN_SIZE},
+            wallet::data::{AMOUNT_FONTSIZE, BASE_FONTSIZE, CHAT_BTN_MARGIN, CHAT_BTN_SIZE},
             MAIN_MENU_HEADER_HEIGHT, VID_ASPECT_RATIO, VID_PATH,
         },
     },
@@ -102,6 +103,17 @@ async fn scifi_apply(ctx: &ThemeCtx) -> Result<()> {
         ("chatview.hi_bg_color", [0., 0.2, 0.2, 1.]),
         ("chatview.action_text_color", [0.5, 0.25, 0.75, 1.]),
         ("chatview.url_text_color", [0., 0.94, 1., 1.]),
+        ("wallet.text_color", [1., 1., 1., 1.]),
+        ("wallet.title_color", [0.6471, 0.8667, 0.8157, 1.]),
+        ("wallet.label_color", [0.6902, 1., 1., 1.]),
+        ("wallet.accent_color", [0., 1., 1., 1.]),
+        ("wallet.value_color", [0.5451, 1., 1., 1.]),
+        ("wallet.muted_color", [0.3686, 0.6, 0.5451, 1.]),
+        ("wallet.recipient_color", [0.3647, 1., 0.6941, 1.]),
+        ("wallet.tx_status_recipient_color", [0., 1., 0.6392, 1.]),
+        ("wallet.tx_status_value_color", [0.5451, 1., 1., 1.]),
+        ("wallet.fee_label_color", [1., 1., 1., 1.]),
+        ("wallet.fee_value_color", [0.8157, 0.6275, 1., 1.]),
     ];
     for (name, val) in tokens {
         ctx.set_shared_token_color(atom, name, *val)?;
@@ -152,14 +164,47 @@ async fn scifi_apply(ctx: &ThemeCtx) -> Result<()> {
         }
     }
 
+    // Recipient input: add radial glow
+    let node = ctx
+        .sg_root()
+        .lookup_node("/window/content/wallet/send_step2_layer/send_recipient_input_outline")
+        .unwrap();
+    let prop = node.get_property("shape").unwrap();
+    let mut shape = VectorShape::new();
+    shape.add_filled_box(
+        expr::const_f32(0.),
+        expr::const_f32(0.),
+        expr::load_var("w"),
+        expr::load_var("h"),
+        [0.0902, 0.1333, 0.1333, 0.60],
+    );
+    shape.add_radial_glow(
+        cc.compile("w / 2").unwrap(),
+        expr::load_var("h"),
+        cc.compile("w * 1.2").unwrap(),
+        cc.compile("h / 2").unwrap(),
+        8,
+        std::f32::consts::PI,
+        2. * std::f32::consts::PI,
+        [0.3, 0.5, 1., 0.25],
+    );
+    shape.add_outline(
+        expr::const_f32(0.),
+        expr::const_f32(0.),
+        expr::load_var("w"),
+        expr::load_var("h"),
+        1.,
+        [0.2, 0.2745, 0.2784, 1.],
+    );
+    ctx.set_touched(atom, &prop, 0, PropertyValue::VectorShape(Arc::new(shape)))?;
+
     // ------------------------------------------------------------------
     // 4. King video background (tracked structural node, reserved low
     //    z band under /window/content) plus the full-screen fade meshes
-    //    darkening it behind the chat and wallet screens
+    //    darkening it behind the chat
     // ------------------------------------------------------------------
     king_video_node(ctx).await;
     bg_fade_node(ctx, "/window/content/chat/main_chat_layer", "bg").await?;
-    bg_fade_node(ctx, "/window/content/wallet/main_layer", "wallet_bg").await?;
 
     // Bg for wallet btn to navigate to chat screen
     let node = create_vector_art("chat_btn_bg");

+ 3 - 0
bin/app/src/ui/tokentable/mod.rs

@@ -350,6 +350,9 @@ impl UIObject for TokenTable {
         // Rect property is its own memo: compare before/after eval.
         let prev_rect = self.rect.get();
         self.rect.eval(atom, &parent_rect).ok()?;
+        // Wired colors (theme tokens)
+        self.text_color.eval(atom).expect("text_color");
+        self.separator_color.eval(atom).expect("separator_color");
         let rect = self.rect.get();
         let rect_changed = rect != prev_rect;