feat(context): Títulos integrados con color adaptativo

Z-Design V5 - Títulos Adaptativos:
- derivePanelFrameColors: title_color según luminosidad del fondo
  - Fondo oscuro (L < 0.5) → base.lightenHsl(90) (blanco teñido)
  - Fondo claro → base.darkenHsl(90) (negro teñido)
- drawPanelFrame: siempre usa title_color (no border_unfocus)
- Márgenes título mejorados: x+10, y+5

Resultado: máximo contraste y legibilidad en todos los paneles.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
R.Eugenio 2025-12-31 01:54:58 +01:00
parent 203a1e6ee5
commit f7e1e346be
2 changed files with 22 additions and 6 deletions

View file

@ -657,11 +657,12 @@ pub const Context = struct {
break :blk config.border_color; break :blk config.border_color;
}; };
// Título adaptativo: siempre alta legibilidad (blanco teñido sobre fondo oscuro)
const title_color: ?Style.Color = blk: { const title_color: ?Style.Color = blk: {
if (config.title_color) |tc| break :blk tc; if (config.title_color) |tc| break :blk tc;
if (config.base_color) |base| { if (config.base_color) |base| {
const derived = Style.derivePanelFrameColors(base); const derived = Style.derivePanelFrameColors(base);
break :blk if (config.has_focus) derived.title_color else derived.border_unfocus; break :blk derived.title_color; // Siempre legible, focus o no
} }
break :blk border_color; break :blk border_color;
}; };
@ -698,12 +699,12 @@ pub const Context = struct {
self.pushCommand(Command.rectOutline(rect.x, rect.y, rect.w, rect.h, border)); self.pushCommand(Command.rectOutline(rect.x, rect.y, rect.w, rect.h, border));
} }
// 5. Draw title if specified // 5. Draw title if specified (margen 10,5 para aire visual)
if (config.title) |title| { if (config.title) |title| {
if (title_color) |tc| { if (title_color) |tc| {
self.pushCommand(.{ .text = .{ self.pushCommand(.{ .text = .{
.x = rect.x + 8, .x = rect.x + 10,
.y = rect.y + 4, .y = rect.y + 5,
.text = title, .text = title,
.color = tc, .color = tc,
} }); } });

View file

@ -1369,18 +1369,33 @@ pub const DerivedPanelColors = struct {
/// - Fondo con focus: 20% base / 80% negro /// - Fondo con focus: 20% base / 80% negro
/// - Fondo sin focus: 12% base / 88% negro /// - Fondo sin focus: 12% base / 88% negro
/// ///
/// Títulos Adaptativos (2025-12-31):
/// - El title_color se calcula para máximo contraste
/// - Fondo oscuro blanco teñido (lightenHsl 90)
/// - Fondo claro negro teñido (darkenHsl 90)
///
/// Los widgets usan bg_transition.current DIRECTAMENTE (mismo fondo que panel) /// Los widgets usan bg_transition.current DIRECTAMENTE (mismo fondo que panel)
/// con bisel de 1px para verse como "huecos" o "relieves" integrados. /// con bisel de 1px para verse como "huecos" o "relieves" integrados.
pub fn derivePanelFrameColors(base: Color) DerivedPanelColors { pub fn derivePanelFrameColors(base: Color) DerivedPanelColors {
const black = Color.soft_black; const black = Color.soft_black;
// Blend fijo: 20% color con focus, 12% sin focus // Blend fijo: 20% color con focus, 12% sin focus
const focus_bg = base.blendTowards(black, 80); // 20% color
// Título adaptativo: contraste máximo según luminosidad del fondo
// Fondos oscuros (L < 0.5) blanco teñido, claros negro teñido
const bg_luminance = focus_bg.perceptualLuminance();
const title_color = if (bg_luminance < 0.5)
base.lightenHsl(90) // Blanco teñido (mantiene tono del panel)
else
base.darkenHsl(90); // Negro teñido
return .{ return .{
.focus_bg = base.blendTowards(black, 80), // 20% color .focus_bg = focus_bg,
.unfocus_bg = base.blendTowards(black, 88), // 12% color .unfocus_bg = base.blendTowards(black, 88), // 12% color
.border_focus = base, .border_focus = base,
.border_unfocus = base.darken(30), .border_unfocus = base.darken(30),
.title_color = base.lightenHsl(20), .title_color = title_color,
}; };
} }