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,
};
/// 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:
/// - Luminance L = 0.2126*R + 0.7152*G + 0.0722*B
/// - Blend factor inversely proportional to L
/// Z-Design V4: "El rojo es el estándar"
/// - Colores brillantes (L > 0.2, ej: rojo): 22% color visible (el estándar bueno)
/// - Colores oscuros (L <= 0.2, ej: azul): 35% color visible (boost para compensar)
///
/// Example values:
/// - Blue (L=0.07): blend ~19% more blue visible in dark background
/// - Red (L=0.21): blend ~18% moderate visibility
/// - Green (L=0.72): blend ~13% less green (already bright)
/// - Yellow (L=0.93): blend ~11% minimal (very bright)
/// El objetivo es que TODOS los paneles tengan la vibrancia del rojo.
/// El azul debe "saltar" del negro tanto como lo hace el rojo.
///
/// Luminance L = 0.2126*R + 0.7152*G + 0.0722*B
/// - 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 {
const L = base.perceptualLuminance();
// Generic formula: blend inversely proportional to luminance
// Base blend: 10% (bright colors), Max blend: 20% (dark colors)
const base_blend: f32 = 0.10;
const max_blend: f32 = 0.20;
const blend_factor = base_blend + (max_blend - base_blend) * (1.0 - L);
// Z-Design V4: Threshold-based boost
// El rojo (~22%) es el estándar de "buena vibrancia"
// Colores oscuros como el azul necesitan más intensidad para verse igual
const blend_factor: f32 = if (L > 0.2)
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)
// focus: full blend, unfocus: half blend
const focus_pct: u8 = @intFromFloat((1.0 - blend_factor) * 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" {
const blue = Color.rgb(59, 130, 246); // laravel_blue
test "derivePanelFrameColors blue gets boost" {
// 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);
// 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
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.b, derived.border_focus.b);
// Focus bg should be darker than unfocus bg (more blend towards black)
// Actually focus has MORE color (less blend to black)
try std.testing.expect(derived.focus_bg.r >= derived.unfocus_bg.r);
// Focus bg should have significant blue visible (35% boost)
// 35% of 255 = ~89, blended with soft_black (17,17,20)
try std.testing.expect(derived.focus_bg.b > 70); // Should be vibrant blue
}
test "derivePanelFrameColors generic formula" {
// Test that brighter colors get less visibility (lower blend)
const dark_blue = Color.rgb(0, 0, 200); // L ~= 0.057
const bright_yellow = Color.rgb(255, 255, 0); // L ~= 0.93
test "derivePanelFrameColors red is standard" {
// Red has L ~= 0.21, which is > 0.2, so it gets standard 22%
const red = Color.rgb(255, 0, 0);
const derived = derivePanelFrameColors(red);
const blue_derived = derivePanelFrameColors(dark_blue);
const yellow_derived = derivePanelFrameColors(bright_yellow);
// Blue background should have more color visible (less black blend)
// 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);
// Focus bg should have standard red visibility (22%)
// 22% of 255 = ~56, blended with soft_black
try std.testing.expect(derived.focus_bg.r > 40);
try std.testing.expect(derived.focus_bg.r < 100); // Not too bright
}
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);
}