/*
* Ricochet - A minimalist CLI web browser for fast, distraction-free navigation.
* Copyright (C) 2026 Richard Qin
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "ricochet/render/table_renderer.hh"
#include "ricochet/render/internal.hh"
#include "ricochet/render/utils.hh"
#include
namespace ricochet::render {
namespace {
std::size_t get_node_text_len( const parser::DomNode& node ) // NOLINT(misc-no-recursion)
{
std::size_t len = get_visible_length( decode_entities( node.text_content ) );
for ( const auto& child : node.children ) {
len += get_node_text_len( child );
}
return len;
}
void process_table_row_widths( const parser::DomNode& row, std::vector& widths )
{
std::size_t col_idx = 0;
for ( const auto& cell : row.children ) {
const std::size_t space_pos = cell.tag_name.find( ' ' );
const std::string tag
= ( space_pos == std::string::npos ) ? cell.tag_name : cell.tag_name.substr( 0, space_pos );
if ( tag == "td" || tag == "th" ) {
const std::size_t len = get_node_text_len( cell ) + 2;
if ( col_idx >= widths.size() ) {
widths.push_back( len );
} else if ( len > widths[col_idx] ) {
widths[col_idx] = len;
}
col_idx++;
}
}
}
std::vector calc_table_widths( const parser::DomNode& table_node )
{
std::vector widths;
for ( const auto& child : table_node.children ) {
const std::size_t space_pos = child.tag_name.find( ' ' );
const std::string tag
= ( space_pos == std::string::npos ) ? child.tag_name : child.tag_name.substr( 0, space_pos );
if ( tag == "tr" ) {
process_table_row_widths( child, widths );
} else if ( tag == "tbody" || tag == "thead" ) {
for ( const auto& row : child.children ) {
if ( row.tag_name.starts_with( "tr" ) ) {
process_table_row_widths( row, widths );
}
}
}
}
for ( auto& w : widths ) {
w = std::min( w, 30 );
}
return widths;
}
void render_table_row( const parser::DomNode& row,
RenderResult& result,
const FormContext& ctx,
const std::vector& widths )
{
std::string& output = result.text;
output += "\033[90m|\033[0m ";
std::size_t col_idx = 0;
for ( const auto& cell : row.children ) {
const std::size_t cell_space = cell.tag_name.find( ' ' );
const std::string c_tag
= ( cell_space == std::string::npos ) ? cell.tag_name : cell.tag_name.substr( 0, cell_space );
if ( c_tag != "td" && c_tag != "th" ) {
continue;
}
const std::size_t text_start = output.size();
if ( c_tag == "th" ) {
output += "\033[1;36m";
}
for ( const auto& cell_child : cell.children ) {
render_node( cell_child, result, ctx );
}
if ( c_tag == "th" ) {
output += "\033[0m";
}
const std::string added = output.substr( text_start );
const std::size_t vis_len = get_visible_length( added );
const std::size_t tw = ( col_idx < widths.size() ) ? widths[col_idx] : 10;
if ( vis_len < tw ) {
output.append( tw - vis_len, ' ' );
}
output += " \033[90m|\033[0m ";
col_idx++;
}
output += "\n";
}
} // namespace
void render_table( const parser::DomNode& node, RenderResult& result, const FormContext& ctx )
{
std::string& output = result.text;
const std::vector widths = calc_table_widths( node );
output += "\n\033[90m+" + std::string( 78, '-' ) + "+\033[0m\n";
for ( const auto& child : node.children ) {
const std::size_t child_space = child.tag_name.find( ' ' );
const std::string child_tag
= ( child_space == std::string::npos ) ? child.tag_name : child.tag_name.substr( 0, child_space );
if ( child_tag == "tr" ) {
render_table_row( child, result, ctx, widths );
} else if ( child_tag == "tbody" || child_tag == "thead" ) {
for ( const auto& row : child.children ) {
if ( row.tag_name.starts_with( "tr" ) ) {
render_table_row( row, result, ctx, widths );
}
}
}
}
output += "\033[90m+" + std::string( 78, '-' ) + "+\033[0m\n";
}
} // namespace ricochet::render