fix(style): Z-Design V5 - Blend fijo sin compensación perceptual

Simplificación consensuada:
- Eliminar compensación perceptual (causaba más problemas)
- Blend fijo para TODOS los colores:
  - Focus: 18% base / 82% negro
  - Unfocus: 6% base / 94% negro

La fluidez viene de pasar bg_transition.current a widgets,
no de compensar matemáticamente.
This commit is contained in:
R.Eugenio 2025-12-31 00:43:24 +01:00
parent 23204bdd0a
commit f41e502f9c

View file

@ -1375,46 +1375,31 @@ pub const DerivedPanelColors = struct {
/// Derive panel frame colors from a single base color.
///
/// 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)
/// Z-Design V5: Sincronía Atmosférica (2025-12-31)
/// - SIN compensación perceptual (causaba más problemas que soluciones)
/// - Blend fijo y generoso para TODOS los colores:
/// - Fondo con focus: 18% base / 82% negro
/// - Fondo sin focus: 6% base / 94% negro
///
/// 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%)
/// La clave de la fluidez está en pasar bg_transition.current a los widgets,
/// NO en compensar matemáticamente los colores.
pub fn derivePanelFrameColors(base: Color) DerivedPanelColors {
const L = base.perceptualLuminance();
// 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);
const black = Color.soft_black;
// Blend fijo: 18% color con focus, 6% sin focus
// blendTowards(black, 82) = 18% base + 82% black
// blendTowards(black, 94) = 6% base + 94% black
return .{
.focus_bg = base.blendTowards(black, focus_pct),
.unfocus_bg = base.blendTowards(black, unfocus_pct),
.focus_bg = base.blendTowards(black, 82),
.unfocus_bg = base.blendTowards(black, 94),
.border_focus = base,
.border_unfocus = base.darken(30),
.title_color = base.lightenHsl(20),
};
}
test "derivePanelFrameColors blue gets boost" {
// Pure blue has L ~= 0.07, which is < 0.2, so it gets 35% boost
test "derivePanelFrameColors fixed blend" {
// Z-Design V5: All colors use same fixed blend (18% focus, 6% unfocus)
const blue = Color.rgb(0, 0, 255);
const derived = derivePanelFrameColors(blue);
@ -1423,34 +1408,30 @@ test "derivePanelFrameColors blue gets boost" {
try std.testing.expectEqual(blue.g, derived.border_focus.g);
try std.testing.expectEqual(blue.b, derived.border_focus.b);
// 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
// Focus bg: 18% blue + 82% soft_black(17,17,20)
// 0.18 * 255 + 0.82 * 20 = 45.9 + 16.4 = 62.3
try std.testing.expect(derived.focus_bg.b > 50);
try std.testing.expect(derived.focus_bg.b < 80);
}
test "derivePanelFrameColors red is standard" {
// Red has L ~= 0.21, which is > 0.2, so it gets standard 22%
test "derivePanelFrameColors same blend for all colors" {
// V5: No perceptual correction - same blend for red and blue
const blue = Color.rgb(0, 0, 255);
const red = Color.rgb(255, 0, 0);
const derived = derivePanelFrameColors(red);
// 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);
// Both should have ~18% of their primary color channel
// Blue: ~18% of 255 = ~46, Red: ~18% of 255 = ~46
// (plus soft_black contribution)
const blue_intensity = blue_derived.focus_bg.b;
const red_intensity = red_derived.focus_bg.r;
// Should be approximately equal (within tolerance for soft_black blend)
const diff = if (blue_intensity > red_intensity)
blue_intensity - red_intensity
else
red_intensity - blue_intensity;
try std.testing.expect(diff < 10); // Close enough
}