fix(style): Z-Design V4 - El rojo es el estándar, boost para azul

Corrección de rumbo consensuada:
- Colores brillantes (L > 0.2): 22% intensidad (estándar del rojo)
- Colores oscuros (L <= 0.2): 35% intensidad (boost para azul/violeta)

El objetivo es que todos los paneles tengan la vibrancia del rojo.
El azul debe 'saltar' del negro tanto como lo hace el rojo.
This commit is contained in:
R.Eugenio 2025-12-31 00:31:01 +01:00
parent 797cca736c
commit 23204bdd0a

View file

@ -1373,27 +1373,32 @@ pub const DerivedPanelColors = struct {
title_color: Color, title_color: Color,
}; };
/// Derive panel frame colors from a single base color using generic luminance formula. /// Derive panel frame colors from a single base color.
/// ///
/// The derivation is purely mathematical and works for ANY color: /// Z-Design V4: "El rojo es el estándar"
/// - Luminance L = 0.2126*R + 0.7152*G + 0.0722*B /// - Colores brillantes (L > 0.2, ej: rojo): 22% color visible (el estándar bueno)
/// - Blend factor inversely proportional to L /// - Colores oscuros (L <= 0.2, ej: azul): 35% color visible (boost para compensar)
/// ///
/// Example values: /// El objetivo es que TODOS los paneles tengan la vibrancia del rojo.
/// - Blue (L=0.07): blend ~19% more blue visible in dark background /// El azul debe "saltar" del negro tanto como lo hace el rojo.
/// - Red (L=0.21): blend ~18% moderate visibility ///
/// - Green (L=0.72): blend ~13% less green (already bright) /// Luminance L = 0.2126*R + 0.7152*G + 0.0722*B
/// - Yellow (L=0.93): blend ~11% minimal (very bright) /// - Rojo puro: L ~= 0.21 (justo en el límite, usa estándar 22%)
/// - Azul puro: L ~= 0.07 (muy bajo, usa boost 35%)
/// - Verde puro: L ~= 0.72 (alto, usa estándar 22%)
pub fn derivePanelFrameColors(base: Color) DerivedPanelColors { pub fn derivePanelFrameColors(base: Color) DerivedPanelColors {
const L = base.perceptualLuminance(); const L = base.perceptualLuminance();
// Generic formula: blend inversely proportional to luminance // Z-Design V4: Threshold-based boost
// Base blend: 10% (bright colors), Max blend: 20% (dark colors) // El rojo (~22%) es el estándar de "buena vibrancia"
const base_blend: f32 = 0.10; // Colores oscuros como el azul necesitan más intensidad para verse igual
const max_blend: f32 = 0.20; const blend_factor: f32 = if (L > 0.2)
const blend_factor = base_blend + (max_blend - base_blend) * (1.0 - L); 0.22 // Estándar: colores brillantes (rojo, verde, amarillo)
else
0.35; // Boost: colores oscuros (azul, violeta)
// Convert to percentage for blendTowards (inverted: 100% = all black) // Convert to percentage for blendTowards (inverted: 100% = all black)
// focus: full blend, unfocus: half blend
const focus_pct: u8 = @intFromFloat((1.0 - blend_factor) * 100.0); const focus_pct: u8 = @intFromFloat((1.0 - blend_factor) * 100.0);
const unfocus_pct: u8 = @intFromFloat((1.0 - blend_factor * 0.5) * 100.0); const unfocus_pct: u8 = @intFromFloat((1.0 - blend_factor * 0.5) * 100.0);
@ -1408,35 +1413,44 @@ pub fn derivePanelFrameColors(base: Color) DerivedPanelColors {
}; };
} }
test "derivePanelFrameColors blue" { test "derivePanelFrameColors blue gets boost" {
const blue = Color.rgb(59, 130, 246); // laravel_blue // Pure blue has L ~= 0.07, which is < 0.2, so it gets 35% boost
const blue = Color.rgb(0, 0, 255);
const derived = derivePanelFrameColors(blue); const derived = derivePanelFrameColors(blue);
// Blue has low luminance, should get more visible background
// L ~= 0.07 * 0.23 + 0.51 * 0.72 + 0.96 * 0.07 = 0.016 + 0.37 + 0.067 = 0.45
// Actually for laravel_blue: L = 59/255*0.2126 + 130/255*0.7152 + 246/255*0.0722
// = 0.049 + 0.365 + 0.070 = 0.484
// Border focus should be the original color // Border focus should be the original color
try std.testing.expectEqual(blue.r, derived.border_focus.r); try std.testing.expectEqual(blue.r, derived.border_focus.r);
try std.testing.expectEqual(blue.g, derived.border_focus.g); try std.testing.expectEqual(blue.g, derived.border_focus.g);
try std.testing.expectEqual(blue.b, derived.border_focus.b); try std.testing.expectEqual(blue.b, derived.border_focus.b);
// Focus bg should be darker than unfocus bg (more blend towards black) // Focus bg should have significant blue visible (35% boost)
// Actually focus has MORE color (less blend to black) // 35% of 255 = ~89, blended with soft_black (17,17,20)
try std.testing.expect(derived.focus_bg.r >= derived.unfocus_bg.r); try std.testing.expect(derived.focus_bg.b > 70); // Should be vibrant blue
} }
test "derivePanelFrameColors generic formula" { test "derivePanelFrameColors red is standard" {
// Test that brighter colors get less visibility (lower blend) // Red has L ~= 0.21, which is > 0.2, so it gets standard 22%
const dark_blue = Color.rgb(0, 0, 200); // L ~= 0.057 const red = Color.rgb(255, 0, 0);
const bright_yellow = Color.rgb(255, 255, 0); // L ~= 0.93 const derived = derivePanelFrameColors(red);
const blue_derived = derivePanelFrameColors(dark_blue); // Focus bg should have standard red visibility (22%)
const yellow_derived = derivePanelFrameColors(bright_yellow); // 22% of 255 = ~56, blended with soft_black
try std.testing.expect(derived.focus_bg.r > 40);
// Blue background should have more color visible (less black blend) try std.testing.expect(derived.focus_bg.r < 100); // Not too bright
// This means blue's focus_bg should be brighter/more colorful than yellow's relative to base }
// We check that the formula produces different results
try std.testing.expect(blue_derived.focus_bg.b != yellow_derived.focus_bg.b); test "derivePanelFrameColors threshold behavior" {
// Test that blue (L < 0.2) gets MORE visibility than red (L > 0.2)
const blue = Color.rgb(0, 0, 255); // L ~= 0.07
const red = Color.rgb(255, 0, 0); // L ~= 0.21
const blue_derived = derivePanelFrameColors(blue);
const red_derived = derivePanelFrameColors(red);
// Blue gets 35% blend, red gets 22%
// So blue's color channel should be proportionally MORE visible
// Blue: 35% of 255 ~= 89
// Red: 22% of 255 ~= 56
// Blue should have higher relative intensity
try std.testing.expect(blue_derived.focus_bg.b > red_derived.focus_bg.r);
} }