diff options
| author | Richard Qin <richard.qin.x@gmail.com> | 2026-10-16 22:35:09 +0800 |
|---|---|---|
| committer | Richard Qin <richard.qin.x@gmail.com> | 2026-10-16 22:35:09 +0800 |
| commit | 31a6c85731b439dbeb64eacf8f4fc461c0a6264c (patch) | |
| tree | 34f604cfbe8a2807c1b4adf5323b926102d6a9ab | |
| parent | 1621b3dbfaccb4e2960adba4daf81c967e5cdf9e (diff) | |
| download | rwebbrowser-31a6c85731b439dbeb64eacf8f4fc461c0a6264c.tar.gz rwebbrowser-31a6c85731b439dbeb64eacf8f4fc461c0a6264c.tar.xz | |
Remove legacy terminal UI infrastructure and rename license file
| -rw-r--r-- | COPYING (renamed from LICENSE) | 0 | ||||
| -rw-r--r-- | include/ricochet/core/browser.hh | 5 | ||||
| -rw-r--r-- | include/ricochet/core/form_handler.hh | 11 | ||||
| -rw-r--r-- | include/ricochet/core/link_hints.hh | 27 | ||||
| -rw-r--r-- | include/ricochet/core/search_handler.hh | 23 | ||||
| -rw-r--r-- | include/ricochet/core/types.hh | 4 | ||||
| -rw-r--r-- | include/ricochet/core/ui_input.hh | 29 | ||||
| -rw-r--r-- | include/ricochet/core/ui_view.hh | 37 | ||||
| -rw-r--r-- | include/ricochet/core/utils.hh | 2 | ||||
| -rw-r--r-- | include/ricochet/tui/terminal.hh | 56 | ||||
| -rw-r--r-- | src/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/core/browser.cc | 154 | ||||
| -rw-r--r-- | src/core/form_handler.cc | 101 | ||||
| -rw-r--r-- | src/core/link_hints.cc | 104 | ||||
| -rw-r--r-- | src/core/search_handler.cc | 81 | ||||
| -rw-r--r-- | src/core/ui_input.cc | 94 | ||||
| -rw-r--r-- | src/core/ui_view.cc | 103 | ||||
| -rw-r--r-- | src/core/utils.cc | 33 | ||||
| -rw-r--r-- | src/tui/terminal.cc | 90 |
19 files changed, 2 insertions, 953 deletions
diff --git a/include/ricochet/core/browser.hh b/include/ricochet/core/browser.hh index ab7a473..fabcba4 100644 --- a/include/ricochet/core/browser.hh +++ b/include/ricochet/core/browser.hh @@ -18,7 +18,7 @@ #pragma once -#include <string_view> +#include <string> namespace ricochet::core { @@ -35,7 +35,6 @@ public: static std::string format_url( std::string input ); static std::string get_configured_homepage(); - static int run( std::string_view initial_url ); }; -} // namespace ricochet::core
\ No newline at end of file +} // namespace ricochet::core diff --git a/include/ricochet/core/form_handler.hh b/include/ricochet/core/form_handler.hh index 5abf148..d95bd02 100644 --- a/include/ricochet/core/form_handler.hh +++ b/include/ricochet/core/form_handler.hh @@ -12,7 +12,6 @@ #include "ricochet/core/types.hh" #include "ricochet/render/renderer.hh" -#include "ricochet/tui/terminal.hh" #include <cstddef> #include <string> #include <vector> @@ -25,14 +24,4 @@ InputAction submit_form( const render::InputField& target_input, std::vector<HttpRequest>& history, std::size_t& history_idx ); -void update_input_value( PageData& page_data, std::size_t index, const std::string& query ); -void update_single_line_ui( std::string& line, const std::string& marker, const render::InputField& in ); -void update_toggle_ui( PageData& page_data ); - -InputAction handle_form_input( const tui::Terminal& terminal, - PageData& page_data, - const std::string& current_url, - std::vector<HttpRequest>& history, - std::size_t& history_idx ); - } // namespace ricochet::core diff --git a/include/ricochet/core/link_hints.hh b/include/ricochet/core/link_hints.hh deleted file mode 100644 index 5da4846..0000000 --- a/include/ricochet/core/link_hints.hh +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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. - */ - -#pragma once - -#include "ricochet/core/types.hh" -#include "ricochet/tui/terminal.hh" -#include <string> - -namespace ricochet::core { - -std::string index_to_hint( std::size_t index ); -void toggle_link_hints( PageData& page_data, bool show ); -void handle_follow_link( const tui::Terminal& terminal, - PageData& page_data, - std::string& current_url, - std::size_t& scroll_y, - bool& navigate ); - -} // namespace ricochet::core diff --git a/include/ricochet/core/search_handler.hh b/include/ricochet/core/search_handler.hh deleted file mode 100644 index 7053be8..0000000 --- a/include/ricochet/core/search_handler.hh +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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. - */ - -#pragma once - -#include "ricochet/core/types.hh" -#include <cstddef> -#include <string> - -namespace ricochet::core { - -void find_search_matches( PageData& page_data, const std::string& query ); -void apply_search_highlight( PageData& page_data, const std::string& query ); -void execute_search( PageData& page_data, const std::string& query, std::size_t& scroll_y, bool next_match ); - -} // namespace ricochet::core diff --git a/include/ricochet/core/types.hh b/include/ricochet/core/types.hh index bd7e819..ef48016 100644 --- a/include/ricochet/core/types.hh +++ b/include/ricochet/core/types.hh @@ -31,10 +31,6 @@ struct PageData std::vector<std::string> links; std::vector<render::InputField> inputs; std::string title; - std::vector<std::string> original_lines {}; - std::string last_search {}; - std::size_t current_match_idx { 0 }; - std::vector<std::size_t> match_lines {}; }; struct HttpRequest diff --git a/include/ricochet/core/ui_input.hh b/include/ricochet/core/ui_input.hh deleted file mode 100644 index bf71685..0000000 --- a/include/ricochet/core/ui_input.hh +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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 <https://www.gnu.org/licenses/>. - */ - -#pragma once - -#include "ricochet/tui/terminal.hh" -#include <string> - -namespace ricochet::core { - -std::string get_url_input( const tui::Terminal& terminal ); -std::string get_terminal_input( const tui::Terminal& terminal, const std::string& prompt ); - -} // namespace ricochet::core diff --git a/include/ricochet/core/ui_view.hh b/include/ricochet/core/ui_view.hh deleted file mode 100644 index d9a55fa..0000000 --- a/include/ricochet/core/ui_view.hh +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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 <https://www.gnu.org/licenses/>. - */ - -#pragma once - -#include "ricochet/tui/terminal.hh" - -#include <string> -#include <string_view> -#include <vector> - -namespace ricochet::core { - -std::string build_footer( std::string_view url, std::size_t width ); - -void draw_view( const tui::Terminal& terminal, - const std::vector<std::string>& lines, - std::size_t scroll_y, - std::string_view url, - std::string_view title ); - -} // namespace ricochet::core diff --git a/include/ricochet/core/utils.hh b/include/ricochet/core/utils.hh index d9f58dd..61d480b 100644 --- a/include/ricochet/core/utils.hh +++ b/include/ricochet/core/utils.hh @@ -18,7 +18,6 @@ #pragma once -#include <cstddef> #include <string> #include <vector> @@ -27,6 +26,5 @@ namespace ricochet::core { std::vector<std::string> split_into_lines( const std::string& text ); std::string normalize_url( const std::string& target, const std::string& current_url ); std::string strip_ddg_tracker( std::string target ); -std::vector<std::string> wrap_lines( const std::vector<std::string>& original_lines, std::size_t wrap_width ); } // namespace ricochet::core diff --git a/include/ricochet/tui/terminal.hh b/include/ricochet/tui/terminal.hh deleted file mode 100644 index 2942a2a..0000000 --- a/include/ricochet/tui/terminal.hh +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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 <https://www.gnu.org/licenses/>. - */ - -#pragma once - -#include <termios.h> -#include <utility> - -namespace ricochet::tui { - -class Terminal -{ -public: - Terminal(); - ~Terminal(); - - Terminal( const Terminal& ) = delete; - Terminal& operator=( const Terminal& ) = delete; - Terminal( Terminal&& ) = delete; - Terminal& operator=( Terminal&& ) = delete; - - // Read a single key - char read_key() const; - - // Clear screen and move cursor to top-left - void clear_screen() const; - - // Get terminal size - std::pair<std::size_t, std::size_t> get_size() const; - - // Move cursor to a specific position - void move_cursor( std::size_t row, std::size_t col ) const; - - // Show or hide cursor - void show_cursor( bool visible ) const; - -private: - struct termios original_termios_ {}; // Save terminal original state -}; - -} // namespace ricochet::tui
\ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dcc8f49..e46ac9a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -17,4 +17,3 @@ target_link_libraries(ricochet_core add_executable(ricochet ${MAIN_SRC}) target_link_libraries(ricochet PRIVATE ricochet_core) -# target_link_libraries(ricochet PRIVATE ricochet_core curl ncurses)
\ No newline at end of file diff --git a/src/core/browser.cc b/src/core/browser.cc index 86a5096..c413a0d 100644 --- a/src/core/browser.cc +++ b/src/core/browser.cc @@ -18,117 +18,12 @@ #include "ricochet/core/browser.hh" -#include <cstddef> -#include <cstdlib> #include <fstream> -#include <iostream> -#include <sys/stat.h> -#include "ricochet/core/form_handler.hh" -#include "ricochet/core/link_hints.hh" #include "ricochet/core/local_data.hh" -#include "ricochet/core/page_loader.hh" -#include "ricochet/core/search_handler.hh" -#include "ricochet/core/ui_input.hh" -#include "ricochet/core/ui_view.hh" -#include "ricochet/core/utils.hh" namespace ricochet::core { -namespace { - -InputAction push_history( const std::string& url, std::vector<HttpRequest>& history, std::size_t& history_idx ) -{ - history.resize( history_idx + 1 ); - history.push_back( HttpRequest { .url = url, .method = "GET", .body = "" } ); - history_idx++; - return InputAction::Navigate; -} - -InputAction process_key( char c, - const tui::Terminal& terminal, - PageData& page_data, - std::string& current_url, - std::vector<HttpRequest>& history, - std::size_t& history_idx, // NOLINT(bugprone-easily-swappable-parameters) - std::size_t& scroll_y ) // NOLINT(bugprone-easily-swappable-parameters) -{ - auto [width, height] = terminal.get_size(); - const std::size_t view_h = ( height > 1 ) ? ( height - 1 ) : 1; - - switch ( c ) { - case 'q': - return InputAction::Quit; - case 'j': - if ( scroll_y + view_h < page_data.lines.size() ) { - scroll_y++; - } - break; - case 'k': - if ( scroll_y > 0 ) { - scroll_y--; - } - break; - case 'H': - if ( history_idx > 0 ) { - history_idx--; - return InputAction::Navigate; - } - break; - case 'L': - if ( history_idx + 1 < history.size() ) { - history_idx++; - return InputAction::Navigate; - } - break; - case 'r': - return InputAction::Navigate; - case 'b': - save_bookmark( page_data.title, current_url ); - break; - case 'B': - return push_history( "ricochet://bookmarks", history, history_idx ); - case '?': - return push_history( "ricochet://help", history, history_idx ); - case 'h': - return push_history( "ricochet://history", history, history_idx ); - case 'n': { - const std::string query = get_terminal_input( terminal, "Find in Page:" ); - execute_search( page_data, query, scroll_y, false ); - break; - } - case 'N': - execute_search( page_data, page_data.last_search, scroll_y, true ); - break; - case '/': { - const std::string next_url = get_url_input( terminal ); - if ( !next_url.empty() ) { - return push_history( next_url, history, history_idx ); - } - break; - } - case 'f': { - const std::string old_url = current_url; - bool nav = false; - handle_follow_link( terminal, page_data, current_url, scroll_y, nav ); - if ( nav && current_url != old_url ) { - return push_history( current_url, history, history_idx ); - } - break; - } - case 'i': - if ( !page_data.inputs.empty() ) { - return handle_form_input( terminal, page_data, current_url, history, history_idx ); - } - break; - default: - break; - } - return InputAction::None; -} - -} // namespace - std::string Browser::format_url( std::string input ) { if ( input.empty() ) { @@ -168,53 +63,4 @@ std::string Browser::get_configured_homepage() return "https://wikipedia.org"; } -int Browser::run( std::string_view initial_url ) -{ - std::vector<HttpRequest> history; - history.emplace_back( HttpRequest { .url = std::string( initial_url ), .method = "GET", .body = "" } ); - std::size_t history_idx = 0; - const tui::Terminal terminal; - terminal.show_cursor( false ); - - while ( true ) { - const HttpRequest current_req = history[history_idx]; - std::string current_url = current_req.url; - terminal.clear_screen(); - std::cout << "-> Fetching: " << current_req.url << "...\r\n"; - std::cout.flush(); - - auto page_data = load_page( current_req ); - - static std::string last_recorded_url; - if ( current_url != last_recorded_url && !current_url.starts_with( "ricochet://" ) ) { - append_to_history( current_url, page_data.title ); - last_recorded_url = current_url; - } - - auto [term_width, term_height] = terminal.get_size(); - const std::size_t wrap_width = ( term_width > 1 ) ? ( term_width - 1 ) : 80; - page_data.lines = wrap_lines( page_data.lines, wrap_width ); - page_data.original_lines = page_data.lines; - - std::size_t scroll_y = 0; - bool navigate = false; - - while ( !navigate ) { - draw_view( terminal, page_data.lines, scroll_y, current_url, page_data.title ); - const char c = terminal.read_key(); - - const InputAction action = process_key( c, terminal, page_data, current_url, history, history_idx, scroll_y ); - if ( action == InputAction::Quit ) { - terminal.clear_screen(); - return 0; - } - if ( action == InputAction::Navigate ) { - navigate = true; - } - } - } - terminal.clear_screen(); - return 0; -} - } // namespace ricochet::core diff --git a/src/core/form_handler.cc b/src/core/form_handler.cc index 2d018c9..6100a30 100644 --- a/src/core/form_handler.cc +++ b/src/core/form_handler.cc @@ -17,7 +17,6 @@ */ #include "ricochet/core/form_handler.hh" -#include "ricochet/core/ui_input.hh" #include "ricochet/core/utils.hh" namespace ricochet::core { @@ -62,104 +61,4 @@ InputAction submit_form( const render::InputField& target_input, return InputAction::Navigate; } -void update_input_value( PageData& page_data, std::size_t index, const std::string& query ) -{ - page_data.inputs[index - 1].value = query; - const std::string marker = "[I" + std::to_string( index ) + ":"; - const std::string replacement = "[I" + std::to_string( index ) + ":" + query + "]"; - - auto update_lines = [&]( std::vector<std::string>& lines_arr ) { - for ( auto& line : lines_arr ) { - const std::size_t pos = line.find( marker ); - if ( pos != std::string::npos ) { - const std::size_t end = line.find( ']', pos ); - if ( end != std::string::npos ) { - line.replace( pos, end - pos + 1, replacement ); - } - } - } - }; - update_lines( page_data.lines ); - update_lines( page_data.original_lines ); -} - -void update_single_line_ui( std::string& line, const std::string& marker, const render::InputField& in ) -{ - const std::string checked_str = marker + ( in.type == "checkbox" ? "[X] " : "(X) " ) + in.value + "]"; - const std::string unchecked_str = marker + ( in.type == "checkbox" ? "[ ] " : "( ) " ) + in.value + "]"; - - const std::string& target_str = in.checked ? checked_str : unchecked_str; - const std::string& old_str = in.checked ? unchecked_str : checked_str; - - const std::size_t pos = line.find( old_str ); - if ( pos != std::string::npos ) { - line.replace( pos, old_str.length(), target_str ); - } -} - -void update_toggle_ui( PageData& page_data ) -{ - for ( std::size_t i = 0; i < page_data.inputs.size(); ++i ) { - const auto& in = page_data.inputs[i]; - if ( in.type != "checkbox" && in.type != "radio" ) { - continue; - } - const std::string marker = "[I" + std::to_string( i + 1 ) + ":"; - for ( auto& line : page_data.lines ) { - update_single_line_ui( line, marker, in ); - } - for ( auto& line : page_data.original_lines ) { - update_single_line_ui( line, marker, in ); - } - } -} - -InputAction handle_form_input( const tui::Terminal& terminal, - PageData& page_data, - const std::string& current_url, - std::vector<HttpRequest>& history, - std::size_t& history_idx ) -{ - const std::string num_input = get_terminal_input( terminal, "Select Input [#]:" ); - if ( num_input.empty() ) { - return InputAction::None; - } - std::size_t index = 0; - try { - index = std::stoul( num_input ); - } catch ( ... ) { - return InputAction::None; - } - if ( index == 0 || index > page_data.inputs.size() ) { - return InputAction::None; - } - - auto& target_input = page_data.inputs[index - 1]; - if ( target_input.type == "submit" || target_input.type == "button" ) { - return submit_form( target_input, page_data.inputs, current_url, history, history_idx ); - } - - if ( target_input.type == "checkbox" || target_input.type == "radio" ) { - if ( target_input.type == "radio" ) { - for ( auto& in : page_data.inputs ) { - if ( in.name == target_input.name && in.action == target_input.action ) { - in.checked = false; - } - } - target_input.checked = true; - } else { - target_input.checked = !target_input.checked; - } - update_toggle_ui( page_data ); - return InputAction::None; - } - - const std::string query = get_terminal_input( terminal, "Enter Text [I" + std::to_string( index ) + "]:" ); - if ( query.empty() ) { - return InputAction::None; - } - update_input_value( page_data, index, query ); - return InputAction::None; -} - } // namespace ricochet::core diff --git a/src/core/link_hints.cc b/src/core/link_hints.cc deleted file mode 100644 index 77b91b8..0000000 --- a/src/core/link_hints.cc +++ /dev/null @@ -1,104 +0,0 @@ -/* - * 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 <https://www.gnu.org/licenses/>. - */ - -#include "ricochet/core/link_hints.hh" -#include "ricochet/core/ui_view.hh" -#include "ricochet/core/utils.hh" -#include <iostream> - -namespace ricochet::core { - -std::string index_to_hint( std::size_t index ) -{ - const std::string alphabet = "asdfghjklqwertyuiopzxcvbnm"; - std::string hint; - hint += alphabet[( index / alphabet.length() ) % alphabet.length()]; - hint += alphabet[index % alphabet.length()]; - return hint; -} - -void toggle_link_hints( PageData& page_data, bool show ) -{ - const std::string hl_start = "\033[0;1;35;7m "; - const std::string hl_end = " \033[0m"; - - std::vector<std::string> formatted_hints; - formatted_hints.reserve( page_data.links.size() ); - for ( std::size_t i = 0; i < page_data.links.size(); ++i ) { - std::string h = hl_start; - h += index_to_hint( i ); - h += hl_end; - formatted_hints.push_back( std::move( h ) ); - } - - for ( auto& line : page_data.lines ) { - for ( std::size_t i = 0; i < page_data.links.size(); ++i ) { - const std::string marker = "{L:" + std::to_string( i + 1 ) + "}"; - if ( show ) { - const std::size_t pos = line.find( marker ); - if ( pos != std::string::npos ) { - line.replace( pos, marker.length(), formatted_hints[i] ); - } - } else { - const std::size_t pos = line.find( formatted_hints[i] ); - if ( pos != std::string::npos ) { - line.replace( pos, formatted_hints[i].length(), marker ); - } - } - } - } -} - -void handle_follow_link( const tui::Terminal& terminal, - PageData& page_data, - std::string& current_url, - std::size_t& scroll_y, // NOLINT(bugprone-easily-swappable-parameters) - bool& navigate ) -{ - toggle_link_hints( page_data, true ); - - draw_view( terminal, page_data.lines, scroll_y, current_url, page_data.title ); - - auto [w, h] = terminal.get_size(); - terminal.move_cursor( h, 1 ); - std::cout << "\x1b[2K\033[1;35;7m Follow Hint (2 keys): \033[0m "; - std::cout.flush(); - - std::string input; - input += terminal.read_key(); - std::cout << input[0]; - std::cout.flush(); - input += terminal.read_key(); - - // bool found = false; - for ( std::size_t i = 0; i < page_data.links.size(); ++i ) { - if ( index_to_hint( i ) == input ) { - std::string target = page_data.links[i]; - target = normalize_url( target, current_url ); - target = strip_ddg_tracker( target ); - current_url = target; - navigate = true; - // found = true; - break; - } - } - - toggle_link_hints( page_data, false ); -} - -} // namespace ricochet::core diff --git a/src/core/search_handler.cc b/src/core/search_handler.cc deleted file mode 100644 index cab9e9c..0000000 --- a/src/core/search_handler.cc +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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 <https://www.gnu.org/licenses/>. - */ - -#include "ricochet/core/search_handler.hh" - -namespace ricochet::core { - -void find_search_matches( PageData& page_data, const std::string& query ) -{ - page_data.last_search = query; - page_data.match_lines.clear(); - page_data.current_match_idx = 0; - for ( std::size_t i = 0; i < page_data.original_lines.size(); ++i ) { - if ( page_data.original_lines[i].find( query ) != std::string::npos ) { - page_data.match_lines.push_back( i ); - } - } -} - -void apply_search_highlight( PageData& page_data, const std::string& query ) -{ - page_data.lines = page_data.original_lines; - if ( page_data.match_lines.empty() ) { - return; - } - - const std::string hl_start = "\033[43;30m"; - const std::string hl_end = "\033[0m"; - - for ( const auto match_idx : page_data.match_lines ) { - auto& line = page_data.lines[match_idx]; - std::size_t pos = 0; - std::string replacement; - replacement.reserve( hl_start.length() + query.length() + hl_end.length() ); - replacement += hl_start; - replacement += query; - replacement += hl_end; - while ( ( pos = line.find( query, pos ) ) != std::string::npos ) { - line.replace( pos, query.length(), replacement ); - pos += hl_start.length() + query.length() + hl_end.length(); - } - } -} - -void execute_search( PageData& page_data, const std::string& query, std::size_t& scroll_y, bool next_match ) -{ - if ( query.empty() ) { - return; - } - - if ( !next_match || query != page_data.last_search ) { - find_search_matches( page_data, query ); - apply_search_highlight( page_data, query ); - } else if ( !page_data.match_lines.empty() ) { - page_data.current_match_idx = ( page_data.current_match_idx + 1 ) % page_data.match_lines.size(); - } - - if ( page_data.match_lines.empty() ) { - return; - } - - const std::size_t target_line = page_data.match_lines[page_data.current_match_idx]; - scroll_y = ( target_line > 5 ) ? ( target_line - 5 ) : 0; -} - -} // namespace ricochet::core diff --git a/src/core/ui_input.cc b/src/core/ui_input.cc deleted file mode 100644 index 0f57dd1..0000000 --- a/src/core/ui_input.cc +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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 <https://www.gnu.org/licenses/>. - */ - -#include "ricochet/core/ui_input.hh" -#include "ricochet/core/browser.hh" -#include <iostream> - -namespace ricochet::core { - -std::string get_url_input( const tui::Terminal& terminal ) -{ - auto [width, height] = terminal.get_size(); - terminal.move_cursor( height, 1 ); - std::cout << "\x1b[2K\033[7m URL: \033[0m "; - std::cout.flush(); - - std::string input; - terminal.show_cursor( true ); - while ( true ) { - const char ch = terminal.read_key(); - if ( ch == '\r' || ch == '\n' ) { - break; - } - if ( ch == 27 ) { - input.clear(); - break; - } - if ( ch == 127 || ch == 8 ) { - if ( !input.empty() ) { - input.pop_back(); - std::cout << "\b \b"; - std::cout.flush(); - } - } else if ( std::isprint( static_cast<unsigned char>( ch ) ) ) { - input += ch; - std::cout << ch; - std::cout.flush(); - } - } - terminal.show_cursor( false ); - - return Browser::format_url( std::move( input ) ); -} - -std::string get_terminal_input( const tui::Terminal& terminal, const std::string& prompt ) -{ - auto [width, height] = terminal.get_size(); - terminal.move_cursor( height, 1 ); - std::cout << "\x1b[2K\033[7m " << prompt << " \033[0m "; - std::cout.flush(); - - std::string input; - terminal.show_cursor( true ); - while ( true ) { - const char ch = terminal.read_key(); - if ( ch == '\r' || ch == '\n' ) { - break; - } - if ( ch == 27 ) { - input.clear(); - break; - } - if ( ch == 127 || ch == 8 ) { - if ( !input.empty() ) { - input.pop_back(); - std::cout << "\b \b"; - std::cout.flush(); - } - } else if ( std::isprint( static_cast<unsigned char>( ch ) ) ) { - input += ch; - std::cout << ch; - std::cout.flush(); - } - } - terminal.show_cursor( false ); - return input; -} - -} // namespace ricochet::core diff --git a/src/core/ui_view.cc b/src/core/ui_view.cc deleted file mode 100644 index 5164fe0..0000000 --- a/src/core/ui_view.cc +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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 <https://www.gnu.org/licenses/>. - */ - -#include "ricochet/core/ui_view.hh" -#include <iostream> - -namespace ricochet::core { - -std::string build_footer( std::string_view url, std::size_t width ) -{ - const std::string_view keys - = " | [j/k]Scroll [n/N]Find [/]Nav [f]Hint [i]In [r]Ref [H/L]Back [h]Hist [b/B]Mark [?]Help [q]Quit "; - const std::string_view prefix = " URL: "; - - std::string footer; - if ( width <= keys.length() + prefix.length() + 3 ) { - footer.reserve( prefix.length() + url.length() + keys.length() ); - footer += prefix; - footer += url; - footer += keys; - if ( footer.length() > width && width > 3 ) { - footer.erase( width - 3 ); - footer += "..."; - } - } else { - const std::size_t max_url_len = width - keys.length() - prefix.length(); - footer.reserve( width ); - footer += prefix; - if ( url.length() > max_url_len ) { - footer += url.substr( 0, max_url_len - 3 ); - footer += "..."; - } else { - footer += url; - } - footer += keys; - } - - if ( footer.length() < width ) { - footer.append( width - footer.length(), ' ' ); - } - return footer; -} - -void draw_view( const tui::Terminal& terminal, - const std::vector<std::string>& lines, - std::size_t scroll_y, - std::string_view url, // NOLINT(bugprone-easily-swappable-parameters) - std::string_view title ) // NOLINT(bugprone-easily-swappable-parameters) -{ - terminal.clear_screen(); - auto [width, height] = terminal.get_size(); - const std::size_t usable_height = ( height > 2 ) ? ( height - 2 ) : 1; - - std::string clean_title( title ); - if ( clean_title.empty() ) { - const std::size_t start = url.find( "://" ); - const std::size_t host_start = ( start == std::string::npos ) ? 0 : start + 3; - const std::size_t host_end = url.find( '/', host_start ); - clean_title = std::string( url.substr( host_start, host_end - host_start ) ); - } - for ( char& c : clean_title ) { - if ( c == '\n' || c == '\r' ) { - c = ' '; - } - } - - std::string header = " PAGE: " + clean_title; - if ( header.size() < width ) { - header.append( width - header.size(), ' ' ); - } else if ( header.size() > width && width > 3 ) { - header = header.substr( 0, width - 3 ) + "..."; - } - std::cout << "\033[7m" << header << "\033[0m\r\n"; - - for ( std::size_t i = 0; i < usable_height; ++i ) { - if ( scroll_y + i < lines.size() ) { - std::cout << lines[scroll_y + i] << "\r\n"; - } else { - std::cout << "~\r\n"; - } - } - - const std::string footer = build_footer( url, width ); - std::cout << "\033[7m" << footer << "\033[0m"; - std::cout.flush(); -} - -} // namespace ricochet::core diff --git a/src/core/utils.cc b/src/core/utils.cc index ddc6321..a410c8a 100644 --- a/src/core/utils.cc +++ b/src/core/utils.cc @@ -82,37 +82,4 @@ std::string strip_ddg_tracker( std::string target ) return target; } -std::vector<std::string> wrap_lines( const std::vector<std::string>& original_lines, std::size_t wrap_width ) -{ - std::vector<std::string> wrapped_lines; - for ( const auto& line : original_lines ) { - std::string current_line; - std::size_t vis_count = 0; - bool in_ansi = false; - for ( const char c : line ) { - if ( c == '\x1b' ) { - in_ansi = true; - } - if ( in_ansi ) { - current_line += c; - if ( c == 'm' ) { - in_ansi = false; - } - } else { - if ( vis_count >= wrap_width ) { - wrapped_lines.push_back( current_line + "\033[0m" ); - current_line.clear(); - vis_count = 0; - } - current_line += c; - vis_count++; - } - } - if ( !current_line.empty() || line.empty() ) { - wrapped_lines.push_back( current_line + "\033[0m" ); - } - } - return wrapped_lines; -} - } // namespace ricochet::core diff --git a/src/tui/terminal.cc b/src/tui/terminal.cc deleted file mode 100644 index 44c0f3b..0000000 --- a/src/tui/terminal.cc +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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 <https://www.gnu.org/licenses/>. - */ - -#include "ricochet/tui/terminal.hh" -#include <cstdlib> -#include <iostream> -#include <sys/ioctl.h> -#include <unistd.h> - -namespace ricochet::tui { - -Terminal::Terminal() -{ - // get terminal attributes - tcgetattr( STDIN_FILENO, &original_termios_ ); - - struct termios raw = original_termios_; - - // close echo, close canonical mode - // close terminal signals (ISIG disable Ctrl+C and Ctrl+Z, let the program handle it) - raw.c_lflag &= ~( ECHO | ICANON | ISIG ); - - // close software flow control (Ctrl+S, Ctrl+Q) - raw.c_iflag &= ~( IXON | ICRNL ); - - // apply new terminal attributes - tcsetattr( STDIN_FILENO, TCSAFLUSH, &raw ); -} - -Terminal::~Terminal() -{ - show_cursor( true ); - // RAII: restore terminal attributes when program exits - tcsetattr( STDIN_FILENO, TCSAFLUSH, &original_termios_ ); -} - -char Terminal::read_key() const // NOLINT(readability-convert-member-functions-to-static) -{ - char c = '\0'; - // read 1 byte - if ( read( STDIN_FILENO, &c, 1 ) == -1 ) { - return '\0'; - } - return c; -} - -void Terminal::clear_screen() const // NOLINT(readability-convert-member-functions-to-static) -{ - // ANSI escape codes: \x1b[2J clear screen, \x1b[H move cursor to (1,1) - std::cout << "\x1b[2J\x1b[H"; -} - -std::pair<std::size_t, std::size_t> Terminal::get_size() // NOLINT(readability-convert-member-functions-to-static) - const -{ - struct winsize w {}; - if ( ioctl( STDOUT_FILENO, TIOCGWINSZ, &w ) == -1 ) { - return { 80, 24 }; - } - return { static_cast<std::size_t>( w.ws_col ), static_cast<std::size_t>( w.ws_row ) }; -} - -void Terminal::move_cursor( std::size_t row, std::size_t col ) const // NOLINT -{ - // ANSI: \x1b[H move to 1,1; \x1b[row;colH move to specified row and column - std::cout << "\x1b[" << row << ";" << col << "H"; -} - -void Terminal::show_cursor( bool visible ) const // NOLINT -{ - // \x1b[?25h show cursor, \x1b[?25l hide cursor - std::cout << ( visible ? "\x1b[?25h" : "\x1b[?25l" ); -} - -} // namespace ricochet::tui
\ No newline at end of file |
