feat(cosmobiologia): GR — scrubbing live de la edad con el jog-dial

Tercer y último incremento del Sistema GR: en modo GR (direcciones
primarias activas) el jog-dial deja de rotar el wheel y pasa a
scrubear la edad en vivo.

- canvas: CanvasState::gr_active() detecta el modo; on_jog_move emite
  CanvasEvent::GrAgeDelta (años por grado de jog, sensibilidad 0.1)
  en vez de rotar; on_jog_up no aplica snap de tiempo.
- shell: scrub_gr_age acumula el delta sobre target_age_years del
  módulo primary_directions, clampa a [0,120], sincroniza el slider
  del panel y recompone — los glifos dirigidos y el HUD se mueven en
  vivo bajo el cursor.

Con esto el Sistema GR queda completo: cómputo de triggers, resaltado
de convergencias, HUD de rectificación y scrubbing live.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
sergio
2026-05-22 13:47:57 +00:00
parent 363f401b75
commit ec111a2e27
2 changed files with 67 additions and 4 deletions
+29
View File
@@ -1172,9 +1172,38 @@ impl Shell {
CanvasEvent::ExportSvgRequested => {
self.export_current_to_svg();
}
CanvasEvent::GrAgeDelta(delta) => {
self.scrub_gr_age(*delta, cx);
}
}
}
/// Scrubbing en vivo de la edad GR vía jog-dial. Acumula `delta`
/// sobre `target_age_years` del módulo `primary_directions`,
/// clampa a [0,120], sincroniza el slider del panel y recompone.
fn scrub_gr_age(&mut self, delta_years: f64, cx: &mut Context<Self>) {
if !module_enabled(&self.module_configs, "primary_directions") {
return;
}
let current = self.module_age_or_current("primary_directions");
let next = (current + delta_years).clamp(0.0, 120.0);
if (next - current).abs() < 1e-6 {
return;
}
let entry = self
.module_configs
.entry("primary_directions".into())
.or_insert_with(|| serde_json::json!({}));
if let serde_json::Value::Object(map) = entry {
map.insert("target_age_years".into(), serde_json::json!(next));
}
self.panel.update(cx, |p, cx| {
p.set_slider("primary_directions", "target_age_years", next, cx)
});
self.persist_module("primary_directions");
self.render_current(cx);
}
/// Recompone la carta actual + escribe el SVG a un archivo en
/// `$XDG_DATA_HOME/cosmobiologia/exports/<label>_<short_id>.svg`.
/// Logea la ruta a stderr — futuro: file save dialog GPUI.