Files
brahman/crates/modules/cosmobiologia/cosmobiologia-web/src/lib.rs
T
sergio 86fb6ae20b feat(cosmobiologia-render): compose_wheel rico con palette + dial 3D + spread + coord labels
El render agnóstico ya no es un esqueleto — porta al WASM la mayoría
de los detalles visuales que tenía solo el canvas gpui nativo:

- palette.rs: Palette dark/light replicando AstroPalette del theme
  nativo, pero en Rgba (no Hsla de gpui). Métodos planet/aspect/sign
  para resolver color por id simbólico, + house_ring con hue-shift.
- CompositionOpts extendido: palette, dial_3d, draw_ascensional_cross,
  show_coord_labels, show_minor_aspects. Defaults razonables.
- compose_wheel ahora dibuja: background panel, dial 3D bevel (4
  strokes concéntricos con alpha decreciente), subdivisiones cada 10°
  con sign boundaries reforzados, signos con color elemental, casas
  topocéntricas + geocéntricas en sus rings canónicos, cuerpos con
  spread anti-solapamiento + clusters + disco coloreado por planeta,
  coord labels "DD°MM'" en natal, aspectos con width inversa al
  orbe + filtrado opcional de minors, cruz ascensional dashed +
  pills ASC/MC/DESC/IC.
- cosmobiologia-web: nuevo render_model_to_svg_themed(dark: bool)
  para que el cliente JS elija palette según preferencia del UA.

Tests del módulo math siguen verdes (10/10). Smoke test del server:
/api/sky.svg ahora emite 22 circles, 77 lines, 52 texts con paleta
real (vs ~6 circles, 24 lines, 36 texts del esqueleto previo).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-19 01:41:36 +00:00

100 lines
3.5 KiB
Rust

//! `cosmobiologia-web` — cdylib WASM que renderiza la rueda
//! astrológica desde el browser, sin round-trip al server por cada
//! interacción.
//!
//! ## Flujo
//!
//! 1. El cliente JS hace `await fetch('/api/sky')` o
//! `/api/charts/:id/render?...` y recibe un `RenderModel` JSON.
//! 2. JS llama `render_model_to_svg(json)` (exportado desde WASM) que
//! deserializa + corre `cosmobiologia_render::compose_wheel` +
//! serializa SVG.
//! 3. JS hace `wheelContainer.innerHTML = svg`.
//!
//! ## Build
//!
//! ```bash
//! cargo install wasm-pack # una vez
//! cd crates/modules/cosmobiologia/cosmobiologia-web
//! wasm-pack build --target web --out-dir ../../../../apps/cosmobiologia-server/static/wasm
//! ```
//!
//! Esto produce un módulo ES6 (`cosmobiologia_web.js` +
//! `cosmobiologia_web_bg.wasm`) que el `index.html` del server
//! importa con `import init, { render_model_to_svg } from
//! '/static/wasm/cosmobiologia_web.js';`.
#![forbid(unsafe_code)]
#![warn(rust_2018_idioms)]
// La API pública SOLO se expone con `wasm-bindgen` en target
// wasm32. En nativo (rlib) el crate compila para validar la
// signature pero no exporta nada — los tests del render ya viven
// en `cosmobiologia-render::math`.
#[cfg(target_arch = "wasm32")]
mod wasm {
use cosmobiologia_render::{
compose_wheel, draw_commands_to_svg, CompositionOpts, Palette, RenderModel,
};
use wasm_bindgen::prelude::*;
/// Renderea un `RenderModel` (JSON string) como SVG. El JSON sale
/// de `/api/sky` o `/api/charts/:id/render` del server.
///
/// `size` es el lado del cuadrado contenedor en px (default 600).
/// `rot_offset_deg` permite rotar la vista (jog-dial / preview).
#[wasm_bindgen]
pub fn render_model_to_svg(
json: &str,
size: f32,
rot_offset_deg: f32,
) -> Result<String, JsValue> {
render_with_opts(json, size, rot_offset_deg, true)
}
/// Variante con palette explícita (dark = `true` por default, light
/// = `false`). El JS pasa el modo según preferencia/tema del UA.
#[wasm_bindgen]
pub fn render_model_to_svg_themed(
json: &str,
size: f32,
rot_offset_deg: f32,
dark: bool,
) -> Result<String, JsValue> {
render_with_opts(json, size, rot_offset_deg, dark)
}
fn render_with_opts(
json: &str,
size: f32,
rot_offset_deg: f32,
dark: bool,
) -> Result<String, JsValue> {
let model: RenderModel = serde_json::from_str(json)
.map_err(|e| JsValue::from_str(&format!("parse RenderModel: {}", e)))?;
let opts = CompositionOpts {
size: if size > 0.0 { size } else { 600.0 },
rot_offset_deg,
palette: if dark { Palette::dark() } else { Palette::light() },
..Default::default()
};
let cmds = compose_wheel(&model, &opts);
Ok(draw_commands_to_svg(&cmds, opts.size))
}
/// Hook de inicialización opcional — wasm_pack lo invoca al
/// cargar el módulo. Útil para instalar un panic hook hacia
/// `console.error`. Por ahora no-op.
#[wasm_bindgen(start)]
pub fn main_js() {}
}
#[cfg(not(target_arch = "wasm32"))]
pub fn _native_marker() {
// Sin target wasm32, el crate solo expone el render como
// transitivo. Esta función vive para que `cargo check -p
// cosmobiologia-web` valide la compilación nativa sin
// wasm-bindgen — útil en CI y en desarrollo desktop.
let _ = std::any::type_name::<cosmobiologia_render::RenderModel>();
}