feat(tahuantinsuyu): fase 19 — theme switcher + house tooltips + midpoints + city presets
Fase completa con 4 mejoras independientes que aprovechan toda la
infraestructura previa. La aplicación ahora cubre lecturas profundas
(midpoints uranianos), accesibilidad visual (tooltips de cusps),
personalización (6 themes vía yahweh-widget-theme-switcher) y
usabilidad pragmática (city presets en el form).
## C — Theme switcher en header
- apps/tahuantinsuyu: nueva dep yahweh-widget-theme-switcher.
- shell render(): theme_switcher(cx) en el extremo derecho del header
(con flex_grow del divider del medio). Click cicla entre los 6
presets de yahweh-theme (Nebula, Aurora, Sunset, FlatDark,
SolarizedLight, HighContrast). AstroPalette::for_theme(theme) lee
is_dark, así toda la rueda se re-tinta automáticamente.
## B — Tooltips sobre house cusps
- canvas: HoverInfo deja de ser struct para ser enum con variantes
Body { ... } y HouseCusp { house_number, deg, local_x, local_y }.
Helpers .local() y .key() unifican el acceso.
- on_hover_check: primero hit-test bodies (threshold 14px); si no hubo
match Y el mouse está dentro del anillo de casas
(houses_inner..houses_outer ± 6px), calcula la longitud zodiacal
desde el ángulo de pantalla (inversa de polar_to_screen) y busca el
cusp más cercano (proximidad angular < 2.5°). HoverInfo::HouseCusp.
- Tooltip render: "Cusp Casa N · Signo XX.X°".
## D — MidpointsModule (Uranian-lite)
- engine: PipelineRequest::Midpoints (sin parámetros, default empty).
- bridge: build_midpoints_overlay computa midpoints entre todos los
pares de placements donde involucran Sol o Luna (~10 puntos según
body set). Fórmula: si |a-b|>180, mid=((a+b)/2+180) mod 360, sino
(a+b)/2 mod 360. Emite como Layer { kind: Midpoints, module_id:
"midpoints", ring: 0.62 } con Glyph.symbol="sun/jupiter" y
annotation="Sun/Jupiter".
- modules: midpoints::MidpointsModule con toggle "Activar". Registry
pasa a 7 módulos. Test actualizado.
- shell: build_requests detecta midpoints.enabled, pushea
PipelineRequest::Midpoints (no toma age ni body — es derivado puro).
- canvas: Radii agrega midpoints: r * 0.62 (entre houses_inner y
bodies natales). body_ring("midpoints") y aspect_endpoints retornan
ese radio. paint_wheel agrega un loop para LayerKind::Midpoints
pintando dots pequeños (r=0.012, alpha 0.7 sobre house_cusp color)
— los midpoints no llevan unicode symbol propio (no existe en
Unicode astrológico estándar). El detalle del par viene en hover.
- Hover sobre un midpoint: tooltip muestra "☉/♄ Tauro 14.3° ·
Sun/Jupiter" (display_symbol parsea "a/b" en dos unicodes;
annotation incluye nombres completos eternal).
## A — City presets en el ChartForm
- tree: nueva const CITY_PRESETS con 25 ciudades (Latinoamérica
capitales + 5 europeas + 5 anglosajonas + Tokyo/Sydney/Mumbai/Cairo)
con (name, lat, lon, tz_offset_minutes) sin DST. CityPreset struct.
- tree: TahuantinsuyuTree gana city_picker_open: bool. close_modal
lo resetea. toggle_city_picker + apply_city_preset(preset) helpers.
apply_city_preset lee el Modal activo (CreateChart o EditChart),
llama TextInput::set_text en place/lat/lon/tz del ChartForm,
cierra el picker.
- render_chart_form: title_row ahora tiene "📍 Ciudad rápida ▾"
button a la derecha del title. Click → toggle. Cuando picker_open,
popup absoluto debajo con la lista de presets. Click en preset →
autocompleta + cierra. El usuario sigue pudiendo editar manualmente
cualquier campo después; el preset es solo un punto de partida
rápido para evitar tipear coordenadas a mano.
cargo check verde, 8 tests engine + 1 test modules (7 módulos)
verdes.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -137,6 +137,7 @@ impl Registry {
|
||||
r.register(Box::new(solar_arc::SolarArcModule));
|
||||
r.register(Box::new(synastry::SynastryModule));
|
||||
r.register(Box::new(planetary_return::PlanetaryReturnModule));
|
||||
r.register(Box::new(midpoints::MidpointsModule));
|
||||
r
|
||||
}
|
||||
|
||||
@@ -545,6 +546,49 @@ pub mod solar_arc {
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// MidpointsModule — puntos medios entre cuerpos natales (Sol/Luna)
|
||||
// =====================================================================
|
||||
|
||||
pub mod midpoints {
|
||||
use super::*;
|
||||
|
||||
/// Computa midpoints entre los cuerpos natales (filtrado a los que
|
||||
/// involucran Sol o Luna, ~10 puntos) y los renderea como pequeños
|
||||
/// puntos en un anillo interior. Hovering muestra los dos cuerpos
|
||||
/// que originan el midpoint.
|
||||
pub struct MidpointsModule;
|
||||
|
||||
impl Module for MidpointsModule {
|
||||
fn id(&self) -> &'static str {
|
||||
"midpoints"
|
||||
}
|
||||
fn label(&self) -> &'static str {
|
||||
"Midpoints"
|
||||
}
|
||||
fn description(&self) -> &'static str {
|
||||
"Puntos medios que involucran al Sol o a la Luna."
|
||||
}
|
||||
fn applies_to(&self, kind: ChartKind) -> bool {
|
||||
matches!(kind, ChartKind::Natal)
|
||||
}
|
||||
fn enabled_by_default(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn controls(&self) -> Vec<Control> {
|
||||
vec![Control::Toggle {
|
||||
key: "enabled".into(),
|
||||
label: "Activar".into(),
|
||||
default: false,
|
||||
hotkey: None,
|
||||
}]
|
||||
}
|
||||
fn compute_layers(&self, _chart: &Chart, _cfg: &serde_json::Value) -> Vec<Layer> {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -558,8 +602,9 @@ mod tests {
|
||||
assert!(r.find("solar_arc").is_some());
|
||||
assert!(r.find("synastry").is_some());
|
||||
assert!(r.find("planetary_return").is_some());
|
||||
// Natal kind tiene 6 módulos aplicables.
|
||||
assert_eq!(r.for_kind(ChartKind::Natal).len(), 6);
|
||||
assert!(r.find("midpoints").is_some());
|
||||
// Natal kind tiene 7 módulos aplicables.
|
||||
assert_eq!(r.for_kind(ChartKind::Natal).len(), 7);
|
||||
assert!(r.for_kind(ChartKind::Synastry).is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user