diff --git a/Cargo.toml b/Cargo.toml index bce666a..264decb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -248,20 +248,20 @@ members = [ "crates/modules/barra/barra-web", # ============================================================ -# # modules/cosmobiologia/ — Estudio de astrología profesional -# # ============================================================ -# "crates/modules/cosmobiologia/cosmobiologia-card", -# "crates/modules/cosmobiologia/cosmobiologia-model", -# "crates/modules/cosmobiologia/cosmobiologia-store", -# "crates/modules/cosmobiologia/cosmobiologia-render", -# "crates/modules/cosmobiologia/cosmobiologia-corpus", -# "crates/modules/cosmobiologia/cosmobiologia-engine", -# "crates/modules/cosmobiologia/cosmobiologia-modules", -# "crates/modules/cosmobiologia/cosmobiologia-theme", -# "crates/modules/cosmobiologia/cosmobiologia-canvas", -# "crates/modules/cosmobiologia/cosmobiologia-tree", -# "crates/modules/cosmobiologia/cosmobiologia-panel", -# "crates/modules/cosmobiologia/cosmobiologia-web", +#### # modules/cosmobiologia/ — Estudio de astrología profesional +#### # ============================================================ +#### "crates/modules/cosmobiologia/cosmobiologia-card", +#### "crates/modules/cosmobiologia/cosmobiologia-model", +#### "crates/modules/cosmobiologia/cosmobiologia-store", +#### "crates/modules/cosmobiologia/cosmobiologia-render", +#### "crates/modules/cosmobiologia/cosmobiologia-corpus", +#### "crates/modules/cosmobiologia/cosmobiologia-engine", +#### "crates/modules/cosmobiologia/cosmobiologia-modules", +#### "crates/modules/cosmobiologia/cosmobiologia-theme", +#### "crates/modules/cosmobiologia/cosmobiologia-canvas", +#### "crates/modules/cosmobiologia/cosmobiologia-tree", +#### "crates/modules/cosmobiologia/cosmobiologia-panel", +#### "crates/modules/cosmobiologia/cosmobiologia-web", # ============================================================ # apps/ — Binarios finales que consumen el protocolo @@ -287,9 +287,9 @@ members = [ "crates/apps/pineal-stream-demo", "crates/apps/pineal-phosphor-demo", "crates/apps/pineal-financial-demo", -# "crates/apps/cosmobiologia", -# "crates/apps/cosmobiologia-cli", -# "crates/apps/cosmobiologia-server", +#### "crates/apps/cosmobiologia", +#### "crates/apps/cosmobiologia-cli", +#### "crates/apps/cosmobiologia-server", "crates/apps/dominium", "crates/apps/fana", "crates/apps/agorapura", diff --git a/crates/apps/gioser-web/Cargo.toml b/crates/apps/gioser-web/Cargo.toml index 12e1170..465bd98 100644 --- a/crates/apps/gioser-web/Cargo.toml +++ b/crates/apps/gioser-web/Cargo.toml @@ -40,4 +40,5 @@ features = [ "Performance", "console", "Location", + "History", ] diff --git a/crates/apps/gioser-web/pkg/gioser_web.d.ts b/crates/apps/gioser-web/pkg/gioser_web.d.ts index 6567e62..dfcbe74 100644 --- a/crates/apps/gioser-web/pkg/gioser_web.d.ts +++ b/crates/apps/gioser-web/pkg/gioser_web.d.ts @@ -8,14 +8,15 @@ export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembl export interface InitOutput { readonly memory: WebAssembly.Memory; readonly boot: () => void; - readonly __wasm_bindgen_func_elem_229: (a: number, b: number, c: number) => void; - readonly __wasm_bindgen_func_elem_1437: (a: number, b: number, c: number, d: number) => void; - readonly __wasm_bindgen_func_elem_228: (a: number, b: number, c: number) => void; - readonly __wasm_bindgen_func_elem_228_3: (a: number, b: number, c: number) => void; - readonly __wasm_bindgen_func_elem_510: (a: number, b: number, c: number) => void; - readonly __wasm_bindgen_func_elem_620: (a: number, b: number, c: number) => void; - readonly __wasm_bindgen_func_elem_302: (a: number, b: number, c: number) => void; - readonly __wasm_bindgen_func_elem_303: (a: number, b: number) => void; + readonly __wasm_bindgen_func_elem_234: (a: number, b: number, c: number) => void; + readonly __wasm_bindgen_func_elem_1460: (a: number, b: number, c: number, d: number) => void; + readonly __wasm_bindgen_func_elem_233: (a: number, b: number, c: number) => void; + readonly __wasm_bindgen_func_elem_233_3: (a: number, b: number, c: number) => void; + readonly __wasm_bindgen_func_elem_515: (a: number, b: number, c: number) => void; + readonly __wasm_bindgen_func_elem_635: (a: number, b: number, c: number) => void; + readonly __wasm_bindgen_func_elem_515_6: (a: number, b: number, c: number) => void; + readonly __wasm_bindgen_func_elem_307: (a: number, b: number, c: number) => void; + readonly __wasm_bindgen_func_elem_308: (a: number, b: number) => void; readonly __wbindgen_export: (a: number, b: number) => number; readonly __wbindgen_export2: (a: number, b: number, c: number, d: number) => number; readonly __wbindgen_export3: (a: number) => void; diff --git a/crates/apps/gioser-web/pkg/gioser_web.js b/crates/apps/gioser-web/pkg/gioser_web.js index 92f1e92..0f65406 100644 --- a/crates/apps/gioser-web/pkg/gioser_web.js +++ b/crates/apps/gioser-web/pkg/gioser_web.js @@ -228,13 +228,6 @@ function __wbg_get_imports() { const ret = getObject(arg0).getUniformLocation(getObject(arg1), getStringFromWasm0(arg2, arg3)); return isLikeNone(ret) ? 0 : addHeapObject(ret); }, - __wbg_hash_db43ea0a219f3045: function() { return handleError(function (arg0, arg1) { - const ret = getObject(arg1).hash; - const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_export, wasm.__wbindgen_export2); - const len1 = WASM_VECTOR_LEN; - getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); - getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); - }, arguments); }, __wbg_height_110f80d27112b6b3: function(arg0) { const ret = getObject(arg0).height; return ret; @@ -243,6 +236,10 @@ function __wbg_get_imports() { const ret = getObject(arg0).height; return ret; }, + __wbg_history_4fea091a79b506f0: function() { return handleError(function (arg0) { + const ret = getObject(arg0).history; + return addHeapObject(ret); + }, arguments); }, __wbg_id_e2fe0a117fc8156c: function(arg0, arg1) { const ret = getObject(arg1).id; const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_export, wasm.__wbindgen_export2); @@ -426,6 +423,13 @@ function __wbg_get_imports() { const ret = getObject(arg0).ok; return ret; }, + __wbg_pathname_4da19d3e179041e2: function() { return handleError(function (arg0, arg1) { + const ret = getObject(arg1).pathname; + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_export, wasm.__wbindgen_export2); + const len1 = WASM_VECTOR_LEN; + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); + }, arguments); }, __wbg_pointerId_b61ce7aca1eab0cc: function(arg0) { const ret = getObject(arg0).pointerId; return ret; @@ -433,6 +437,9 @@ function __wbg_get_imports() { __wbg_preventDefault_077a15ca7e97dc5a: function(arg0) { getObject(arg0).preventDefault(); }, + __wbg_pushState_c8f418e428c76877: function() { return handleError(function (arg0, arg1, arg2, arg3, arg4, arg5) { + getObject(arg0).pushState(getObject(arg1), getStringFromWasm0(arg2, arg3), arg4 === 0 ? undefined : getStringFromWasm0(arg4, arg5)); + }, arguments); }, __wbg_querySelectorAll_0981bdbbafa5bf17: function() { return handleError(function (arg0, arg1, arg2) { const ret = getObject(arg0).querySelectorAll(getStringFromWasm0(arg1, arg2)); return addHeapObject(ret); @@ -478,9 +485,6 @@ function __wbg_get_imports() { __wbg_setProperty_ee784b2651f9ff8d: function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { getObject(arg0).setProperty(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); }, arguments); }, - __wbg_set_hash_62c4dbdd31cfb200: function() { return handleError(function (arg0, arg1, arg2) { - getObject(arg0).hash = getStringFromWasm0(arg1, arg2); - }, arguments); }, __wbg_set_height_bdd58e6b04e88cca: function(arg0, arg1) { getObject(arg0).height = arg1 >>> 0; }, @@ -592,51 +596,56 @@ function __wbg_get_imports() { return ret; }, __wbindgen_cast_0000000000000001: function(arg0, arg1) { - // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 187, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`. - const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_1437); + // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 196, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`. + const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_1460); return addHeapObject(ret); }, __wbindgen_cast_0000000000000002: function(arg0, arg1) { // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [F64], shim_idx: 2, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. - const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_229); + const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_234); return addHeapObject(ret); }, __wbindgen_cast_0000000000000003: function(arg0, arg1) { // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("Event")], shim_idx: 6, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. - const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_228); + const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_233); return addHeapObject(ret); }, __wbindgen_cast_0000000000000004: function(arg0, arg1) { // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("KeyboardEvent")], shim_idx: 6, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. - const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_228_3); + const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_233_3); return addHeapObject(ret); }, __wbindgen_cast_0000000000000005: function(arg0, arg1) { // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("MouseEvent")], shim_idx: 144, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. - const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_510); + const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_515); return addHeapObject(ret); }, __wbindgen_cast_0000000000000006: function(arg0, arg1) { - // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("MouseEvent")], shim_idx: 181, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. - const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_620); + // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("MouseEvent")], shim_idx: 190, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_635); return addHeapObject(ret); }, __wbindgen_cast_0000000000000007: function(arg0, arg1) { - // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("PointerEvent")], shim_idx: 74, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. - const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_302); + // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("PointerEvent")], shim_idx: 144, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_515_6); return addHeapObject(ret); }, __wbindgen_cast_0000000000000008: function(arg0, arg1) { - // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 76, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. - const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_303); + // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("PointerEvent")], shim_idx: 74, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_307); return addHeapObject(ret); }, __wbindgen_cast_0000000000000009: function(arg0, arg1) { + // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 76, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_308); + return addHeapObject(ret); + }, + __wbindgen_cast_000000000000000a: function(arg0, arg1) { // Cast intrinsic for `Ref(Slice(F32)) -> NamedExternref("Float32Array")`. const ret = getArrayF32FromWasm0(arg0, arg1); return addHeapObject(ret); }, - __wbindgen_cast_000000000000000a: function(arg0, arg1) { + __wbindgen_cast_000000000000000b: function(arg0, arg1) { // Cast intrinsic for `Ref(String) -> Externref`. const ret = getStringFromWasm0(arg0, arg1); return addHeapObject(ret); @@ -655,34 +664,38 @@ function __wbg_get_imports() { }; } -function __wasm_bindgen_func_elem_303(arg0, arg1) { - wasm.__wasm_bindgen_func_elem_303(arg0, arg1); +function __wasm_bindgen_func_elem_308(arg0, arg1) { + wasm.__wasm_bindgen_func_elem_308(arg0, arg1); } -function __wasm_bindgen_func_elem_228(arg0, arg1, arg2) { - wasm.__wasm_bindgen_func_elem_228(arg0, arg1, addHeapObject(arg2)); +function __wasm_bindgen_func_elem_233(arg0, arg1, arg2) { + wasm.__wasm_bindgen_func_elem_233(arg0, arg1, addHeapObject(arg2)); } -function __wasm_bindgen_func_elem_228_3(arg0, arg1, arg2) { - wasm.__wasm_bindgen_func_elem_228_3(arg0, arg1, addHeapObject(arg2)); +function __wasm_bindgen_func_elem_233_3(arg0, arg1, arg2) { + wasm.__wasm_bindgen_func_elem_233_3(arg0, arg1, addHeapObject(arg2)); } -function __wasm_bindgen_func_elem_510(arg0, arg1, arg2) { - wasm.__wasm_bindgen_func_elem_510(arg0, arg1, addHeapObject(arg2)); +function __wasm_bindgen_func_elem_515(arg0, arg1, arg2) { + wasm.__wasm_bindgen_func_elem_515(arg0, arg1, addHeapObject(arg2)); } -function __wasm_bindgen_func_elem_620(arg0, arg1, arg2) { - wasm.__wasm_bindgen_func_elem_620(arg0, arg1, addHeapObject(arg2)); +function __wasm_bindgen_func_elem_635(arg0, arg1, arg2) { + wasm.__wasm_bindgen_func_elem_635(arg0, arg1, addHeapObject(arg2)); } -function __wasm_bindgen_func_elem_302(arg0, arg1, arg2) { - wasm.__wasm_bindgen_func_elem_302(arg0, arg1, addHeapObject(arg2)); +function __wasm_bindgen_func_elem_515_6(arg0, arg1, arg2) { + wasm.__wasm_bindgen_func_elem_515_6(arg0, arg1, addHeapObject(arg2)); } -function __wasm_bindgen_func_elem_1437(arg0, arg1, arg2) { +function __wasm_bindgen_func_elem_307(arg0, arg1, arg2) { + wasm.__wasm_bindgen_func_elem_307(arg0, arg1, addHeapObject(arg2)); +} + +function __wasm_bindgen_func_elem_1460(arg0, arg1, arg2) { try { const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); - wasm.__wasm_bindgen_func_elem_1437(retptr, arg0, arg1, addHeapObject(arg2)); + wasm.__wasm_bindgen_func_elem_1460(retptr, arg0, arg1, addHeapObject(arg2)); var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true); var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true); if (r1) { @@ -693,8 +706,8 @@ function __wasm_bindgen_func_elem_1437(arg0, arg1, arg2) { } } -function __wasm_bindgen_func_elem_229(arg0, arg1, arg2) { - wasm.__wasm_bindgen_func_elem_229(arg0, arg1, arg2); +function __wasm_bindgen_func_elem_234(arg0, arg1, arg2) { + wasm.__wasm_bindgen_func_elem_234(arg0, arg1, arg2); } function addHeapObject(obj) { diff --git a/crates/apps/gioser-web/pkg/gioser_web_bg.wasm b/crates/apps/gioser-web/pkg/gioser_web_bg.wasm index c8df104..45c7ee6 100644 Binary files a/crates/apps/gioser-web/pkg/gioser_web_bg.wasm and b/crates/apps/gioser-web/pkg/gioser_web_bg.wasm differ diff --git a/crates/apps/gioser-web/pkg/gioser_web_bg.wasm.d.ts b/crates/apps/gioser-web/pkg/gioser_web_bg.wasm.d.ts index 370d339..91b6d92 100644 --- a/crates/apps/gioser-web/pkg/gioser_web_bg.wasm.d.ts +++ b/crates/apps/gioser-web/pkg/gioser_web_bg.wasm.d.ts @@ -2,14 +2,15 @@ /* eslint-disable */ export const memory: WebAssembly.Memory; export const boot: () => void; -export const __wasm_bindgen_func_elem_229: (a: number, b: number, c: number) => void; -export const __wasm_bindgen_func_elem_1437: (a: number, b: number, c: number, d: number) => void; -export const __wasm_bindgen_func_elem_228: (a: number, b: number, c: number) => void; -export const __wasm_bindgen_func_elem_228_3: (a: number, b: number, c: number) => void; -export const __wasm_bindgen_func_elem_510: (a: number, b: number, c: number) => void; -export const __wasm_bindgen_func_elem_620: (a: number, b: number, c: number) => void; -export const __wasm_bindgen_func_elem_302: (a: number, b: number, c: number) => void; -export const __wasm_bindgen_func_elem_303: (a: number, b: number) => void; +export const __wasm_bindgen_func_elem_234: (a: number, b: number, c: number) => void; +export const __wasm_bindgen_func_elem_1460: (a: number, b: number, c: number, d: number) => void; +export const __wasm_bindgen_func_elem_233: (a: number, b: number, c: number) => void; +export const __wasm_bindgen_func_elem_233_3: (a: number, b: number, c: number) => void; +export const __wasm_bindgen_func_elem_515: (a: number, b: number, c: number) => void; +export const __wasm_bindgen_func_elem_635: (a: number, b: number, c: number) => void; +export const __wasm_bindgen_func_elem_515_6: (a: number, b: number, c: number) => void; +export const __wasm_bindgen_func_elem_307: (a: number, b: number, c: number) => void; +export const __wasm_bindgen_func_elem_308: (a: number, b: number) => void; export const __wbindgen_export: (a: number, b: number) => number; export const __wbindgen_export2: (a: number, b: number, c: number, d: number) => number; export const __wbindgen_export3: (a: number) => void; diff --git a/crates/apps/gioser-web/src/lib.rs b/crates/apps/gioser-web/src/lib.rs index ee36472..edc8c3f 100644 --- a/crates/apps/gioser-web/src/lib.rs +++ b/crates/apps/gioser-web/src/lib.rs @@ -79,9 +79,16 @@ impl AppState { self.sync_active_class(); self.sync_taskbar(); self.load_md_if_empty(element, md_url); - // Actualizar hash sin disparar evento (evitar loop) + // Actualizar URL con history.pushState (sin #) if let Some(win) = web_sys::window() { - let _ = win.location().set_hash(&format!("/{}", element)); + if let Ok(hist) = win.history() { + let path = format!("/estudio/{}", element); + let _ = hist.push_state_with_url( + &wasm_bindgen::JsValue::NULL, + "", + Some(&path), + ); + } } } @@ -110,9 +117,15 @@ impl AppState { self.sync_active_class(); self.sync_taskbar(); self.hide_deck(origin_x, origin_y); - // Limpiar hash + // Restaurar URL if let Some(win) = web_sys::window() { - let _ = win.location().set_hash(""); + if let Ok(hist) = win.history() { + let _ = hist.push_state_with_url( + &wasm_bindgen::JsValue::NULL, + "", + Some("/"), + ); + } } } @@ -164,6 +177,7 @@ impl AppState { if let Some(body) = self.document.body() { let _ = body.class_list().add_1("deck-visible"); } + self.sync_page_controls(); } fn hide_deck(&self, x: f64, y: f64) { @@ -175,6 +189,35 @@ impl AppState { if let Some(body) = self.document.body() { let _ = body.class_list().remove_1("deck-visible"); } + self.sync_page_controls(); + } + + fn sync_page_controls(&self) { + let exists = self.document.get_element_by_id("global-page-controls"); + let is_visible = self.state.borrow().active.is_some(); + if let Some(ctl) = exists { + ctl.set_attribute("style", if is_visible { + "opacity:1;pointer-events:auto;" + } else { + "opacity:0;pointer-events:none;" + }).ok(); + } else if is_visible { + let Some(body) = self.document.body() else { return }; + let div: HtmlElement = self.document + .create_element("div") + .ok() + .and_then(|e| e.dyn_into().ok()) + .unwrap(); + div.set_id("global-page-controls"); + div.set_attribute("class", "page-controls").ok(); + div.set_inner_html( + "\ + " + ); + body.append_child(&div).ok(); + } } fn deck_el(&self) -> Option { @@ -243,12 +286,6 @@ impl AppState { }; let html = format!( "
\ -
\ - \ - \ -
\
\
\ {el}\ @@ -448,13 +485,13 @@ pub fn boot() -> Result<(), JsValue> { install_deck_delegation(&document, &app)?; install_taskbar(&document, &app)?; install_keyboard(&document, &app)?; - install_hash_listener(&window, &app)?; + install_popstate_listener(&window, &app)?; install_raf(&window, &document, &canvas, &renderer); - // Leer hash inicial para abrir página directa - if let Ok(hash) = window.location().hash() { - let clean = hash.trim_start_matches('#').trim_start_matches('/'); - if !clean.is_empty() && clean != "" { + // Leer ruta inicial para abrir página directa + if let Ok(pathname) = window.location().pathname() { + let clean = pathname.trim_start_matches('/').trim_start_matches("estudio/"); + if !clean.is_empty() { if let Some(el) = document.query_selector(&format!(".tip[data-md][id='tip-{}']", clean)).ok().flatten() { let rect = el.get_bounding_client_rect(); let cx = rect.left() + rect.width() / 2.0; @@ -469,14 +506,14 @@ pub fn boot() -> Result<(), JsValue> { Ok(()) } -fn install_hash_listener(window: &Window, app: &Rc) -> Result<(), JsValue> { +fn install_popstate_listener(window: &Window, app: &Rc) -> Result<(), JsValue> { let app2 = app.clone(); let doc = app.document.clone(); let win2 = window.clone(); let cb = Closure::::new(move |_e: Event| { - if let Ok(hash) = win2.location().hash() { - let clean = hash.trim_start_matches('#').trim_start_matches('/'); - if clean.is_empty() { + if let Ok(pathname) = win2.location().pathname() { + let clean = pathname.trim_start_matches('/').trim_start_matches("estudio/"); + if clean.is_empty() || clean == "/" { app2.home(); } else if let Some(el) = doc.query_selector(&format!(".tip[data-md][id='tip-{}']", clean)).ok().flatten() { let rect = el.get_bounding_client_rect(); @@ -488,7 +525,7 @@ fn install_hash_listener(window: &Window, app: &Rc) -> Result<(), JsVa } } }); - window.add_event_listener_with_callback("hashchange", cb.as_ref().unchecked_ref())?; + window.add_event_listener_with_callback("popstate", cb.as_ref().unchecked_ref())?; cb.forget(); Ok(()) } @@ -615,22 +652,35 @@ fn install_deck_delegation(document: &Document, app: &Rc) -> Result<() if let Ok(Some(btn)) = target_el.closest("[data-minimize]") { e.stop_propagation(); let element = btn.get_attribute("data-minimize").unwrap_or_default(); - // Origin = la cajita correspondiente en la taskbar (efecto - // visual: la página se "encoge" hacia su entrada del taskbar). - let origin = app2 - .taskbar_item_center(&element) - .unwrap_or_else(|| center_of_element(&btn)); - app2.minimize(origin.0, origin.1); + // Si el data-minimize está vacío, usar el elemento activo + let el = if element.is_empty() { + app2.state.borrow().active.clone().unwrap_or_default() + } else { + element + }; + if !el.is_empty() { + let origin = app2 + .taskbar_item_center(&el) + .unwrap_or_else(|| center_of_element(&btn)); + app2.minimize(origin.0, origin.1); + } return; } // Close if let Ok(Some(btn)) = target_el.closest("[data-close-page]") { e.stop_propagation(); let element = btn.get_attribute("data-close-page").unwrap_or_default(); - let origin = app2 - .taskbar_item_center(&element) - .unwrap_or_else(|| center_of_element(&btn)); - app2.close(&element, origin.0, origin.1); + let el = if element.is_empty() { + app2.state.borrow().active.clone().unwrap_or_default() + } else { + element + }; + if !el.is_empty() { + let origin = app2 + .taskbar_item_center(&el) + .unwrap_or_else(|| center_of_element(&btn)); + app2.close(&el, origin.0, origin.1); + } } }); deck_el.add_event_listener_with_callback("click", cb.as_ref().unchecked_ref())?; diff --git a/crates/apps/gioser-web/styles.css b/crates/apps/gioser-web/styles.css index 43bfe56..bee8dc0 100644 --- a/crates/apps/gioser-web/styles.css +++ b/crates/apps/gioser-web/styles.css @@ -254,14 +254,21 @@ body.deck-active-tierra .deck { --deck-glow: rgba(212, 152, 115, 0.24); } to { opacity: 0.80; } } -/* Head + controls */ +/* Head + controls — fijos en el deck, no dentro de la página */ .page-controls { - position: absolute; - top: 1.8vh; - right: 1.8vw; - z-index: 5; + position: fixed; + top: 1.2rem; + right: 1.2rem; + z-index: 100; display: flex; gap: 0.5rem; + opacity: 0; + pointer-events: none; + transition: opacity 0.3s ease; +} +.deck--visible .page-controls { + opacity: 1; + pointer-events: auto; } .page-control-btn { width: 40px; diff --git a/crates/modules/gioser/gioser-graph-web/Cargo.toml b/crates/modules/gioser/gioser-graph-web/Cargo.toml index 7eac66b..e54eb29 100644 --- a/crates/modules/gioser/gioser-graph-web/Cargo.toml +++ b/crates/modules/gioser/gioser-graph-web/Cargo.toml @@ -35,5 +35,6 @@ features = [ "Event", "EventTarget", "MouseEvent", + "PointerEvent", "console", ] diff --git a/crates/modules/gioser/gioser-graph-web/src/lib.rs b/crates/modules/gioser/gioser-graph-web/src/lib.rs index 3bcb260..36c4f66 100644 --- a/crates/modules/gioser/gioser-graph-web/src/lib.rs +++ b/crates/modules/gioser/gioser-graph-web/src/lib.rs @@ -15,7 +15,7 @@ use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; use wasm_bindgen_futures::JsFuture; use web_sys::{ - Document, HtmlElement, MouseEvent, Response, SvgLineElement, SvgRectElement, + Document, HtmlElement, MouseEvent, PointerEvent, Response, SvgLineElement, SvgRectElement, SvgsvgElement, SvgTextElement, SvgCircleElement, }; @@ -204,13 +204,6 @@ impl GraphWidget { .gb-node { transition: filter 250ms ease, opacity 200ms ease; }\ .gb-node:hover {\ filter: drop-shadow(0 0 14px rgba(255,255,255,0.2));\ - animation: node-bounce 0.4s ease-out;\ - }\ - @keyframes node-bounce {\ - 0% { transform: scale(1); }\ - 40% { transform: scale(1.08); }\ - 70% { transform: scale(0.96); }\ - 100% { transform: scale(1); }\ }\ .gb-edge-group { pointer-events: none; }\ .gb-line { transition: opacity 400ms ease; }", @@ -395,33 +388,37 @@ impl GraphWidget { g.append_child(&text).ok(); g.append_child(&sub).ok(); - // Hover - let rect_clone = rect.clone(); - let color_c = color.clone(); - let glow_clone = glow.clone(); + // Hover + drag + let rect_h = rect.clone(); + let col_h = color.clone(); + let glow_h = glow.clone(); + let g_hover = g.clone(); + let g_clone_for_drag = g.clone(); + let enter = Closure::::new(move |_| { - rect_clone.set_attribute("fill-opacity", "0.55").ok(); - rect_clone.set_attribute("stroke-opacity", "1").ok(); - rect_clone.style() - .set_property("filter", &format!("drop-shadow(0 0 12px {})", color_c)) + rect_h.set_attribute("fill-opacity", "0.55").ok(); + rect_h.set_attribute("stroke-opacity", "1").ok(); + rect_h.style() + .set_property("filter", &format!("drop-shadow(0 0 12px {})", col_h)) .ok(); - glow_clone.set_attribute("fill-opacity", "0.20").ok(); + glow_h.set_attribute("fill-opacity", "0.20").ok(); }); - g.add_event_listener_with_callback("mouseenter", enter.as_ref().unchecked_ref()).ok(); + g_hover.add_event_listener_with_callback("mouseenter", enter.as_ref().unchecked_ref()).ok(); enter.forget(); - let rect_clone2 = rect.clone(); - let glow_clone2 = glow.clone(); + let rect_l = rect.clone(); + let glow_l = glow.clone(); let leave = Closure::::new(move |_| { - rect_clone2.set_attribute("fill-opacity", "0.28").ok(); - rect_clone2.set_attribute("stroke-opacity", "0.7").ok(); - rect_clone2.style().set_property("filter", "none").ok(); - glow_clone2.set_attribute("fill-opacity", "0.05").ok(); + rect_l.set_attribute("fill-opacity", "0.28").ok(); + rect_l.set_attribute("stroke-opacity", "0.7").ok(); + rect_l.style().set_property("filter", "none").ok(); + glow_l.set_attribute("fill-opacity", "0.05").ok(); }); g.add_event_listener_with_callback("mouseleave", leave.as_ref().unchecked_ref()).ok(); leave.forget(); - let nav_target = node.camino.clone(); // pasamos el camino (logos, nomos, etc) + // Click → navegar + let nav_target = node.camino.clone(); let on_nav2 = on_nav.clone(); let click = Closure::::new(move |_| { let mut cb = on_nav2.borrow_mut(); @@ -430,6 +427,50 @@ impl GraphWidget { g.add_event_listener_with_callback("click", click.as_ref().unchecked_ref()).ok(); click.forget(); + // Drag: pointerdown → move → up (reacomodar al soltar) + let g_drag = g.clone(); + let g_move = g.clone(); + let g_up = g.clone(); + let drag_start = Rc::new(RefCell::new(None::<(f64, f64, f64, f64)>)); + let drag_start2 = drag_start.clone(); + let drag_start3 = drag_start.clone(); + + let pdown = Closure::::new(move |e: PointerEvent| { + e.prevent_default(); + let g_rect = g_drag.get_bounding_client_rect(); + *drag_start.borrow_mut() = Some(( + e.client_x() as f64, + e.client_y() as f64, + g_rect.left() + g_rect.width() / 2.0, + g_rect.top() + g_rect.height() / 2.0, + )); + g_drag.set_pointer_capture(e.pointer_id()).ok(); + }); + g.add_event_listener_with_callback("pointerdown", pdown.as_ref().unchecked_ref()).ok(); + pdown.forget(); + + let pmove = Closure::::new(move |e: PointerEvent| { + if let Some((start_cx, start_cy, _, _)) = *drag_start2.borrow() { + let dx = e.client_x() as f64 - start_cx; + let dy = e.client_y() as f64 - start_cy; + g_move.set_attribute( + "transform", + &format!("translate({:.1},{:.1})", dx, dy), + ).ok(); + } + }); + g.add_event_listener_with_callback("pointermove", pmove.as_ref().unchecked_ref()).ok(); + pmove.forget(); + + let pup = Closure::::new(move |_e: PointerEvent| { + *drag_start3.borrow_mut() = None; + g_up.set_attribute("transform", "translate(0,0)").ok(); + }); + g.add_event_listener_with_callback("pointerup", pup.as_ref().unchecked_ref()).ok(); + pup.forget(); + + g.style().set_property("transition", "transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1)").ok(); + nodes_group.append_child(&g).ok(); }