#include "ui.h" #include "splash.h" #include #include #include #include "logo.h" #include "icons.h" #include "hal/board_caps.h" #include "hal/imu_hal.h" // Custom fonts (scaled for 314 PPI, ~1.9x from original 165 PPI) LV_FONT_DECLARE(font_tiempos_56); LV_FONT_DECLARE(font_tiempos_34); LV_FONT_DECLARE(font_styrene_48); LV_FONT_DECLARE(font_styrene_28); LV_FONT_DECLARE(font_styrene_24); LV_FONT_DECLARE(font_styrene_20); LV_FONT_DECLARE(font_styrene_16); LV_FONT_DECLARE(font_styrene_14); // Composite Styrene (Latin) + Montserrat (Cyrillic) — used where user content // (media titles) can be non-Latin; brand Styrene has no Cyrillic glyphs. LV_FONT_DECLARE(font_styrene_cyr_28); LV_FONT_DECLARE(font_styrene_cyr_20); LV_FONT_DECLARE(font_mono_32); // Layout values computed from the active board's geometry. Populated once // in ui_init() and treated as const for the rest of the program. Adding a // new display size means extending compute_layout() with another // breakpoint — never editing the screen-builder functions below. struct Layout { int16_t scr_w, scr_h; int16_t margin; int16_t title_y; int16_t content_y; int16_t content_w; // Usage screen int16_t usage_panel_h; int16_t usage_panel_gap; int16_t usage_bar_y; int16_t usage_reset_y; // Bluetooth screen int16_t bt_info_panel_h; int16_t bt_reset_zone_h; const lv_font_t* bt_title_font; const lv_font_t* bt_status_font; const lv_font_t* bt_device_font; const lv_font_t* bt_credit_1_font; const lv_font_t* bt_credit_2_font; }; static Layout L = {}; // Pick layout values from the active board's pixel dimensions. The two // existing boards happen to land on the two breakpoints below; new ports // inherit the closer one — visually OK, may need a polish pass for // pixel-perfect alignment but never blocks the port from booting. static void compute_layout(const BoardCaps& c) { L.scr_w = c.width; L.scr_h = c.height; L.margin = 20; L.title_y = 30; if (c.height >= 460) { // Large layout — tuned for 480x480 (AMOLED-2.16). L.content_y = 100; L.usage_panel_h = 150; L.usage_panel_gap = 16; L.usage_bar_y = 56; L.usage_reset_y = 94; L.bt_info_panel_h = 160; L.bt_reset_zone_h = 110; L.bt_title_font = &font_tiempos_56; L.bt_status_font = &font_styrene_48; L.bt_device_font = &font_styrene_28; L.bt_credit_1_font = &font_styrene_24; L.bt_credit_2_font = &font_styrene_20; } else { // Compact layout — tuned for 368x448 (AMOLED-1.8). L.content_y = 85; L.usage_panel_h = 130; L.usage_panel_gap = 12; L.usage_bar_y = 48; L.usage_reset_y = 78; L.bt_info_panel_h = 140; L.bt_reset_zone_h = 90; L.bt_title_font = &font_tiempos_34; L.bt_status_font = &font_styrene_28; L.bt_device_font = &font_styrene_20; L.bt_credit_1_font = &font_styrene_16; L.bt_credit_2_font = &font_styrene_14; } L.content_w = L.scr_w - 2 * L.margin; } // Anthropic brand palette — design tokens live in theme.h #include "theme.h" #define COL_BG THEME_BG #define COL_PANEL THEME_PANEL #define COL_TEXT THEME_TEXT #define COL_DIM THEME_DIM #define COL_ACCENT THEME_ACCENT #define COL_GREEN THEME_GREEN #define COL_AMBER THEME_AMBER #define COL_RED THEME_RED #define COL_BAR_BG THEME_BAR_BG // ---- Usage screen widgets (single non-splash view) ---- static lv_obj_t* usage_container; static lv_obj_t* lbl_title; static lv_obj_t* usage_group; // the two usage panels — shown when connected static lv_obj_t* pair_group; // pairing hint — shown when disconnected static lv_obj_t* bar_session; static lv_obj_t* lbl_session_pct; static lv_obj_t* lbl_session_label; static lv_obj_t* lbl_session_reset; static lv_obj_t* bar_weekly; static lv_obj_t* lbl_weekly_pct; static lv_obj_t* lbl_weekly_label; static lv_obj_t* lbl_weekly_reset; static lv_obj_t* lbl_anim; // status line: connection state + whimsical idle // ---- Battery indicator (shared, on top) ---- static lv_obj_t* battery_img; static lv_obj_t* logo_img; static lv_image_dsc_t battery_dscs[5]; // empty, low, medium, full, charging // Latest battery telemetry, pushed by ui_update_battery() and read by the // battery detail screen. minutes_left: >=0 minutes, -1 estimating, -2 charging. static int g_bat_pct = -1; static int g_bat_mv = 0; static int g_bat_min = -2; static bool g_bat_charging = false; // ---- v2 navigation: launcher, stub & bluetooth screens ---- static lv_obj_t* nav_back_btn; // top-left back chevron (shown off the home screen) static lv_obj_t* menu_container; // app launcher (scrollable tile grid) static lv_obj_t* bt_container; // BLE connection info static lv_obj_t* bt_status_lbl; static lv_obj_t* bt_name_lbl; static lv_obj_t* bt_mac_lbl; static lv_obj_t* session_container; // today's tokens / cost static lv_obj_t* sess_cost_lbl; static lv_obj_t* sess_tokens_lbl; static lv_obj_t* sess_gen_lbl; static lv_obj_t* sess_msgs_lbl; static lv_obj_t* nowplaying_container; // media now-playing (Phase 5) static lv_obj_t* np_icon_lbl; // play / pause / music glyph static lv_obj_t* np_title_lbl; // track title (scrolls if long) static lv_obj_t* np_artist_lbl; // track artist static lv_obj_t* np_status_lbl; // "Playing" / "Paused" static lv_obj_t* battery_container; // battery detail (voltage + time left) static lv_obj_t* bat_pct_lbl; // big percentage hero static lv_obj_t* bat_bar; // horizontal charge bar static lv_obj_t* bat_volt_lbl; // "3.92 V" static lv_obj_t* bat_time_lbl; // "~2h 15m left" / "Charging" / "Estimating…" static lv_obj_t* bat_note_lbl; // protective-cutoff note static lv_obj_t* homeassist_container; // Home control: config-driven buttons (Phase 7 M2) static lv_obj_t* ha_status_lbl; // bottom feedback line ("Sent: