atproto blogging
1//! DOM-based syntax visibility updates.
2//!
3//! This module applies visibility state to the DOM by toggling CSS classes
4//! on syntax span elements. Works with the core `VisibilityState` calculation.
5//!
6//! # How it works
7//!
8//! 1. Core's `VisibilityState::calculate()` determines which syntax spans should be visible
9//! 2. This module's `update_syntax_visibility()` applies that state to the DOM
10//! 3. Elements with `data-syn-id` attributes get "hidden" class toggled
11//!
12//! # CSS Integration
13//!
14//! Your CSS should hide elements with the "hidden" class:
15//! ```css
16//! [data-syn-id].hidden {
17//! opacity: 0;
18//! /* or display: none, visibility: hidden, etc. */
19//! }
20//! ```
21
22use weaver_editor_core::{ParagraphRender, Selection, SyntaxSpanInfo};
23
24/// Update syntax span visibility in the DOM based on cursor position.
25///
26/// Calculates which syntax spans should be visible using `VisibilityState::calculate()`,
27/// then toggles the "hidden" class on matching DOM elements.
28///
29/// # Parameters
30/// - `cursor_offset`: Current cursor position in characters
31/// - `selection`: Optional text selection
32/// - `syntax_spans`: All syntax spans from rendered paragraphs
33/// - `paragraphs`: Rendered paragraph data for boundary detection
34///
35/// # DOM Requirements
36/// Syntax span elements must have `data-syn-id` attributes matching `SyntaxSpanInfo.syn_id`.
37#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
38pub fn update_syntax_visibility(
39 cursor_offset: usize,
40 selection: Option<&Selection>,
41 syntax_spans: &[SyntaxSpanInfo],
42 paragraphs: &[ParagraphRender],
43) {
44 use wasm_bindgen::JsCast;
45
46 let visibility = weaver_editor_core::VisibilityState::calculate(
47 cursor_offset,
48 selection,
49 syntax_spans,
50 paragraphs,
51 );
52
53 let Some(window) = web_sys::window() else {
54 return;
55 };
56 let Some(document) = window.document() else {
57 return;
58 };
59
60 // Single querySelectorAll instead of N individual queries.
61 let Ok(node_list) = document.query_selector_all("[data-syn-id]") else {
62 return;
63 };
64
65 for i in 0..node_list.length() {
66 let Some(node) = node_list.item(i) else {
67 continue;
68 };
69
70 let Some(element) = node.dyn_ref::<web_sys::Element>() else {
71 continue;
72 };
73
74 let Some(syn_id) = element.get_attribute("data-syn-id") else {
75 continue;
76 };
77
78 let class_list = element.class_list();
79 if visibility.is_visible(&syn_id) {
80 let _ = class_list.remove_1("hidden");
81 } else {
82 let _ = class_list.add_1("hidden");
83 }
84 }
85}
86
87/// No-op on non-WASM targets.
88#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
89pub fn update_syntax_visibility(
90 _cursor_offset: usize,
91 _selection: Option<&Selection>,
92 _syntax_spans: &[SyntaxSpanInfo],
93 _paragraphs: &[ParagraphRender],
94) {
95}