Files
brahman/crates/apps/tahuantinsuyu
sergio 2cd34c82da feat(tahuantinsuyu): fase 18 — aspect list + hover tooltips + dignidades + export SVG
Fase grande con 4 features que aprovechan toda la infraestructura ya
construida. Engine ganó 2 módulos nuevos (dignity table data-only +
svg_export), el RenderModel se enriqueció con AspectSummary y los
glyphs con dignity_marker, y el canvas trae hit-test pasivo + lista
textual + botón de export.

## C — Lista textual de aspectos

- engine: nuevo `AspectSummary { module_id, from_body, to_body, kind,
  orb_deg, applying }` + campo `aspect_summary: Vec<AspectSummary>`
  en RenderModel. populate_natal_aspect_summary y
  populate_cross_aspect_summary se llaman desde compose por cada
  pasada (natal + 4 overlays). Ordenado por orb_deg asc (los más
  exactos primero).
- canvas: nuevo aspect_unicode helper (☌ ☍ △ □ ⚹ ⚻ ⚺ ∠ ⚼ Q bQ).
  Footer agrega un grid flex-wrap con las top 12 entries del summary,
  cada una formateada como "[module_id] ☉ △ ☾  ·  2.3° A" coloreado
  por palette.aspect(kind).

## A — Tooltips al hover

- canvas: nuevo HoverInfo { module_id, symbol, deg, house, retrograde,
  dignity_marker, annotation, local_x, local_y } + state.hover.
  on_hover_check ejecuta hit-test sobre todos los glyphs Bodies +
  Outer (threshold 14px); se llama desde el handler MouseMoveEvent
  cuando NO está dragging (handler reescrito para soportar drag y
  hover en mismo callback). Cuando mouse sale del wheel, hover=None.
- Tooltip absoluto: "☉ Tauro · 23.4° · Casa 5 · ℞" con border
  angle_highlight. Posición offset arriba-derecha del planeta,
  clampada al wheel para no salirse.

## B — Dignidades esenciales clásicas

- engine: nuevo mod `dignity` con `Dignity { Rulership/Exaltation/
  Detriment/Fall }` + tabla rules_classical (7 planetas tradicionales)
  + exalts_at table. `essential_dignity(body, sign_index) -> Option`.
  4 tests cubren rulership/detriment/exaltation/fall + edge case
  modernos (Urano/Nept/Plutón sin dignidad clásica). 4 markers:
  + (domicilio), · (exaltación), − (exilio), * (caída).
- engine: Glyph gana campo `dignity_marker: Option<String>`. Default
  derive en Glyph para no romper N construction sites. bridge::
  annotate_dignities mutua RenderModel post-build agregando markers
  a glyphs natales según el signo de cada placement.
- NatalOptions agrega show_dignities. NatalModule.controls() agrega
  Toggle "Dignidades esenciales (+ · − *)" default false.
- canvas: glyph render append dignity_marker al texto después del ᴿ.

## D — Export SVG

- engine: nuevo `pub mod svg_export` con `render_to_svg(&RenderModel)
  -> String`. Reproduce la geometría del canvas en un SVG standalone
  800×800 escalable: anillos zodiacales, cusps, planetas con
  retrograde+dignity markers, aspectos coloreados por kind (cross
  conocen rings de origen/destino vía aspect_radii), labels ASC/MC/
  DESC/IC. Sin dependencias nuevas — write! sobre String. Test
  asserts well-formed XML.
- canvas: CanvasEvent::ExportSvgRequested + botón pequeño "⬇ SVG"
  en el header del wheel (al lado del title).
- shell: on_canvas_event ExportSvgRequested → export_current_to_svg
  recompose actual + svg_export::render_to_svg + write a
  $XDG_DATA_HOME/tahuantinsuyu/exports/<label>_<short_id>.svg.
  Ruta logueada a stderr para que el usuario encuentre el archivo.

`cargo check` y `cargo test` verdes con 8 tests en engine
(2 existentes + 4 dignity + 1 svg + 1 mock).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 12:07:45 +00:00
..