diff --git a/src/app/navigation.rs b/src/app/navigation.rs index 719b77f..ab94aff 100644 --- a/src/app/navigation.rs +++ b/src/app/navigation.rs @@ -1,12 +1,15 @@ use crate::app::AppState; +use crate::excel::{EXCEL_MAX_COLS, EXCEL_MAX_ROWS}; use crate::utils::find_non_empty_cell; use crate::utils::Direction; impl AppState<'_> { pub fn move_cursor(&mut self, delta_row: isize, delta_col: isize) { // Calculate new position - let new_row = (self.selected_cell.0 as isize + delta_row).max(1) as usize; - let new_col = (self.selected_cell.1 as isize + delta_col).max(1) as usize; + let new_row = + (self.selected_cell.0 as isize + delta_row).clamp(1, EXCEL_MAX_ROWS as isize) as usize; + let new_col = + (self.selected_cell.1 as isize + delta_col).clamp(1, EXCEL_MAX_COLS as isize) as usize; // Update selected position self.selected_cell = (new_row, new_col); @@ -155,6 +158,7 @@ impl AppState<'_> { } pub fn ensure_column_visible(&mut self, column: usize) { + let column = column.min(EXCEL_MAX_COLS); let frozen_cols = self.workbook.get_current_sheet().freeze_panes.cols; let min_scroll_col = frozen_cols + 1; @@ -187,11 +191,8 @@ impl AppState<'_> { } // If the column is already visible but at the right edge, try to add a margin - let sheet = self.workbook.get_current_sheet(); - let max_col = sheet.max_cols; - // Only apply margin logic if not at the max column - if column < max_col && column == last_visible_col && scroll_cols_visible > 1 { + if column < EXCEL_MAX_COLS && column == last_visible_col && scroll_cols_visible > 1 { // Adjust start column to show more columns to the left // This creates a margin on the right self.start_col = (column - (scroll_cols_visible - 2)).max(min_scroll_col); diff --git a/src/app/sheet.rs b/src/app/sheet.rs index 92bb2fb..3f76ee2 100644 --- a/src/app/sheet.rs +++ b/src/app/sheet.rs @@ -69,14 +69,10 @@ impl AppState<'_> { // Restore cell position and view position for the new sheet if let Some(saved_position) = self.sheet_cell_positions.get(&new_sheet_name) { - // Ensure the saved position is valid for the current sheet - let sheet = self.workbook.get_current_sheet(); - let valid_row = saved_position.selected.0.min(sheet.max_rows.max(1)); - let valid_col = saved_position.selected.1.min(sheet.max_cols.max(1)); - - self.selected_cell = (valid_row, valid_col); + self.selected_cell = Self::clamp_cell_to_excel_bounds(saved_position.selected); self.start_row = saved_position.view.0; self.start_col = saved_position.view.1; + self.clamp_selected_cell_to_excel_bounds(); // Make sure the view position is valid relative to the selected cell self.handle_scrolling(); @@ -230,14 +226,10 @@ impl AppState<'_> { // Restore saved cell position for the new current sheet or use default if let Some(saved_position) = self.sheet_cell_positions.get(&new_sheet_name) { - // Ensure the saved position is valid for the current sheet - let sheet = self.workbook.get_current_sheet(); - let valid_row = saved_position.selected.0.min(sheet.max_rows.max(1)); - let valid_col = saved_position.selected.1.min(sheet.max_cols.max(1)); - - self.selected_cell = (valid_row, valid_col); + self.selected_cell = Self::clamp_cell_to_excel_bounds(saved_position.selected); self.start_row = saved_position.view.0; self.start_col = saved_position.view.1; + self.clamp_selected_cell_to_excel_bounds(); // Make sure the view position is valid relative to the selected cell self.handle_scrolling(); @@ -314,11 +306,7 @@ impl AppState<'_> { self.workbook.recalculate_max_rows(); self.workbook.recalculate_max_cols(); - let sheet = self.workbook.get_current_sheet(); - - if self.selected_cell.0 > sheet.max_rows { - self.selected_cell.0 = sheet.max_rows.max(1); - } + self.clamp_selected_cell_to_excel_bounds(); self.handle_scrolling(); self.search_results.clear(); @@ -360,11 +348,7 @@ impl AppState<'_> { self.workbook.recalculate_max_rows(); self.workbook.recalculate_max_cols(); - let sheet = self.workbook.get_current_sheet(); - - if self.selected_cell.0 > sheet.max_rows { - self.selected_cell.0 = sheet.max_rows.max(1); - } + self.clamp_selected_cell_to_excel_bounds(); self.handle_scrolling(); self.search_results.clear(); @@ -420,11 +404,7 @@ impl AppState<'_> { self.workbook.recalculate_max_rows(); self.workbook.recalculate_max_cols(); - let sheet = self.workbook.get_current_sheet(); - - if self.selected_cell.0 > sheet.max_rows { - self.selected_cell.0 = sheet.max_rows.max(1); - } + self.clamp_selected_cell_to_excel_bounds(); self.handle_scrolling(); self.search_results.clear(); @@ -477,21 +457,14 @@ impl AppState<'_> { self.workbook.recalculate_max_rows(); self.workbook.recalculate_max_cols(); - let sheet = self.workbook.get_current_sheet(); - - if col > sheet.max_cols { - self.selected_cell.1 = sheet.max_cols.max(1); - } - - if self.selected_cell.0 > sheet.max_rows { - self.selected_cell.0 = sheet.max_rows.max(1); - } + let max_cols = self.workbook.get_current_sheet().max_cols; + self.clamp_selected_cell_to_excel_bounds(); if self.column_widths.len() > col { self.column_widths.remove(col); } - self.adjust_column_widths(sheet.max_cols); + self.adjust_column_widths(max_cols); self.handle_scrolling(); self.search_results.clear(); @@ -544,21 +517,14 @@ impl AppState<'_> { self.workbook.recalculate_max_rows(); self.workbook.recalculate_max_cols(); - let sheet = self.workbook.get_current_sheet(); - - if self.selected_cell.1 > sheet.max_cols { - self.selected_cell.1 = sheet.max_cols.max(1); - } - - if self.selected_cell.0 > sheet.max_rows { - self.selected_cell.0 = sheet.max_rows.max(1); - } + let max_cols = self.workbook.get_current_sheet().max_cols; + self.clamp_selected_cell_to_excel_bounds(); if self.column_widths.len() > col { self.column_widths.remove(col); } - self.adjust_column_widths(sheet.max_cols); + self.adjust_column_widths(max_cols); self.handle_scrolling(); self.search_results.clear(); @@ -630,15 +596,8 @@ impl AppState<'_> { self.workbook.recalculate_max_rows(); self.workbook.recalculate_max_cols(); - let sheet = self.workbook.get_current_sheet(); - - if self.selected_cell.1 > sheet.max_cols { - self.selected_cell.1 = sheet.max_cols.max(1); - } - - if self.selected_cell.0 > sheet.max_rows { - self.selected_cell.0 = sheet.max_rows.max(1); - } + let max_cols = self.workbook.get_current_sheet().max_cols; + self.clamp_selected_cell_to_excel_bounds(); for col in (start_col..=effective_end_col).rev() { if self.column_widths.len() > col { @@ -646,7 +605,7 @@ impl AppState<'_> { } } - self.adjust_column_widths(sheet.max_cols); + self.adjust_column_widths(max_cols); self.handle_scrolling(); self.search_results.clear(); diff --git a/src/app/state.rs b/src/app/state.rs index 0467570..9f65f6c 100644 --- a/src/app/state.rs +++ b/src/app/state.rs @@ -5,7 +5,7 @@ use tui_textarea::TextArea; use crate::actions::UndoHistory; use crate::app::VimState; -use crate::excel::Workbook; +use crate::excel::{Workbook, EXCEL_MAX_COLS, EXCEL_MAX_ROWS}; /// Represents a cell position in a sheet, including both the selected cell and view position #[derive(Clone, Copy)] @@ -170,12 +170,28 @@ impl AppState<'_> { /// Updates the row number width based on the maximum row number in the current sheet pub fn update_row_number_width(&mut self) { - let max_rows = self.workbook.get_current_sheet().max_rows; + let max_rows = self + .workbook + .get_current_sheet() + .max_rows + .max(self.selected_cell.0) + .max(self.start_row) + .clamp(1, EXCEL_MAX_ROWS); let width = max_rows.to_string().len(); // Ensure a minimum width of 4 for row numbers self.row_number_width = width.max(4); } + pub fn clamp_cell_to_excel_bounds((row, col): (usize, usize)) -> (usize, usize) { + (row.clamp(1, EXCEL_MAX_ROWS), col.clamp(1, EXCEL_MAX_COLS)) + } + + pub fn clamp_selected_cell_to_excel_bounds(&mut self) { + self.selected_cell = Self::clamp_cell_to_excel_bounds(self.selected_cell); + self.start_row = self.start_row.clamp(1, EXCEL_MAX_ROWS); + self.start_col = self.start_col.clamp(1, EXCEL_MAX_COLS); + } + pub fn adjust_info_panel_height(&mut self, delta: isize) { let new_height = (self.info_panel_height as isize + delta).clamp(6, 16) as usize; if new_height != self.info_panel_height { diff --git a/src/app/undo_manager.rs b/src/app/undo_manager.rs index 5561182..122d258 100644 --- a/src/app/undo_manager.rs +++ b/src/app/undo_manager.rs @@ -16,14 +16,7 @@ impl AppState<'_> { self.workbook.recalculate_max_cols(); self.ensure_column_widths(); - // Update cursor position if it's outside the valid range - let sheet = self.workbook.get_current_sheet(); - if self.selected_cell.0 > sheet.max_rows { - self.selected_cell.0 = sheet.max_rows.max(1); - } - if self.selected_cell.1 > sheet.max_cols { - self.selected_cell.1 = sheet.max_cols.max(1); - } + self.clamp_selected_cell_to_excel_bounds(); if self.undo_history.all_undone() { self.workbook.set_modified(false); @@ -44,14 +37,7 @@ impl AppState<'_> { self.workbook.recalculate_max_cols(); self.ensure_column_widths(); - // Update cursor position if it's outside the valid range - let sheet = self.workbook.get_current_sheet(); - if self.selected_cell.0 > sheet.max_rows { - self.selected_cell.0 = sheet.max_rows.max(1); - } - if self.selected_cell.1 > sheet.max_cols { - self.selected_cell.1 = sheet.max_cols.max(1); - } + self.clamp_selected_cell_to_excel_bounds(); self.workbook.set_modified(true); } else { @@ -175,9 +161,7 @@ impl AppState<'_> { sheet.data.remove(row_action.row); sheet.max_rows = sheet.max_rows.saturating_sub(1); - if self.selected_cell.0 > sheet.max_rows { - self.selected_cell.0 = sheet.max_rows.max(1); - } + self.clamp_selected_cell_to_excel_bounds(); self.add_notification(format!("Redid row {} deletion", row_action.row)); } @@ -256,9 +240,7 @@ impl AppState<'_> { self.column_widths.push(15); } - if self.selected_cell.1 > sheet.max_cols { - self.selected_cell.1 = sheet.max_cols.max(1); - } + self.clamp_selected_cell_to_excel_bounds(); self.add_notification(format!("Redid column {} deletion", index_to_col_name(col))); } @@ -309,14 +291,10 @@ impl AppState<'_> { // Restore saved cell position for the new current sheet or use default if let Some(saved_position) = self.sheet_cell_positions.get(&new_sheet_name) { - // Ensure the saved position is valid for the current sheet - let sheet = self.workbook.get_current_sheet(); - let valid_row = saved_position.selected.0.min(sheet.max_rows.max(1)); - let valid_col = saved_position.selected.1.min(sheet.max_cols.max(1)); - - self.selected_cell = (valid_row, valid_col); + self.selected_cell = Self::clamp_cell_to_excel_bounds(saved_position.selected); self.start_row = saved_position.view.0; self.start_col = saved_position.view.1; + self.clamp_selected_cell_to_excel_bounds(); // Make sure the view position is valid relative to the selected cell self.handle_scrolling(); @@ -446,11 +424,7 @@ impl AppState<'_> { } else { self.workbook.delete_rows(start_row, end_row)?; - let sheet = self.workbook.get_current_sheet(); - - if self.selected_cell.0 > sheet.max_rows { - self.selected_cell.0 = sheet.max_rows.max(1); - } + self.clamp_selected_cell_to_excel_bounds(); self.add_notification(format!("Redid rows {} to {} deletion", start_row, end_row)); } @@ -519,12 +493,9 @@ impl AppState<'_> { } else { self.workbook.delete_columns(start_col, end_col)?; - let sheet = self.workbook.get_current_sheet(); Self::remove_column_widths(&mut self.column_widths, start_col, end_col); - if self.selected_cell.1 > sheet.max_cols { - self.selected_cell.1 = sheet.max_cols.max(1); - } + self.clamp_selected_cell_to_excel_bounds(); self.add_notification(format!( "Redid columns {} to {} deletion", diff --git a/src/commands/executor.rs b/src/commands/executor.rs index 58fc44b..485542b 100644 --- a/src/commands/executor.rs +++ b/src/commands/executor.rs @@ -1,8 +1,9 @@ use std::path::Path; use crate::app::AppState; +use crate::excel::{EXCEL_MAX_COLS, EXCEL_MAX_ROWS}; use crate::json_export::{export_all_sheets_json, export_json, HeaderDirection}; -use crate::utils::{cell_reference, col_name_to_index, parse_cell_reference}; +use crate::utils::{cell_reference, col_name_to_index, index_to_col_name, parse_cell_reference}; impl AppState<'_> { pub fn execute_command(&mut self) { @@ -365,14 +366,10 @@ impl AppState<'_> { fn jump_to_cell(&mut self, cell_ref: (usize, usize)) { let (row, col) = cell_ref; // Fixed: cell_ref is already (row, col) - let sheet = self.workbook.get_current_sheet(); - - // Validate row and column - if row > sheet.max_rows || col > sheet.max_cols { + if row > EXCEL_MAX_ROWS || col > EXCEL_MAX_COLS { self.add_notification(format!( - "Cell reference out of range: {}{}", - crate::utils::index_to_col_name(col), - row + "Cell reference out of range: {}", + cell_reference(cell_ref) )); return; } @@ -380,11 +377,7 @@ impl AppState<'_> { self.selected_cell = (row, col); self.handle_scrolling(); - self.add_notification(format!( - "Jumped to cell {}{}", - crate::utils::index_to_col_name(col), - row - )); + self.add_notification(format!("Jumped to cell {}{}", index_to_col_name(col), row)); } } @@ -392,7 +385,7 @@ impl AppState<'_> { mod tests { use super::parse_cell_reference; use crate::app::AppState; - use crate::excel::{Cell, FreezePanes, Sheet, Workbook}; + use crate::excel::{Cell, FreezePanes, Sheet, Workbook, EXCEL_MAX_COLS, EXCEL_MAX_ROWS}; use std::path::PathBuf; fn app_with_sheet() -> AppState<'static> { @@ -429,6 +422,46 @@ mod tests { assert_eq!(parse_cell_reference("测试1"), None); } + #[test] + fn cell_reference_command_can_jump_to_blank_cell_beyond_used_range() { + let mut app = app_with_sheet(); + app.input_buffer = "A3".to_string(); + + app.execute_command(); + + assert_eq!(app.selected_cell, (3, 1)); + assert_eq!(app.get_cell_content(3, 1), ""); + assert_eq!( + app.notification_messages.last().map(String::as_str), + Some("Jumped to cell A3") + ); + } + + #[test] + fn cell_reference_command_can_jump_to_excel_bottom_right_cell() { + let mut app = app_with_sheet(); + app.input_buffer = "XFD1048576".to_string(); + + app.execute_command(); + + assert_eq!(app.selected_cell, (EXCEL_MAX_ROWS, EXCEL_MAX_COLS)); + assert_eq!(app.get_cell_content(EXCEL_MAX_ROWS, EXCEL_MAX_COLS), ""); + } + + #[test] + fn cell_reference_command_rejects_cells_beyond_excel_bounds() { + let mut app = app_with_sheet(); + app.input_buffer = "XFE1048577".to_string(); + + app.execute_command(); + + assert_eq!(app.selected_cell, (1, 1)); + assert_eq!( + app.notification_messages.last().map(String::as_str), + Some("Cell reference out of range: XFE1048577") + ); + } + #[test] fn freeze_command_uses_current_cell_and_marks_workbook_modified() { let mut app = app_with_sheet(); diff --git a/src/excel/sheet.rs b/src/excel/sheet.rs index efe050b..549e020 100644 --- a/src/excel/sheet.rs +++ b/src/excel/sheet.rs @@ -1,6 +1,9 @@ use crate::excel::Cell; use crate::utils::cell_reference; +pub const EXCEL_MAX_COLS: usize = 16_384; +pub const EXCEL_MAX_ROWS: usize = 1_048_576; + #[derive(Clone)] pub struct FreezePanes { pub rows: usize, diff --git a/src/ui/handlers.rs b/src/ui/handlers.rs index 4a56686..d4b8b84 100644 --- a/src/ui/handlers.rs +++ b/src/ui/handlers.rs @@ -399,7 +399,8 @@ mod tests { use super::handle_key_event; use crate::app::{AppState, InputMode}; - use crate::excel::{Cell, FreezePanes, Sheet, Workbook}; + use crate::excel::{Cell, FreezePanes, Sheet, Workbook, EXCEL_MAX_COLS, EXCEL_MAX_ROWS}; + use crate::utils::index_to_col_name; fn app_with_sheet() -> AppState<'static> { let mut data = vec![vec![Cell::empty(); 3]; 3]; @@ -447,6 +448,61 @@ mod tests { assert!(matches!(app.input_mode, InputMode::Normal)); } + #[test] + fn right_movement_can_enter_blank_columns_beyond_used_range() { + let mut app = app_with_sheet(); + app.selected_cell = (2, 2); + + handle_key_event( + &mut app, + KeyEvent::new(KeyCode::Char('l'), KeyModifiers::empty()), + ); + + assert_eq!(app.selected_cell, (2, 3)); + assert_eq!(app.get_cell_content(2, 3), ""); + } + + #[test] + fn down_movement_can_enter_blank_rows_beyond_used_range() { + let mut app = app_with_sheet(); + app.selected_cell = (2, 2); + + handle_key_event( + &mut app, + KeyEvent::new(KeyCode::Char('j'), KeyModifiers::empty()), + ); + + assert_eq!(app.selected_cell, (3, 2)); + assert_eq!(app.get_cell_content(3, 2), ""); + } + + #[test] + fn down_movement_stops_at_excel_row_limit() { + let mut app = app_with_sheet(); + app.selected_cell = (EXCEL_MAX_ROWS, 2); + + handle_key_event( + &mut app, + KeyEvent::new(KeyCode::Down, KeyModifiers::empty()), + ); + + assert_eq!(app.selected_cell, (EXCEL_MAX_ROWS, 2)); + } + + #[test] + fn right_movement_stops_at_excel_column_limit() { + let mut app = app_with_sheet(); + app.selected_cell = (2, EXCEL_MAX_COLS); + + handle_key_event( + &mut app, + KeyEvent::new(KeyCode::Char('l'), KeyModifiers::empty()), + ); + + assert_eq!(app.selected_cell, (2, EXCEL_MAX_COLS)); + assert_eq!(index_to_col_name(app.selected_cell.1), "XFD"); + } + #[test] fn q_closes_help_overlay_without_quitting() { let mut app = app_with_sheet(); diff --git a/src/ui/render/spreadsheet.rs b/src/ui/render/spreadsheet.rs index 7c6241b..43c5bdc 100644 --- a/src/ui/render/spreadsheet.rs +++ b/src/ui/render/spreadsheet.rs @@ -7,6 +7,7 @@ use ratatui::{ }; use crate::app::{AppState, InputMode}; +use crate::excel::{EXCEL_MAX_COLS, EXCEL_MAX_ROWS}; use crate::ui::theme; use crate::utils::index_to_col_name; @@ -76,7 +77,7 @@ fn visible_data_columns(app_state: &AppState, available_width: usize) -> Vec<(us let sheet = app_state.workbook.get_current_sheet(); let frozen_cols = sheet.freeze_panes.cols.min(sheet.max_cols); let scroll_start = app_state.start_col.max(frozen_cols + 1); - let max_col = sheet.max_cols.max(scroll_start); + let max_col = EXCEL_MAX_COLS; let has_scroll_cols = scroll_start <= max_col; let frozen_available_width = if has_scroll_cols && available_width > 1 { available_width - 1 @@ -151,9 +152,9 @@ fn push_visible_column( fn visible_data_rows(app_state: &AppState) -> Vec { let sheet = app_state.workbook.get_current_sheet(); - let max_row = sheet.max_rows.max(app_state.start_row); + let max_row = EXCEL_MAX_ROWS; let frozen_rows = sheet.freeze_panes.rows.min(sheet.max_rows); - let scroll_start = app_state.start_row.max(frozen_rows + 1); + let scroll_start = app_state.start_row.clamp(frozen_rows + 1, EXCEL_MAX_ROWS); let has_scroll_rows = scroll_start <= max_row; let available_rows = app_state.visible_rows; let frozen_rows_visible = if has_scroll_rows && available_rows > 1 { diff --git a/src/ui/render/tests.rs b/src/ui/render/tests.rs index c612cb2..2df8bb1 100644 --- a/src/ui/render/tests.rs +++ b/src/ui/render/tests.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use super::{theme, ui}; use crate::app::{AppState, HelpEntry, InputMode}; -use crate::excel::{Cell, FreezePanes, Sheet, Workbook}; +use crate::excel::{Cell, FreezePanes, Sheet, Workbook, EXCEL_MAX_ROWS}; fn app_with_sheet() -> AppState<'static> { let mut data = vec![vec![Cell::empty(); 3]; 3]; @@ -724,3 +724,78 @@ fn renders_rows_cols_in_top_right_with_overflow_hint_when_tabs_exceed_space() { assert!(title_row.contains("Alpha")); assert!(!title_row.contains("Zeta")); } + +#[test] +fn renders_blank_columns_beyond_used_range_to_fill_viewport() { + let backend = TestBackend::new(100, 32); + let mut terminal = Terminal::new(backend).unwrap(); + let mut app = app_with_sheet(); + app.selected_cell = (2, 3); + app.start_col = 3; + + terminal.draw(|frame| ui(frame, &mut app)).unwrap(); + + let lines = rendered_lines(&terminal); + let rendered = lines.join("\n"); + let header_row = lines + .iter() + .find(|line| line.contains("C") && line.contains("D") && line.contains("E")) + .unwrap_or_else(|| panic!("expected blank column headers C, D, and E:\n{rendered}")); + + assert!(rendered.contains("Cell C2 Blank Len 0"), "{rendered}"); + assert!(header_row.contains("C"), "{header_row}"); + assert!(header_row.contains("D"), "{header_row}"); + assert!(header_row.contains("E"), "{header_row}"); + assert!(rendered.contains("Rows/Cols: 2 x 2"), "{rendered}"); +} + +#[test] +fn renders_blank_rows_beyond_used_range_to_fill_viewport() { + let backend = TestBackend::new(100, 32); + let mut terminal = Terminal::new(backend).unwrap(); + let mut app = app_with_sheet(); + app.selected_cell = (3, 1); + app.start_row = 3; + + terminal.draw(|frame| ui(frame, &mut app)).unwrap(); + + let lines = rendered_lines(&terminal); + let rendered = lines.join("\n"); + assert!(rendered.contains("Cell A3 Blank Len 0"), "{rendered}"); + assert!(rendered.contains("3"), "{rendered}"); + assert!(rendered.contains("4"), "{rendered}"); + assert!(rendered.contains("5"), "{rendered}"); + + let row_three = lines + .iter() + .position(|line| line.contains("3") && !line.contains("Cell A3")) + .unwrap_or_else(|| panic!("expected row 3 to render:\n{rendered}")); + let buffer = terminal.backend().buffer(); + let width = buffer.area.width as usize; + let has_selected_style = (0..width).any(|col| { + let cell = &buffer.content[row_three * width + col]; + cell.bg == Color::White && cell.fg == Color::Black + }); + assert!( + has_selected_style, + "expected selected blank cell highlight:\n{rendered}" + ); +} + +#[test] +fn renders_excel_max_row_number_when_selected_at_bottom_boundary() { + let backend = TestBackend::new(100, 32); + let mut terminal = Terminal::new(backend).unwrap(); + let mut app = app_with_sheet(); + app.selected_cell = (EXCEL_MAX_ROWS, 1); + app.start_row = EXCEL_MAX_ROWS; + + terminal.draw(|frame| ui(frame, &mut app)).unwrap(); + + let rendered = rendered_lines(&terminal).join("\n"); + assert!( + rendered.contains("Cell A1048576 Blank Len 0"), + "{rendered}" + ); + assert!(rendered.contains("1048576"), "{rendered}"); +}