fix text sizing, and add custom hotkey support

This commit is contained in:
Evan Carroll 2026-01-16 13:58:19 -06:00
parent 09590edd95
commit ee425e224e
10 changed files with 1096 additions and 280 deletions

View file

@ -10,7 +10,9 @@ use uuid::Uuid;
use chattyness_db::models::ChannelMemberWithAvatar;
use super::chat_types::{emotion_bubble_colors, ActiveBubble};
use super::settings::BASE_PROP_SIZE;
/// Base text size multiplier. Text at 100% slider = base_sizes * 1.4
const BASE_TEXT_SCALE: f64 = 1.4;
/// Get a unique key for a member (for Leptos For keying).
pub fn member_key(m: &ChannelMemberWithAvatar) -> (Option<Uuid>, Option<Uuid>) {
@ -41,6 +43,9 @@ pub fn AvatarCanvas(
z_index: i32,
/// Active speech bubble for this user (if any).
active_bubble: Option<ActiveBubble>,
/// Text size multiplier for display names, chat bubbles, and badges.
#[prop(default = 1.0)]
text_em_size: f64,
) -> impl IntoView {
let canvas_ref = NodeRef::<leptos::html::Canvas>::new();
@ -54,11 +59,24 @@ pub fn AvatarCanvas(
let canvas_x = member.member.position_x * scale_x + offset_x - prop_size / 2.0;
let canvas_y = member.member.position_y * scale_y + offset_y - prop_size;
// Calculate canvas size (extra height for bubble and name)
let bubble_extra = if active_bubble.is_some() { prop_size * 1.5 } else { 0.0 };
let name_extra = 20.0;
let canvas_width = prop_size.max(200.0); // Wide enough for bubble
let canvas_height = prop_size + bubble_extra + name_extra;
// Fixed text dimensions (independent of prop_size/zoom)
// Text stays readable regardless of zoom level - only affected by text_em_size slider
let text_scale = text_em_size * BASE_TEXT_SCALE;
let fixed_bubble_height = if active_bubble.is_some() {
// 4 lines * 16px line_height + 16px padding + 8px tail + 5px margin
(4.0 * 16.0 + 16.0 + 8.0 + 5.0) * text_scale
} else {
0.0
};
let fixed_name_height = 20.0 * text_scale;
let fixed_text_width = 200.0 * text_scale;
// Canvas must fit both avatar AND fixed-size text
let canvas_width = prop_size.max(fixed_text_width);
let canvas_height = prop_size + fixed_bubble_height + fixed_name_height;
// Adjust bubble_extra for positioning (used later in avatar_cy calculation)
let bubble_extra = fixed_bubble_height;
// Adjust position to account for extra space above avatar
let adjusted_y = canvas_y - bubble_extra;
@ -184,8 +202,8 @@ pub fn AvatarCanvas(
draw_image(emotion_path, &image_cache, &ctx, avatar_cx, avatar_cy, prop_size);
}
// Scale factor for text/badges
let text_scale = prop_size / BASE_PROP_SIZE;
// Text scale independent of zoom - only affected by user's text_em_size setting
let text_scale = text_em_size * BASE_TEXT_SCALE;
// Draw emotion badge if non-neutral
if current_emotion > 0 {
@ -205,18 +223,24 @@ pub fn AvatarCanvas(
let _ = ctx.fill_text(&format!("{}", current_emotion), badge_x, badge_y);
}
// Draw display name below avatar
ctx.set_fill_style_str("#fff");
// Draw display name below avatar (with black outline for readability)
ctx.set_font(&format!("{}px sans-serif", 12.0 * text_scale));
ctx.set_text_align("center");
ctx.set_text_baseline("alphabetic");
let _ = ctx.fill_text(&display_name_clone, avatar_cx, avatar_cy + prop_size / 2.0 + 15.0 * text_scale);
let name_y = avatar_cy + prop_size / 2.0 + 15.0 * text_scale;
// Black outline
ctx.set_stroke_style_str("#000");
ctx.set_line_width(3.0);
let _ = ctx.stroke_text(&display_name_clone, avatar_cx, name_y);
// White fill
ctx.set_fill_style_str("#fff");
let _ = ctx.fill_text(&display_name_clone, avatar_cx, name_y);
// Draw speech bubble if active
if let Some(ref bubble) = active_bubble_clone {
let current_time = js_sys::Date::now() as i64;
if bubble.expires_at >= current_time {
draw_bubble(&ctx, bubble, avatar_cx, avatar_cy - prop_size / 2.0, prop_size);
draw_bubble(&ctx, bubble, avatar_cx, avatar_cy - prop_size / 2.0, prop_size, text_em_size);
}
}
});
@ -248,9 +272,11 @@ fn draw_bubble(
bubble: &ActiveBubble,
center_x: f64,
top_y: f64,
prop_size: f64,
_prop_size: f64,
text_em_size: f64,
) {
let text_scale = prop_size / BASE_PROP_SIZE;
// Text scale independent of zoom - only affected by user's text_em_size setting
let text_scale = text_em_size * BASE_TEXT_SCALE;
let max_bubble_width = 200.0 * text_scale;
let padding = 8.0 * text_scale;
let font_size = 12.0 * text_scale;