more bug fixes, some cleanup.

Orual 81b460a4 0d64558b

+166 -174
+9 -14
crates/weaver-app/src/components/editor/cursor.rs
··· 44 44 } 45 45 46 46 // Find mapping for this cursor position 47 - let (mapping, should_snap) = find_mapping_for_char(offset_map, char_offset) 47 + let (mapping, _should_snap) = find_mapping_for_char(offset_map, char_offset) 48 48 .ok_or("no mapping found for cursor offset")?; 49 49 50 - tracing::info!("[CURSOR] Restoring cursor at offset {}", char_offset); 51 - tracing::info!("[CURSOR] found mapping: char_range {:?}, node_id '{}', char_offset_in_node {}", 52 - mapping.char_range, mapping.node_id, mapping.char_offset_in_node); 53 - 54 - // If cursor is in invisible content, snap to next visible position 55 - // For now, we'll still use the mapping but this is a future enhancement 56 - if should_snap { 57 - tracing::debug!("cursor in invisible content at offset {}", char_offset); 58 - } 50 + tracing::trace!( 51 + target: "weaver::cursor", 52 + char_offset, 53 + node_id = %mapping.node_id, 54 + mapping_range = ?mapping.char_range, 55 + child_index = ?mapping.child_index, 56 + "restoring cursor position" 57 + ); 59 58 60 59 // Get window and document 61 60 let window = web_sys::window().ok_or("no window")?; ··· 123 122 let mut accumulated_utf16 = 0; 124 123 let mut last_node: Option<web_sys::Node> = None; 125 124 126 - tracing::info!("[CURSOR] Walking text nodes, target_utf16_offset = {}", target_utf16_offset); 127 125 while let Some(node) = walker.next_node()? { 128 126 last_node = Some(node.clone()); 129 127 130 128 if let Some(text) = node.text_content() { 131 129 let text_len = text.encode_utf16().count(); 132 - tracing::info!("[CURSOR] text node: '{}' (utf16_len {}), accumulated = {}", 133 - text.chars().take(20).collect::<String>(), text_len, accumulated_utf16); 134 130 135 131 // Found the node containing target offset 136 132 if accumulated_utf16 + text_len >= target_utf16_offset { 137 133 let offset_in_node = target_utf16_offset - accumulated_utf16; 138 - tracing::info!("[CURSOR] -> FOUND at offset {} in this node", offset_in_node); 139 134 return Ok((node, offset_in_node)); 140 135 } 141 136
+35 -9
crates/weaver-app/src/components/editor/document.rs
··· 87 87 /// Whether the edit is in the block-syntax zone of a line (first ~6 chars). 88 88 /// Edits here could affect block-level syntax like headings, lists, code fences. 89 89 pub in_block_syntax_zone: bool, 90 + /// Document length (in chars) after this edit was applied. 91 + /// Used to detect stale edit info - if current doc length doesn't match, 92 + /// the edit info is from a previous render cycle and shouldn't be used. 93 + pub doc_len_after: usize, 90 94 } 91 95 92 96 /// Max distance from line start where block syntax can appear. ··· 183 187 /// Insert text and record edit info for incremental rendering. 184 188 pub fn insert_tracked(&mut self, pos: usize, text: &str) -> LoroResult<()> { 185 189 let in_block_syntax_zone = self.is_in_block_syntax_zone(pos); 190 + let len_before = self.text.len_unicode(); 191 + let result = self.text.insert(pos, text); 192 + let len_after = self.text.len_unicode(); 186 193 self.last_edit = Some(EditInfo { 187 194 edit_char_pos: pos, 188 - inserted_len: text.chars().count(), 195 + inserted_len: len_after.saturating_sub(len_before), 189 196 deleted_len: 0, 190 197 contains_newline: text.contains('\n'), 191 198 in_block_syntax_zone, 199 + doc_len_after: len_after, 192 200 }); 193 - self.text.insert(pos, text) 201 + result 194 202 } 195 203 196 204 /// Remove text range and record edit info for incremental rendering. 197 205 pub fn remove_tracked(&mut self, start: usize, len: usize) -> LoroResult<()> { 198 206 let content = self.text.to_string(); 199 - let end = start + len; 200 207 let contains_newline = content 201 208 .chars() 202 209 .skip(start) ··· 204 211 .any(|c| c == '\n'); 205 212 let in_block_syntax_zone = self.is_in_block_syntax_zone(start); 206 213 214 + let result = self.text.delete(start, len); 207 215 self.last_edit = Some(EditInfo { 208 216 edit_char_pos: start, 209 217 inserted_len: 0, 210 218 deleted_len: len, 211 219 contains_newline, 212 220 in_block_syntax_zone, 221 + doc_len_after: self.text.len_unicode(), 213 222 }); 214 - self.text.delete(start, len) 223 + result 215 224 } 216 225 217 226 /// Replace text (delete then insert) and record combined edit info. ··· 224 233 .any(|c| c == '\n'); 225 234 let in_block_syntax_zone = self.is_in_block_syntax_zone(start); 226 235 236 + let len_before = self.text.len_unicode(); 237 + // Use splice for atomic replace 238 + self.text.splice(start, len, text)?; 239 + let len_after = self.text.len_unicode(); 240 + 241 + // inserted_len = (len_after - len_before) + deleted_len 242 + // because: len_after = len_before - deleted + inserted 243 + let inserted_len = (len_after + len).saturating_sub(len_before); 244 + 227 245 self.last_edit = Some(EditInfo { 228 246 edit_char_pos: start, 229 - inserted_len: text.chars().count(), 247 + inserted_len, 230 248 deleted_len: len, 231 249 contains_newline: delete_has_newline || text.contains('\n'), 232 250 in_block_syntax_zone, 251 + doc_len_after: len_after, 233 252 }); 234 - 235 - // Use splice for atomic replace 236 - self.text.splice(start, len, text)?; 237 253 Ok(()) 238 254 } 239 255 ··· 321 337 self.doc.export(ExportMode::Snapshot).unwrap_or_default() 322 338 } 323 339 340 + /// Get the current state frontiers for change detection. 341 + /// Frontiers represent the "version" of the document state. 342 + pub fn state_frontiers(&self) -> loro::Frontiers { 343 + self.doc.state_frontiers() 344 + } 345 + 324 346 /// Create a new EditorDocument from a binary snapshot. 325 347 /// Falls back to empty document if import fails. 326 348 /// 327 349 /// If `loro_cursor` is provided, it will be used to restore the cursor position. 328 350 /// Otherwise, falls back to `fallback_offset`. 351 + /// 352 + /// Note: Undo/redo is session-only. The UndoManager tracks operations as they 353 + /// happen in real-time; it cannot rebuild history from imported CRDT ops. 354 + /// For cross-session "undo", use time travel via `doc.checkout(frontiers)`. 329 355 pub fn from_snapshot( 330 356 snapshot: &[u8], 331 357 loro_cursor: Option<Cursor>, ··· 341 367 342 368 let text = doc.get_text("content"); 343 369 344 - // Set up undo manager 370 + // Set up undo manager - tracks operations from this point forward only 345 371 let mut undo_mgr = UndoManager::new(&doc); 346 372 undo_mgr.set_merge_interval(300); 347 373 undo_mgr.set_max_undo_steps(100);
+50 -75
crates/weaver-app/src/components/editor/mod.rs
··· 140 140 cached_paragraphs.set(new_paras.clone()); 141 141 142 142 // Update syntax visibility after DOM changes 143 - // Debug: log what syntax spans we have 144 - for span in spans.iter() { 145 - tracing::debug!( 146 - "[VISIBILITY_INPUT] span {} char_range {:?} formatted_range {:?}", 147 - span.syn_id, 148 - span.char_range, 149 - span.formatted_range 150 - ); 151 - } 152 143 update_syntax_visibility( 153 144 cursor_offset, 154 145 selection.as_ref(), ··· 157 148 ); 158 149 }); 159 150 160 - // Auto-save with debounce 151 + // Track last saved frontiers to detect changes (peek-only, no subscriptions) 152 + let mut last_saved_frontiers: Signal<Option<loro::Frontiers>> = use_signal(|| None); 153 + 154 + // Auto-save with periodic check (no reactive dependency to avoid loops) 161 155 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] 162 156 use_effect(move || { 163 - // Capture snapshot data, syncing Loro cursor first 164 - let (content, cursor_offset, loro_cursor, snapshot_bytes) = document.with_mut(|doc| { 165 - // Sync Loro cursor to current position before saving 166 - doc.sync_loro_cursor(); 167 - ( 168 - doc.to_string(), 169 - doc.cursor.offset, 170 - doc.loro_cursor().cloned(), 171 - doc.export_snapshot(), 172 - ) 173 - }); 157 + // Check every 500ms if there are unsaved changes 158 + let interval = gloo_timers::callback::Interval::new(500, move || { 159 + // Peek both signals without creating reactive dependencies 160 + let current_frontiers = document.peek().state_frontiers(); 161 + 162 + // Only save if frontiers changed (document was edited) 163 + let needs_save = { 164 + let last_frontiers = last_saved_frontiers.peek(); 165 + match &*last_frontiers { 166 + None => true, // First save 167 + Some(last) => &current_frontiers != last, 168 + } 169 + }; // drop last_frontiers borrow here 170 + 171 + if needs_save { 172 + // Sync cursor and extract data for save 173 + let (content, cursor_offset, loro_cursor, snapshot_bytes) = document.with_mut(|doc| { 174 + doc.sync_loro_cursor(); 175 + ( 176 + doc.to_string(), 177 + doc.cursor.offset, 178 + doc.loro_cursor().cloned(), 179 + doc.export_snapshot(), 180 + ) 181 + }); 182 + 183 + use gloo_storage::Storage as _; // bring trait into scope for LocalStorage::set 184 + let snapshot_b64 = if snapshot_bytes.is_empty() { 185 + None 186 + } else { 187 + Some(base64::Engine::encode( 188 + &base64::engine::general_purpose::STANDARD, 189 + &snapshot_bytes, 190 + )) 191 + }; 192 + let snapshot = storage::EditorSnapshot { 193 + content, 194 + snapshot: snapshot_b64, 195 + cursor: loro_cursor, 196 + cursor_offset, 197 + }; 198 + let _ = gloo_storage::LocalStorage::set("weaver_editor_draft", &snapshot); 174 199 175 - // Save after 500ms of no typing 176 - let timer = gloo_timers::callback::Timeout::new(500, move || { 177 - use gloo_storage::Storage as _; // bring trait into scope for LocalStorage::set 178 - let snapshot_b64 = if snapshot_bytes.is_empty() { 179 - None 180 - } else { 181 - Some(base64::Engine::encode( 182 - &base64::engine::general_purpose::STANDARD, 183 - &snapshot_bytes, 184 - )) 185 - }; 186 - let snapshot = storage::EditorSnapshot { 187 - content, 188 - snapshot: snapshot_b64, 189 - cursor: loro_cursor, 190 - cursor_offset, 191 - }; 192 - let _ = gloo_storage::LocalStorage::set("weaver_editor_draft", &snapshot); 200 + // Update last saved frontiers 201 + last_saved_frontiers.set(Some(current_frontiers)); 202 + } 193 203 }); 194 - timer.forget(); 204 + interval.forget(); 195 205 }); 196 206 197 207 rsx! { ··· 382 392 anchor, 383 393 head: focus, 384 394 }); 385 - tracing::debug!("[SYNC] Selection {}..{}", anchor, focus); 386 395 } else { 387 396 // Collapsed selection (just cursor) 388 397 doc.selection = None; 389 - tracing::debug!("[SYNC] Cursor at {}", focus); 390 398 } 391 399 } 392 400 _ => { ··· 528 536 529 537 /// Handle paste events and insert text at cursor 530 538 fn handle_paste(evt: Event<ClipboardData>, document: &mut Signal<EditorDocument>) { 531 - tracing::info!("[PASTE] handle_paste called"); 532 - 533 539 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] 534 540 { 535 541 use dioxus::web::WebEventExt; ··· 539 545 if let Some(clipboard_evt) = base_evt.dyn_ref::<web_sys::ClipboardEvent>() { 540 546 if let Some(data_transfer) = clipboard_evt.clipboard_data() { 541 547 if let Ok(text) = data_transfer.get_data("text/plain") { 542 - tracing::info!("[PASTE] Got text: {} chars", text.len()); 543 548 document.with_mut(|doc| { 544 549 // Delete selection if present 545 550 if let Some(sel) = doc.selection { ··· 563 568 564 569 /// Handle cut events - extract text, write to clipboard, then delete 565 570 fn handle_cut(evt: Event<ClipboardData>, document: &mut Signal<EditorDocument>) { 566 - tracing::info!("[CUT] handle_cut called"); 567 - 568 571 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] 569 572 { 570 573 use dioxus::web::WebEventExt; ··· 578 581 if start != end { 579 582 // Extract text 580 583 let selected_text = doc.slice(start, end).unwrap_or_default(); 581 - tracing::info!( 582 - "[CUT] Extracted {} chars: {:?}", 583 - selected_text.len(), 584 - &selected_text[..selected_text.len().min(50)] 585 - ); 586 584 587 585 // Write to clipboard BEFORE deleting 588 586 if let Some(data_transfer) = clipboard_evt.clipboard_data() { ··· 609 607 610 608 /// Handle copy events - extract text, clean it up, write to clipboard 611 609 fn handle_copy(evt: Event<ClipboardData>, document: &Signal<EditorDocument>) { 612 - tracing::info!("[COPY] handle_copy called"); 613 - 614 610 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] 615 611 { 616 612 use dioxus::web::WebEventExt; ··· 629 625 let clean_text = selected_text 630 626 .replace('\u{200C}', "") 631 627 .replace('\u{200B}', ""); 632 - 633 - tracing::info!( 634 - "[COPY] Extracted {} chars (cleaned to {})", 635 - selected_text.len(), 636 - clean_text.len() 637 - ); 638 628 639 629 // Write to clipboard 640 630 if let Some(data_transfer) = clipboard_evt.clipboard_data() { ··· 809 799 doc.cursor.offset += 3; 810 800 } else if let Some(ctx) = detect_list_context(doc.loro_text(), doc.cursor.offset) { 811 801 // We're in a list item 812 - tracing::debug!("[ENTER] List context detected: {:?}", ctx); 813 - tracing::debug!( 814 - "[ENTER] Cursor at {}, doc len {}", 815 - doc.cursor.offset, 816 - doc.len_chars() 817 - ); 818 802 if is_list_item_empty(doc.loro_text(), doc.cursor.offset, &ctx) { 819 - tracing::debug!("[ENTER] Item is empty, exiting list"); 820 803 // Empty item - exit list by removing marker and inserting paragraph break 821 804 let line_start = find_line_start(doc.loro_text(), doc.cursor.offset); 822 805 let line_end = find_line_end(doc.loro_text(), doc.cursor.offset); ··· 948 931 indent.len() + number.to_string().len() + 2 // "1. " 949 932 } 950 933 }; 951 - 952 - tracing::debug!( 953 - "[LIST] is_empty check: line={:?}, line.len()={}, marker_len={}, result={}", 954 - line, 955 - line.len(), 956 - marker_len, 957 - line.len() <= marker_len 958 - ); 959 934 960 935 // Item is empty if line length equals marker length (nothing after marker) 961 936 line.len() <= marker_len
+31 -11
crates/weaver-app/src/components/editor/render.rs
··· 151 151 } 152 152 153 153 // Determine if we can use fast path (skip boundary discovery) 154 + // Need cache and non-boundary-affecting edit info (for edit position) 155 + let current_len = text.len_unicode(); 154 156 let use_fast_path = cache.is_some() && edit.is_some() && !is_boundary_affecting(edit.unwrap()); 155 157 158 + tracing::debug!( 159 + target: "weaver::render", 160 + use_fast_path, 161 + has_cache = cache.is_some(), 162 + has_edit = edit.is_some(), 163 + boundary_affecting = edit.map(is_boundary_affecting), 164 + current_len, 165 + "render path decision" 166 + ); 167 + 156 168 // Get paragraph boundaries 157 169 let paragraph_ranges = if use_fast_path { 158 - // Fast path: adjust cached boundaries based on edit 170 + // Fast path: adjust cached boundaries based on actual length change 159 171 let cache = cache.unwrap(); 160 172 let edit = edit.unwrap(); 161 173 162 174 // Find which paragraph the edit falls into 163 175 let edit_pos = edit.edit_char_pos; 164 - let char_delta = edit.inserted_len as isize - edit.deleted_len as isize; 176 + 177 + // Compute delta from actual length difference, not edit info 178 + // This handles stale edits gracefully (delta = 0 if lengths match) 179 + let cached_len = cache.paragraphs.last().map(|p| p.char_range.end).unwrap_or(0); 180 + let char_delta = current_len as isize - cached_len as isize; 165 181 166 182 // Adjust each cached paragraph's range 167 183 cache ··· 210 226 } 211 227 }; 212 228 213 - tracing::debug!("[RENDER] Discovered {} paragraph ranges", paragraph_ranges.len()); 229 + // Log discovered paragraphs 214 230 for (i, (byte_range, char_range)) in paragraph_ranges.iter().enumerate() { 215 - tracing::debug!("[RENDER] Range {}: bytes {:?}, chars {:?}", i, byte_range, char_range); 231 + let preview: String = text_slice_to_string(text, char_range.clone()) 232 + .chars() 233 + .take(30) 234 + .collect(); 235 + tracing::trace!( 236 + target: "weaver::render", 237 + para_idx = i, 238 + char_range = ?char_range, 239 + byte_range = ?byte_range, 240 + preview = %preview, 241 + "paragraph boundary" 242 + ); 216 243 } 217 244 218 245 // Render paragraphs, reusing cache where possible ··· 224 251 for (idx, (byte_range, char_range)) in paragraph_ranges.iter().enumerate() { 225 252 let para_source = text_slice_to_string(text, char_range.clone()); 226 253 let source_hash = hash_source(&para_source); 227 - 228 - tracing::debug!( 229 - "[RENDER] Para {}: char_range {:?}, source preview: {:?}", 230 - idx, 231 - char_range, 232 - &para_source[..para_source.len().min(50)] 233 - ); 234 254 235 255 // Check if we have a cached render with matching hash 236 256 let cached_match =
+5 -2
crates/weaver-app/src/components/editor/storage.rs
··· 16 16 /// 17 17 /// Stores both human-readable content and CRDT snapshot for best of both worlds: 18 18 /// - `content`: Human-readable text for debugging 19 - /// - `snapshot`: Base64-encoded CRDT state for full undo history restoration 19 + /// - `snapshot`: Base64-encoded CRDT state for document history 20 20 /// - `cursor`: Loro Cursor (serialized as JSON) for stable cursor position 21 21 /// - `cursor_offset`: Fallback cursor position if Loro cursor can't be restored 22 + /// 23 + /// Note: Undo/redo is session-only (UndoManager state is ephemeral). 24 + /// For cross-session "undo", use time travel via `doc.checkout(frontiers)`. 22 25 #[derive(Serialize, Deserialize, Clone, Debug)] 23 26 pub struct EditorSnapshot { 24 27 /// Human-readable document content (for debugging/fallback) 25 28 pub content: String, 26 - /// Base64-encoded CRDT snapshot (preserves undo history) 29 + /// Base64-encoded CRDT snapshot 27 30 pub snapshot: Option<String>, 28 31 /// Loro Cursor for stable cursor position tracking 29 32 pub cursor: Option<Cursor>,
+22 -14
crates/weaver-app/src/components/editor/visibility.rs
··· 72 72 .map(|r| selection_overlaps(selection, r)) 73 73 .unwrap_or(false); 74 74 75 - tracing::debug!( 76 - "[VISIBILITY] span {} char_range {:?} formatted_range {:?} cursor {} -> in_extended={} in_formatted={} visible={}", 77 - span.syn_id, 78 - span.char_range, 79 - span.formatted_range, 80 - cursor_offset, 81 - in_extended, 82 - in_formatted_region, 83 - result 84 - ); 85 - 86 75 result 87 76 } 88 77 SyntaxType::Block => { 89 - // Show if cursor anywhere in same paragraph 90 - cursor_in_same_paragraph(cursor_offset, &span.char_range, paragraphs) 91 - || selection_overlaps(selection, &span.char_range) 78 + // Show if cursor anywhere in same paragraph (with slop for edge cases) 79 + // The slop handles typing at the end of a heading like "# |" 80 + let para_bounds = find_paragraph_bounds(&span.char_range, paragraphs); 81 + let in_paragraph = para_bounds 82 + .as_ref() 83 + .map(|p| { 84 + // Extend paragraph bounds by 1 char on each side for slop 85 + let ext_start = p.start.saturating_sub(1); 86 + let ext_end = p.end.saturating_add(1); 87 + cursor_offset >= ext_start && cursor_offset <= ext_end 88 + }) 89 + .unwrap_or(false); 90 + 91 + in_paragraph || selection_overlaps(selection, &span.char_range) 92 92 } 93 93 }; 94 94 ··· 96 96 visible.insert(span.syn_id.clone()); 97 97 } 98 98 } 99 + 100 + tracing::debug!( 101 + target: "weaver::visibility", 102 + cursor_offset, 103 + total_spans = syntax_spans.len(), 104 + visible_count = visible.len(), 105 + "calculated visibility" 106 + ); 99 107 100 108 Self { 101 109 visible_span_ids: visible,
+14 -46
crates/weaver-app/src/components/editor/writer.rs
··· 351 351 let format_end = self.last_char_offset; 352 352 let formatted_range = format_start..format_end; 353 353 354 - tracing::debug!( 355 - "[FINALIZE_PAIRED] Setting formatted_range {:?} for opening '{}' and closing (last span)", 356 - formatted_range, 357 - opening_syn_id 358 - ); 359 - 360 354 // Update the opening span's formatted_range 361 355 if let Some(opening_span) = self 362 356 .syntax_spans ··· 364 358 .find(|s| s.syn_id == opening_syn_id) 365 359 { 366 360 opening_span.formatted_range = Some(formatted_range.clone()); 367 - tracing::debug!("[FINALIZE_PAIRED] Updated opening span {}", opening_syn_id); 368 361 } else { 369 362 tracing::warn!("[FINALIZE_PAIRED] Could not find opening span {}", opening_syn_id); 370 363 } ··· 375 368 // Only update if it's an inline span (closing syntax should be inline) 376 369 if closing_span.syntax_type == SyntaxType::Inline { 377 370 closing_span.formatted_range = Some(formatted_range); 378 - tracing::debug!("[FINALIZE_PAIRED] Updated closing span {}", closing_span.syn_id); 379 371 } 380 372 } 381 373 } ··· 389 381 let char_start = self.last_char_offset; 390 382 let syntax_char_len = syntax.chars().count(); 391 383 let char_end = char_start + syntax_char_len; 384 + 385 + tracing::trace!( 386 + target: "weaver::writer", 387 + byte_range = ?range, 388 + char_range = ?(char_start..char_end), 389 + syntax = %syntax.escape_debug(), 390 + "emit_syntax" 391 + ); 392 392 393 393 // In boundary_only mode, just update offsets without HTML 394 394 if self.boundary_only { ··· 438 438 439 439 // Record offset mapping for this syntax 440 440 self.record_mapping(range.clone(), char_start..char_end); 441 - tracing::debug!( 442 - "[EMIT_SYNTAX] Updating offsets: last_char {} -> {}, last_byte {} -> {}", 443 - self.last_char_offset, 444 - char_end, 445 - self.last_byte_offset, 446 - range.end 447 - ); 448 441 self.last_char_offset = char_end; 449 442 self.last_byte_offset = range.end; // Mark bytes as processed 450 443 ··· 616 609 /// to the writer passed in the constructor. 617 610 pub fn run(mut self) -> Result<WriterResult, W::Error> { 618 611 while let Some((event, range)) = self.events.next() { 619 - // Log events for debugging 620 - // tracing::debug!("[WRITER] Event: {:?}, range: {:?}, last_byte: {}, last_char: {}", 621 - // match &event { 622 - // Event::Start(tag) => format!("Start({:?})", tag), 623 - // Event::End(tag) => format!("End({:?})", tag), 624 - // Event::Text(t) => format!("Text('{}')", t), 625 - // Event::Code(t) => format!("Code('{}')", t), 626 - // Event::Html(t) => format!("Html('{}')", t), 627 - // Event::InlineHtml(t) => format!("InlineHtml('{}')", t), 628 - // Event::FootnoteReference(t) => format!("FootnoteReference('{}')", t), 629 - // Event::SoftBreak => "SoftBreak".to_string(), 630 - // Event::HardBreak => "HardBreak".to_string(), 631 - // Event::Rule => "Rule".to_string(), 632 - // Event::TaskListMarker(b) => format!("TaskListMarker({})", b), 633 - // Event::WeaverBlock(t) => format!("WeaverBlock('{}')", t), 634 - // Event::InlineMath(t) => format!("InlineMath('{}')", t), 635 - // Event::DisplayMath(t) => format!("DisplayMath('{}')", t), 636 - // }, 637 - // &range, 638 - // self.last_byte_offset, 639 - // self.last_char_offset 640 - // ); 612 + tracing::trace!( 613 + target: "weaver::writer", 614 + event = ?event, 615 + byte_range = ?range, 616 + "processing event" 617 + ); 641 618 642 619 // For End events, emit any trailing content within the event's range 643 620 // BEFORE calling end_tag (which calls end_node and clears current_node_id) ··· 654 631 655 632 if matches!(&event, Event::End(_)) && !is_inline_format_end { 656 633 // Emit gap from last_byte_offset to range.end 657 - // (emit_syntax handles char offset tracking) 658 634 self.emit_gap_before(range.end)?; 659 635 } else if !matches!(&event, Event::End(_)) { 660 636 // For other events, emit any gap before range.start ··· 1240 1216 } 1241 1217 1242 1218 // Update tracking - we've consumed this opening syntax 1243 - tracing::debug!( 1244 - "[START_TAG] Opening syntax '{}': last_char {} -> {}, last_byte {} -> {}", 1245 - syntax, 1246 - self.last_char_offset, 1247 - char_end, 1248 - self.last_byte_offset, 1249 - range.start + syntax_byte_len 1250 - ); 1251 1219 self.last_char_offset = char_end; 1252 1220 self.last_byte_offset = range.start + syntax_byte_len; 1253 1221 }
-3
crates/weaver-app/src/views/navbar.rs
··· 16 16 /// routes will be rendered under the outlet inside this component 17 17 #[component] 18 18 pub fn Navbar() -> Element { 19 - tracing::debug!("Navbar component rendering"); 20 19 let route = use_route::<Route>(); 21 20 tracing::debug!("Route: {:?}", route); 22 21 ··· 33 32 34 33 let fetcher = use_context::<Fetcher>(); 35 34 let mut show_login_modal = use_signal(|| false); 36 - 37 - tracing::debug!("Navbar got route_handle: {:?}", route_handle.read()); 38 35 39 36 rsx! { 40 37 document::Link { rel: "stylesheet", href: NAVBAR_CSS }