From 673201e4fd572d6b508dc55e301a30dd393cce02 Mon Sep 17 00:00:00 2001 From: scawful Date: Mon, 6 Oct 2025 14:30:15 -0400 Subject: [PATCH] feat: Introduce Unified Layout with Enhanced Chat and Status Panels - Added a new unified layout system that integrates chat and status panels for improved user experience. - Implemented EnhancedChatComponent and EnhancedStatusPanel to manage chat interactions and display system information. - Updated CMake configuration to include new source files for the unified layout, ensuring proper build setup. - Refactored existing TUI components to support the new layout structure, enhancing modularity and maintainability. --- src/cli/tui/enhanced_chat_component.cc | 263 ++++++++++++ src/cli/tui/enhanced_chat_component.h | 84 ++++ src/cli/tui/enhanced_status_panel.cc | 251 +++++++++++ src/cli/tui/enhanced_status_panel.h | 106 +++++ src/cli/tui/tui.cc | 158 +------ src/cli/tui/unified_layout.cc | 569 +++++++++++++++++++++++++ src/cli/tui/unified_layout.h | 140 ++++++ src/cli/z3ed.cmake | 3 + 8 files changed, 1435 insertions(+), 139 deletions(-) create mode 100644 src/cli/tui/enhanced_chat_component.cc create mode 100644 src/cli/tui/enhanced_chat_component.h create mode 100644 src/cli/tui/enhanced_status_panel.cc create mode 100644 src/cli/tui/enhanced_status_panel.h create mode 100644 src/cli/tui/unified_layout.cc create mode 100644 src/cli/tui/unified_layout.h diff --git a/src/cli/tui/enhanced_chat_component.cc b/src/cli/tui/enhanced_chat_component.cc new file mode 100644 index 00000000..d6c9c624 --- /dev/null +++ b/src/cli/tui/enhanced_chat_component.cc @@ -0,0 +1,263 @@ +#include "cli/tui/enhanced_chat_component.h" + +#include +#include +#include + +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" + +namespace yaze { +namespace cli { + +using namespace ftxui; + +EnhancedChatComponent::EnhancedChatComponent(Rom* rom_context) + : rom_context_(rom_context) { + // Initialize agent service + if (rom_context_) { + agent_service_.SetRomContext(rom_context_); + } + + // Initialize autocomplete + autocomplete_engine_.RegisterCommand("/help", "Show help message"); + autocomplete_engine_.RegisterCommand("/exit", "Exit the chat"); + autocomplete_engine_.RegisterCommand("/clear", "Clear chat history"); + autocomplete_engine_.RegisterCommand("/rom_info", "Display ROM information"); + autocomplete_engine_.RegisterCommand("/status", "Show chat status"); + + // Create components + input_component_ = CreateInputComponent(); + history_component_ = CreateHistoryComponent(); + chat_container_ = CreateChatContainer(); + + // Set up event handlers + input_event_handler_ = [this](const Event& event) { + return HandleInputEvents(event); + }; + + history_event_handler_ = [this](const Event& event) { + return HandleHistoryEvents(event); + }; +} + +Component EnhancedChatComponent::GetComponent() { + return chat_container_; +} + +void EnhancedChatComponent::SetRomContext(Rom* rom_context) { + rom_context_ = rom_context; + if (rom_context_) { + agent_service_.SetRomContext(rom_context_); + } +} + +void EnhancedChatComponent::SendMessage(const std::string& message) { + if (message.empty()) return; + + ProcessMessage(message); + input_message_.clear(); +} + +void EnhancedChatComponent::ClearHistory() { + chat_history_.clear(); + selected_history_index_ = 0; + UpdateHistoryDisplay(); +} + +void EnhancedChatComponent::ResetConversation() { + agent_service_.ResetConversation(); + ClearHistory(); +} + +Component EnhancedChatComponent::CreateInputComponent() { + auto input = Input(&input_message_, "Type your message..."); + + auto send_button = Button("Send", [this] { + SendMessage(input_message_); + }); + + auto clear_button = Button("Clear", [this] { + ClearHistory(); + }); + + auto container = Container::Horizontal({ + input, + send_button, + clear_button + }); + + return CatchEvent(container, input_event_handler_); +} + +Component EnhancedChatComponent::CreateHistoryComponent() { + return Renderer([this] { + return RenderHistoryArea(); + }); +} + +Component EnhancedChatComponent::CreateChatContainer() { + auto container = Container::Vertical({ + history_component_, + input_component_ + }); + + return Renderer(container, [this] { + return vbox({ + text("šŸ¤– AI Chat") | bold | center, + separator(), + history_component_->Render() | flex | frame, + separator(), + input_component_->Render(), + separator(), + text("Commands: /help, /exit, /clear, /rom_info, /status") | dim | center + }) | border; + }); +} + +bool EnhancedChatComponent::HandleInputEvents(const Event& event) { + if (event == Event::Return) { + if (input_message_.empty()) return true; + + SendMessage(input_message_); + return true; + } + + if (event == Event::Character('q')) { + // Exit chat or go back + return true; + } + + return false; +} + +bool EnhancedChatComponent::HandleHistoryEvents(const Event& event) { + if (event == Event::ArrowUp) { + if (selected_history_index_ > 0) { + selected_history_index_--; + } + return true; + } + + if (event == Event::ArrowDown) { + if (selected_history_index_ < static_cast(chat_history_.size()) - 1) { + selected_history_index_++; + } + return true; + } + + return false; +} + +void EnhancedChatComponent::ProcessMessage(const std::string& message) { + // Handle special commands + if (message == "/exit") { + // Signal to parent component to exit + return; + } + + if (message == "/clear") { + ClearHistory(); + return; + } + + if (message == "/help") { + AddMessageToHistory("System", + "Available commands: /help, /exit, /clear, /rom_info, /status"); + return; + } + + if (message == "/rom_info") { + if (rom_context_) { + AddMessageToHistory("System", + absl::StrFormat("ROM: %s | Size: %d bytes", + rom_context_->title(), rom_context_->size())); + } else { + AddMessageToHistory("System", "No ROM loaded"); + } + return; + } + + if (message == "/status") { + AddMessageToHistory("System", + absl::StrFormat("Chat Status: %d messages, %s", + chat_history_.size(), + focused_ ? "Focused" : "Not focused")); + return; + } + + // Add user message to history + AddMessageToHistory("You", message); + + // Send to agent service + auto response = agent_service_.SendMessage(message); + if (response.ok()) { + // Agent response will be handled by the service + // For now, add a placeholder response + AddMessageToHistory("Agent", "Response received (integration pending)"); + } else { + AddMessageToHistory("System", + absl::StrCat("Error: ", response.status().message())); + } + + UpdateHistoryDisplay(); +} + +void EnhancedChatComponent::AddMessageToHistory(const std::string& sender, const std::string& message) { + chat_history_.push_back({sender, message}); + + // Limit history size + if (static_cast(chat_history_.size()) > max_history_lines_) { + chat_history_.erase(chat_history_.begin()); + } + + // Auto-scroll to bottom + selected_history_index_ = chat_history_.size() - 1; +} + +void EnhancedChatComponent::UpdateHistoryDisplay() { + // Trigger re-render of history component + // This is handled automatically by FTXUI +} + +Element EnhancedChatComponent::RenderChatMessage(const std::string& sender, const std::string& message) { + Color sender_color; + if (sender == "You") { + sender_color = Color::Yellow; + } else if (sender == "Agent") { + sender_color = Color::Green; + } else { + sender_color = Color::Cyan; + } + + return hbox({ + text(sender) | bold | color(sender_color), + text(": "), + text(message) | flex + }); +} + +Element EnhancedChatComponent::RenderInputArea() { + return hbox({ + text("You: ") | bold, + input_component_->Render() | flex + }); +} + +Element EnhancedChatComponent::RenderHistoryArea() { + if (chat_history_.empty()) { + return vbox({ + text("No messages yet. Start chatting!") | dim | center + }) | flex | center; + } + + std::vector messages; + for (const auto& [sender, message] : chat_history_) { + messages.push_back(RenderChatMessage(sender, message)); + } + + return vbox(messages) | flex; +} + +} // namespace cli +} // namespace yaze diff --git a/src/cli/tui/enhanced_chat_component.h b/src/cli/tui/enhanced_chat_component.h new file mode 100644 index 00000000..4f261559 --- /dev/null +++ b/src/cli/tui/enhanced_chat_component.h @@ -0,0 +1,84 @@ +#ifndef YAZE_CLI_TUI_ENHANCED_CHAT_COMPONENT_H +#define YAZE_CLI_TUI_ENHANCED_CHAT_COMPONENT_H + +#include +#include +#include +#include +#include + +#include "app/rom.h" +#include "cli/service/agent/conversational_agent_service.h" +#include "cli/util/autocomplete.h" + +namespace yaze { +namespace cli { + +// Enhanced chat component that integrates with the unified layout +class EnhancedChatComponent { + public: + explicit EnhancedChatComponent(Rom* rom_context = nullptr); + + // Component interface + ftxui::Component GetComponent(); + void SetRomContext(Rom* rom_context); + + // Chat functionality + void SendMessage(const std::string& message); + void ClearHistory(); + void ResetConversation(); + + // State management + bool IsFocused() const { return focused_; } + void SetFocused(bool focused) { focused_ = focused; } + + // Configuration + void SetMaxHistoryLines(int lines) { max_history_lines_ = lines; } + int GetMaxHistoryLines() const { return max_history_lines_; } + + private: + // Component creation + ftxui::Component CreateInputComponent(); + ftxui::Component CreateHistoryComponent(); + ftxui::Component CreateChatContainer(); + + // Event handling + bool HandleInputEvents(const ftxui::Event& event); + bool HandleHistoryEvents(const ftxui::Event& event); + + // Message processing + void ProcessMessage(const std::string& message); + void AddMessageToHistory(const std::string& sender, const std::string& message); + void UpdateHistoryDisplay(); + + // Rendering + ftxui::Element RenderChatMessage(const std::string& sender, const std::string& message); + ftxui::Element RenderInputArea(); + ftxui::Element RenderHistoryArea(); + + // State + Rom* rom_context_; + agent::ConversationalAgentService agent_service_; + AutocompleteEngine autocomplete_engine_; + + // UI State + std::string input_message_; + std::vector> chat_history_; + int selected_history_index_ = 0; + bool focused_ = false; + int max_history_lines_ = 20; + + // Components + ftxui::Component input_component_; + ftxui::Component history_component_; + ftxui::Component chat_container_; + + // Event handlers + std::function input_event_handler_; + std::function history_event_handler_; +}; + +} // namespace cli +} // namespace yaze + +#endif // YAZE_CLI_TUI_ENHANCED_CHAT_COMPONENT_H diff --git a/src/cli/tui/enhanced_status_panel.cc b/src/cli/tui/enhanced_status_panel.cc new file mode 100644 index 00000000..1ad93a8c --- /dev/null +++ b/src/cli/tui/enhanced_status_panel.cc @@ -0,0 +1,251 @@ +#include "cli/tui/enhanced_status_panel.h" + +#include +#include +#include +#include +#include + +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" + +namespace yaze { +namespace cli { + +using namespace ftxui; + +EnhancedStatusPanel::EnhancedStatusPanel(Rom* rom_context) + : rom_context_(rom_context) { + // Initialize status data + CollectRomInfo(); + CollectSystemInfo(); + CollectLayoutInfo(); + + // Create components + rom_info_section_ = CreateRomInfoSection(); + system_info_section_ = CreateSystemInfoSection(); + layout_info_section_ = CreateLayoutInfoSection(); + error_section_ = CreateErrorSection(); + status_container_ = CreateStatusContainer(); + + // Set up event handlers + status_event_handler_ = [this](Event event) { + return HandleStatusEvents(event); + }; +} + +Component EnhancedStatusPanel::GetComponent() { + return status_container_; +} + +void EnhancedStatusPanel::SetRomContext(Rom* rom_context) { + rom_context_ = rom_context; + UpdateRomInfo(); +} + +void EnhancedStatusPanel::UpdateRomInfo() { + CollectRomInfo(); +} + +void EnhancedStatusPanel::UpdateSystemInfo() { + CollectSystemInfo(); +} + +void EnhancedStatusPanel::UpdateLayoutInfo() { + CollectLayoutInfo(); +} + +void EnhancedStatusPanel::SetError(const std::string& error) { + current_error_ = error; +} + +void EnhancedStatusPanel::ClearError() { + current_error_.clear(); +} + +Component EnhancedStatusPanel::CreateStatusContainer() { + auto container = Container::Vertical({ + rom_info_section_, + system_info_section_, + layout_info_section_, + error_section_ + }); + + return Renderer(container, [this] { + return vbox({ + text("šŸ“Š Status Panel") | bold | center, + separator(), + rom_info_section_->Render(), + separator(), + system_info_section_->Render(), + separator(), + layout_info_section_->Render(), + separator(), + error_section_->Render(), + separator(), + RenderStatusBar() + }) | border; + }); +} + +Component EnhancedStatusPanel::CreateRomInfoSection() { + return Renderer([this] { + return RenderRomInfo(); + }); +} + +Component EnhancedStatusPanel::CreateSystemInfoSection() { + return Renderer([this] { + return RenderSystemInfo(); + }); +} + +Component EnhancedStatusPanel::CreateLayoutInfoSection() { + return Renderer([this] { + return RenderLayoutInfo(); + }); +} + +Component EnhancedStatusPanel::CreateErrorSection() { + return Renderer([this] { + return RenderErrorInfo(); + }); +} + +bool EnhancedStatusPanel::HandleStatusEvents(const Event& event) { + if (event == Event::Character('e')) { + SetExpanded(!expanded_); + return true; + } + + if (event == Event::Character('d')) { + SetShowDetailedInfo(!show_detailed_info_); + return true; + } + + return false; +} + +Element EnhancedStatusPanel::RenderRomInfo() { + if (!rom_info_.loaded) { + return vbox({ + text("šŸŽ® ROM Information") | bold | color(Color::Red), + text("No ROM loaded") | color(Color::Red), + text("Load a ROM to see information") | dim + }); + } + + return vbox({ + text("šŸŽ® ROM Information") | bold | color(Color::Green), + text(absl::StrFormat("Title: %s", rom_info_.title)) | color(Color::White), + text(absl::StrFormat("Size: %s", rom_info_.size)) | color(Color::Cyan), + text(absl::StrFormat("Checksum: %s", rom_info_.checksum)) | color(Color::Yellow), + text(absl::StrFormat("Region: %s", rom_info_.region)) | color(Color::Magenta) + }); +} + +Element EnhancedStatusPanel::RenderSystemInfo() { + return vbox({ + text("šŸ’» System Information") | bold | color(Color::Blue), + text(absl::StrFormat("Time: %s", system_info_.current_time)) | color(Color::White), + text(absl::StrFormat("Memory: %s", system_info_.memory_usage)) | color(Color::Cyan), + text(absl::StrFormat("CPU: %s", system_info_.cpu_usage)) | color(Color::Yellow), + text(absl::StrFormat("Disk: %s", system_info_.disk_space)) | color(Color::Green) + }); +} + +Element EnhancedStatusPanel::RenderLayoutInfo() { + return vbox({ + text("šŸ–„ļø Layout Information") | bold | color(Color::Magenta), + text(absl::StrFormat("Active Panel: %s", layout_info_.active_panel)) | color(Color::White), + text(absl::StrFormat("Panel Count: %s", layout_info_.panel_count)) | color(Color::Cyan), + text(absl::StrFormat("Layout Mode: %s", layout_info_.layout_mode)) | color(Color::Yellow), + text(absl::StrFormat("Focus State: %s", layout_info_.focus_state)) | color(Color::Green) + }); +} + +Element EnhancedStatusPanel::RenderErrorInfo() { + if (current_error_.empty()) { + return vbox({ + text("āœ… No Errors") | color(Color::Green) + }); + } + + return vbox({ + text("āš ļø Error") | bold | color(Color::Red), + text(current_error_) | color(Color::Yellow) + }); +} + +Element EnhancedStatusPanel::RenderStatusBar() { + return hbox({ + text("e: Expand | d: Details | q: Quit") | dim, + filler(), + text(expanded_ ? "EXPANDED" : "COLLAPSED") | color(Color::Cyan), + filler(), + text(show_detailed_info_ ? "DETAILED" : "SIMPLE") | color(Color::Yellow) + }); +} + +void EnhancedStatusPanel::CollectRomInfo() { + if (!rom_context_) { + rom_info_.loaded = false; + rom_info_.title = "No ROM"; + rom_info_.size = "0 bytes"; + rom_info_.checksum = "N/A"; + rom_info_.region = "N/A"; + return; + } + + rom_info_.loaded = true; + rom_info_.title = rom_context_->title(); + rom_info_.size = absl::StrFormat("%d bytes", rom_context_->size()); + + // Calculate checksum (simplified) + uint32_t checksum = 0; + if (rom_context_->size() > 0) { + for (size_t i = 0; i < std::min(rom_context_->size(), size_t(1024)); ++i) { + checksum += rom_context_->vector()[i]; + } + } + rom_info_.checksum = absl::StrFormat("0x%08X", checksum); + + // Determine region (simplified) + if (rom_context_->size() > 0) { + uint8_t region_byte = rom_context_->vector()[0x7FD9]; // SNES region byte + switch (region_byte) { + case 0x00: rom_info_.region = "NTSC"; break; + case 0x01: rom_info_.region = "PAL"; break; + default: rom_info_.region = "Unknown"; break; + } + } else { + rom_info_.region = "Unknown"; + } +} + +void EnhancedStatusPanel::CollectSystemInfo() { + // Get current time + auto now = std::chrono::system_clock::now(); + auto time_t = std::chrono::system_clock::to_time_t(now); + auto tm = *std::localtime(&time_t); + + std::stringstream ss; + ss << std::put_time(&tm, "%H:%M:%S"); + system_info_.current_time = ss.str(); + + // Placeholder system info (in a real implementation, you'd query the system) + system_info_.memory_usage = "Unknown"; + system_info_.cpu_usage = "Unknown"; + system_info_.disk_space = "Unknown"; +} + +void EnhancedStatusPanel::CollectLayoutInfo() { + // Placeholder layout info (in a real implementation, you'd get this from the layout manager) + layout_info_.active_panel = "Main Menu"; + layout_info_.panel_count = "4"; + layout_info_.layout_mode = "Unified"; + layout_info_.focus_state = "Chat"; +} + +} // namespace cli +} // namespace yaze diff --git a/src/cli/tui/enhanced_status_panel.h b/src/cli/tui/enhanced_status_panel.h new file mode 100644 index 00000000..2c3c0cfb --- /dev/null +++ b/src/cli/tui/enhanced_status_panel.h @@ -0,0 +1,106 @@ +#ifndef YAZE_CLI_TUI_ENHANCED_STATUS_PANEL_H +#define YAZE_CLI_TUI_ENHANCED_STATUS_PANEL_H + +#include +#include +#include +#include +#include + +#include "app/rom.h" + +namespace yaze { +namespace cli { + +// Enhanced status panel with comprehensive system information +class EnhancedStatusPanel { + public: + explicit EnhancedStatusPanel(Rom* rom_context = nullptr); + + // Component interface + ftxui::Component GetComponent(); + void SetRomContext(Rom* rom_context); + + // Status updates + void UpdateRomInfo(); + void UpdateSystemInfo(); + void UpdateLayoutInfo(); + void SetError(const std::string& error); + void ClearError(); + + // Configuration + void SetShowDetailedInfo(bool show) { show_detailed_info_ = show; } + bool GetShowDetailedInfo() const { return show_detailed_info_; } + + // State management + bool IsExpanded() const { return expanded_; } + void SetExpanded(bool expanded) { expanded_ = expanded; } + + private: + // Component creation + ftxui::Component CreateStatusContainer(); + ftxui::Component CreateRomInfoSection(); + ftxui::Component CreateSystemInfoSection(); + ftxui::Component CreateLayoutInfoSection(); + ftxui::Component CreateErrorSection(); + + // Event handling + bool HandleStatusEvents(const ftxui::Event& event); + + // Rendering + ftxui::Element RenderRomInfo(); + ftxui::Element RenderSystemInfo(); + ftxui::Element RenderLayoutInfo(); + ftxui::Element RenderErrorInfo(); + ftxui::Element RenderStatusBar(); + + // Data collection + void CollectRomInfo(); + void CollectSystemInfo(); + void CollectLayoutInfo(); + + // State + Rom* rom_context_; + bool expanded_ = false; + bool show_detailed_info_ = true; + + // Status data + struct RomInfo { + std::string title; + std::string size; + std::string checksum; + std::string region; + bool loaded = false; + } rom_info_; + + struct SystemInfo { + std::string current_time; + std::string memory_usage; + std::string cpu_usage; + std::string disk_space; + } system_info_; + + struct LayoutInfo { + std::string active_panel; + std::string panel_count; + std::string layout_mode; + std::string focus_state; + } layout_info_; + + std::string current_error_; + + // Components + ftxui::Component status_container_; + ftxui::Component rom_info_section_; + ftxui::Component system_info_section_; + ftxui::Component layout_info_section_; + ftxui::Component error_section_; + + // Event handlers + std::function status_event_handler_; +}; + +} // namespace cli +} // namespace yaze + +#endif // YAZE_CLI_TUI_ENHANCED_STATUS_PANEL_H diff --git a/src/cli/tui/tui.cc b/src/cli/tui/tui.cc index 816c9a5e..674b96f2 100644 --- a/src/cli/tui/tui.cc +++ b/src/cli/tui/tui.cc @@ -12,9 +12,8 @@ #include "util/bps.h" #include "util/file_util.h" #include "app/core/asar_wrapper.h" -#include "app/zelda3/overworld/overworld.h" #include "cli/cli.h" -#include "cli/tui/command_palette.h" +#include "cli/tui/unified_layout.h" #include "cli/z3ed_ascii_logo.h" #include "cli/service/agent/simple_chat_session.h" #include "cli/service/agent/conversational_agent_service.h" @@ -865,7 +864,7 @@ void DashboardComponent(ftxui::ScreenInteractive &screen) { }); }); - auto event_handler = CatchEvent(layout, [&](Event event) { + auto event_handler = CatchEvent(layout, [&](const Event& event) { if (event == Event::Character('q')) { SwitchComponents(screen, LayoutID::kExit); return true; @@ -943,7 +942,7 @@ void MainMenuComponent(ftxui::ScreenInteractive &screen) { }); // Catch events like pressing Enter to switch layout or pressing 'q' to exit. - auto main_component = CatchEvent(renderer, [&](Event event) { + auto main_component = CatchEvent(renderer, [&](const Event& event) { if (event == Event::Return) { switch ((MainMenuEntry)selected) { case MainMenuEntry::kLoadRom: @@ -989,141 +988,22 @@ void MainMenuComponent(ftxui::ScreenInteractive &screen) { } // namespace void ShowMain() { - auto screen = ScreenInteractive::TerminalOutput(); - while (app_context.current_layout != LayoutID::kExit) { - if (app_context.rom.is_loaded() && app_context.current_layout == LayoutID::kMainMenu) { - app_context.current_layout = LayoutID::kDashboard; - } - - switch (app_context.current_layout) { - case LayoutID::kDashboard: { - DashboardComponent(screen); - } break; - case LayoutID::kMainMenu: { - MainMenuComponent(screen); - } break; - case LayoutID::kLoadRom: { - LoadRomComponent(screen); - } break; - case LayoutID::kAIAgentChat: { - // Launch simple chat session for agent interaction - agent::SimpleChatSession chat; - chat.SetRomContext(&app_context.rom); - agent::AgentConfig config; - config.output_format = agent::AgentOutputFormat::kFriendly; - chat.SetConfig(config); - - std::cout << "\nšŸ¤– AI Agent Chat (type 'back' to return to menu)\n" << std::endl; - chat.RunInteractive(); - - app_context.current_layout = LayoutID::kMainMenu; - } break; - case LayoutID::kTodoManager: { - TodoManagerComponent(screen); - } break; - case LayoutID::kRomTools: { - // Show submenu for ROM tools - int submenu_selected = 0; - static const std::vector tools = { - "Apply Asar Patch", "Apply BPS Patch", "Extract Symbols", - "Validate Assembly", "Generate Save", "Back" - }; - auto submenu = Menu(&tools, &submenu_selected); - auto submenu_component = CatchEvent(submenu, [&](Event event) { - if (event == Event::Return) { - if (submenu_selected == 0) app_context.current_layout = LayoutID::kApplyAsarPatch; - else if (submenu_selected == 1) app_context.current_layout = LayoutID::kApplyBpsPatch; - else if (submenu_selected == 2) app_context.current_layout = LayoutID::kExtractSymbols; - else if (submenu_selected == 3) app_context.current_layout = LayoutID::kValidateAssembly; - else if (submenu_selected == 4) app_context.current_layout = LayoutID::kGenerateSaveFile; - else app_context.current_layout = LayoutID::kMainMenu; - screen.ExitLoopClosure()(); - return true; - } - return false; - }); - screen.Loop(submenu_component); - } break; - case LayoutID::kGraphicsTools: { - // Show submenu for graphics tools - int submenu_selected = 0; - static const std::vector tools = { - "Palette Editor", "Hex Viewer", "Back" - }; - auto submenu = Menu(&tools, &submenu_selected); - auto submenu_component = CatchEvent(submenu, [&](Event event) { - if (event == Event::Return) { - if (submenu_selected == 0) app_context.current_layout = LayoutID::kPaletteEditor; - else if (submenu_selected == 1) app_context.current_layout = LayoutID::kHexViewer; - else app_context.current_layout = LayoutID::kMainMenu; - screen.ExitLoopClosure()(); - return true; - } - return false; - }); - screen.Loop(submenu_component); - } break; - case LayoutID::kTestingTools: { - app_context.error_message = "Testing tools coming soon"; - app_context.current_layout = LayoutID::kError; - } break; - case LayoutID::kSettings: { - app_context.error_message = "Settings TUI coming soon - use GUI for now"; - app_context.current_layout = LayoutID::kError; - } break; - case LayoutID::kApplyAsarPatch: { - ApplyAsarPatchComponent(screen); - } break; - case LayoutID::kApplyBpsPatch: { - ApplyBpsPatchComponent(screen); - } break; - case LayoutID::kExtractSymbols: { - ExtractSymbolsComponent(screen); - } break; - case LayoutID::kValidateAssembly: { - ValidateAssemblyComponent(screen); - } break; - case LayoutID::kGenerateSaveFile: { - GenerateSaveFileComponent(screen); - } break; - case LayoutID::kPaletteEditor: { - PaletteEditorComponent(screen); - } break; - case LayoutID::kHexViewer: { - HexViewerComponent(screen); - } break; - case LayoutID::kCommandPalette: { - CommandPaletteComponent component; - auto cmd_component = component.Render(); - screen.Loop(cmd_component); - } break; - case LayoutID::kHelp: { - HelpComponent(screen); - } break; - case LayoutID::kError: { - // Display error message and return to main menu. - auto error_button = Button("Back to Main Menu", [&] { - app_context.error_message.clear(); - SwitchComponents(screen, LayoutID::kMainMenu); - }); - - auto error_renderer = Renderer(error_button, [&] { - return vbox({ - text("āš ļø Error") | center | bold | color(Color::Red), - separator(), - text(app_context.error_message) | color(Color::Yellow) | center, - separator(), - error_button->Render() | center - }) | center | border; - }); - - screen.Loop(error_renderer); - } break; - case LayoutID::kExit: - default: - return; // Exit the application. - } - } + // Use the new unified layout system + UnifiedLayout unified_layout(&app_context.rom); + + // Configure the layout + LayoutConfig config; + config.left_panel_width = 30; + config.right_panel_width = 40; + config.bottom_panel_height = 15; + config.show_chat = true; + config.show_status = true; + config.show_tools = true; + + unified_layout.SetLayoutConfig(config); + + // Run the unified layout + unified_layout.Run(); } } // namespace cli diff --git a/src/cli/tui/unified_layout.cc b/src/cli/tui/unified_layout.cc new file mode 100644 index 00000000..56068377 --- /dev/null +++ b/src/cli/tui/unified_layout.cc @@ -0,0 +1,569 @@ +#include "cli/tui/unified_layout.h" + +#include +#include +#include +#include + +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "cli/tui/tui.h" +#include "cli/service/agent/conversational_agent_service.h" + +namespace yaze { +namespace cli { + +using namespace ftxui; + +UnifiedLayout::UnifiedLayout(Rom* rom_context) + : screen_(ScreenInteractive::TerminalOutput()), + rom_context_(rom_context) { + // Initialize chat TUI + chat_tui_ = std::make_unique(rom_context_); + + // Set default configuration + config_ = LayoutConfig{}; + + // Initialize state + state_ = PanelState{}; + if (rom_context_) { + state_.current_rom_file = rom_context_->title(); + } + + // Create components + main_menu_panel_ = CreateMainMenuPanel(); + chat_panel_ = CreateChatPanel(); + status_panel_ = CreateStatusPanel(); + tools_panel_ = CreateToolsPanel(); + hex_viewer_panel_ = CreateHexViewerPanel(); + palette_editor_panel_ = CreatePaletteEditorPanel(); + todo_manager_panel_ = CreateTodoManagerPanel(); + rom_tools_panel_ = CreateRomToolsPanel(); + graphics_tools_panel_ = CreateGraphicsToolsPanel(); + settings_panel_ = CreateSettingsPanel(); + help_panel_ = CreateHelpPanel(); + + // Create layout + unified_layout_ = CreateUnifiedLayout(); + + // Set up event handlers + global_event_handler_ = [this](const Event& event) { + return HandleGlobalEvents(event); + }; + + panel_event_handler_ = [this](const Event& event) { + return HandlePanelEvents(event); + }; +} + +void UnifiedLayout::Run() { + // Wrap the layout with event handling + auto event_handler = CatchEvent(unified_layout_, global_event_handler_); + + screen_.Loop(event_handler); +} + +void UnifiedLayout::SetRomContext(Rom* rom_context) { + rom_context_ = rom_context; + if (chat_tui_) { + chat_tui_->SetRomContext(rom_context_); + } + + if (rom_context_) { + state_.current_rom_file = rom_context_->title(); + } else { + state_.current_rom_file.clear(); + } +} + +void UnifiedLayout::SwitchMainPanel(PanelType panel) { + state_.active_main_panel = panel; +} + +void UnifiedLayout::SwitchToolPanel(PanelType panel) { + state_.active_tool_panel = panel; +} + +void UnifiedLayout::ToggleChat() { + config_.show_chat = !config_.show_chat; +} + +void UnifiedLayout::ToggleStatus() { + config_.show_status = !config_.show_status; +} + +void UnifiedLayout::SetLayoutConfig(const LayoutConfig& config) { + config_ = config; +} + +Component UnifiedLayout::CreateMainMenuPanel() { + static int selected = 0; + MenuOption option; + option.focused_entry = &selected; + + auto menu = Menu(&kMainMenuEntries, &selected, option); + + return Renderer(menu, [&] { + return vbox({ + text("šŸŽ® Z3ED Main Menu") | bold | center, + separator(), + menu->Render(), + separator(), + text("↑/↓: Navigate | Enter: Select | q: Quit") | dim | center + }) | border; + }); +} + +Component UnifiedLayout::CreateChatPanel() { + // Create a simplified chat interface that integrates with the main layout + static std::string input_message; + auto input_component = Input(&input_message, "Type your message..."); + + auto send_button = Button("Send", [&] { + if (input_message.empty()) return; + + // Handle chat commands + if (input_message == "/exit") { + screen_.Exit(); + return; + } + + // For now, just clear the input + // TODO: Integrate with agent service + input_message.clear(); + }); + + // Handle Enter key + input_component = CatchEvent(input_component, [&](Event event) { + if (event == Event::Return) { + if (input_message.empty()) return true; + + // Handle chat commands + if (input_message == "/exit") { + screen_.Exit(); + return true; + } + + // For now, just clear the input + input_message.clear(); + return true; + } + return false; + }); + + auto container = Container::Vertical({ + input_component, + send_button + }); + + return Renderer(container, [&] { + return vbox({ + text("šŸ¤– AI Chat") | bold | center, + separator(), + text("Chat functionality integrated into unified layout") | center | dim, + separator(), + hbox({ + text("You: ") | bold, + input_component->Render() | flex, + text(" "), + send_button->Render() + }), + separator(), + text("Commands: /exit, /clear, /help") | dim | center + }) | border; + }); +} + +Component UnifiedLayout::CreateStatusPanel() { + return Renderer([&] { + std::string rom_info = rom_context_ ? + absl::StrFormat("ROM: %s | Size: %d bytes", + rom_context_->title(), rom_context_->size()) : + "No ROM loaded"; + + return vbox({ + text("šŸ“Š Status") | bold | center, + separator(), + text(rom_info) | color(Color::Green), + separator(), + text("Panel: ") | bold, + text(absl::StrFormat("%s", + state_.active_main_panel == PanelType::kMainMenu ? "Main Menu" : + state_.active_main_panel == PanelType::kChat ? "Chat" : + state_.active_main_panel == PanelType::kHexViewer ? "Hex Viewer" : + "Other")), + separator(), + text("Layout: ") | bold, + text(absl::StrFormat("Chat: %s | Status: %s", + config_.show_chat ? "ON" : "OFF", + config_.show_status ? "ON" : "OFF")), + separator(), + text("Press 'h' for help, 'q' to quit") | dim | center + }) | border; + }); +} + +Component UnifiedLayout::CreateToolsPanel() { + static int selected = 0; + static const std::vector tools = { + "šŸ”§ ROM Tools", + "šŸŽØ Graphics Tools", + "šŸ“ TODO Manager", + "āš™ļø Settings", + "ā“ Help" + }; + + auto menu = Menu(&tools, &selected); + + return Renderer(menu, [&] { + return vbox({ + text("šŸ› ļø Tools") | bold | center, + separator(), + menu->Render(), + separator(), + text("Select a tool category") | dim | center + }) | border; + }); +} + +Component UnifiedLayout::CreateHexViewerPanel() { + static int offset = 0; + const int lines_to_show = 20; + + return Renderer([&] { + if (!rom_context_) { + return vbox({ + text("šŸ” Hex Viewer") | bold | center, + separator(), + text("No ROM loaded") | center | color(Color::Red), + separator(), + text("Load a ROM to view hex data") | center | dim + }) | border; + } + + std::vector rows; + for (int i = 0; i < lines_to_show; ++i) { + int current_offset = offset + (i * 16); + if (current_offset >= static_cast(rom_context_->size())) { + break; + } + + Elements row; + row.push_back(text(absl::StrFormat("0x%08X: ", current_offset)) | color(Color::Yellow)); + + for (int j = 0; j < 16; ++j) { + if (current_offset + j < static_cast(rom_context_->size())) { + row.push_back(text(absl::StrFormat("%02X ", rom_context_->vector()[current_offset + j]))); + } else { + row.push_back(text(" ")); + } + } + + row.push_back(separator()); + + for (int j = 0; j < 16; ++j) { + if (current_offset + j < static_cast(rom_context_->size())) { + char c = rom_context_->vector()[current_offset + j]; + row.push_back(text(std::isprint(c) ? std::string(1, c) : ".")); + } else { + row.push_back(text(" ")); + } + } + + rows.push_back(hbox(row)); + } + + return vbox({ + text("šŸ” Hex Viewer") | bold | center, + separator(), + vbox(rows) | frame | flex, + separator(), + text(absl::StrFormat("Offset: 0x%08X", offset)) | color(Color::Cyan), + separator(), + text("↑/↓: Scroll | q: Back") | dim | center + }) | border; + }); +} + +Component UnifiedLayout::CreatePaletteEditorPanel() { + return Renderer([&] { + return vbox({ + text("šŸŽØ Palette Editor") | bold | center, + separator(), + text("Palette editing functionality") | center, + text("coming soon...") | center | dim, + separator(), + text("This panel will allow editing") | center, + text("color palettes from the ROM") | center, + separator(), + text("Press 'q' to go back") | dim | center + }) | border; + }); +} + +Component UnifiedLayout::CreateTodoManagerPanel() { + return Renderer([&] { + return vbox({ + text("šŸ“ TODO Manager") | bold | center, + separator(), + text("TODO management functionality") | center, + text("coming soon...") | center | dim, + separator(), + text("This panel will integrate with") | center, + text("the existing TODO manager") | center, + separator(), + text("Press 'q' to go back") | dim | center + }) | border; + }); +} + +Component UnifiedLayout::CreateRomToolsPanel() { + static int selected = 0; + static const std::vector tools = { + "Apply Asar Patch", + "Apply BPS Patch", + "Extract Symbols", + "Validate Assembly", + "Generate Save File", + "Back" + }; + + auto menu = Menu(&tools, &selected); + + return Renderer(menu, [&] { + return vbox({ + text("šŸ”§ ROM Tools") | bold | center, + separator(), + menu->Render(), + separator(), + text("Select a ROM tool") | dim | center + }) | border; + }); +} + +Component UnifiedLayout::CreateGraphicsToolsPanel() { + static int selected = 0; + static const std::vector tools = { + "Palette Editor", + "Hex Viewer", + "Back" + }; + + auto menu = Menu(&tools, &selected); + + return Renderer(menu, [&] { + return vbox({ + text("šŸŽØ Graphics Tools") | bold | center, + separator(), + menu->Render(), + separator(), + text("Select a graphics tool") | dim | center + }) | border; + }); +} + +Component UnifiedLayout::CreateSettingsPanel() { + return Renderer([&] { + return vbox({ + text("āš™ļø Settings") | bold | center, + separator(), + text("Settings panel") | center, + text("coming soon...") | center | dim, + separator(), + text("This panel will contain") | center, + text("application settings") | center, + separator(), + text("Press 'q' to go back") | dim | center + }) | border; + }); +} + +Component UnifiedLayout::CreateHelpPanel() { + return Renderer([&] { + return vbox({ + text("ā“ Help") | bold | center, + separator(), + text("Z3ED Unified Layout Help") | center, + separator(), + text("Global Shortcuts:") | bold, + text(" q - Quit application"), + text(" h - Show this help"), + text(" c - Toggle chat panel"), + text(" s - Toggle status panel"), + text(" t - Switch to tools"), + text(" m - Switch to main menu"), + separator(), + text("Navigation:") | bold, + text(" ↑/↓ - Navigate menus"), + text(" Enter - Select item"), + text(" Tab - Switch panels"), + separator(), + text("Press 'q' to go back") | dim | center + }) | border; + }); +} + +Component UnifiedLayout::CreateUnifiedLayout() { + top_layout_ = CreateTopLayout(); + bottom_layout_ = CreateBottomLayout(); + + // Create vertical split between top and bottom + return ResizableSplitBottom( + top_layout_, + bottom_layout_, + &config_.bottom_panel_height + ); +} + +Component UnifiedLayout::CreateTopLayout() { + // Left panel: Main menu or tools + Component left_panel; + switch (state_.active_main_panel) { + case PanelType::kMainMenu: + left_panel = main_menu_panel_; + break; + case PanelType::kHexViewer: + left_panel = hex_viewer_panel_; + break; + case PanelType::kPaletteEditor: + left_panel = palette_editor_panel_; + break; + case PanelType::kTodoManager: + left_panel = todo_manager_panel_; + break; + case PanelType::kRomTools: + left_panel = rom_tools_panel_; + break; + case PanelType::kGraphicsTools: + left_panel = graphics_tools_panel_; + break; + case PanelType::kSettings: + left_panel = settings_panel_; + break; + case PanelType::kHelp: + left_panel = help_panel_; + break; + default: + left_panel = main_menu_panel_; + break; + } + + // Right panel: Status or tools + Component right_panel; + if (config_.show_status) { + right_panel = status_panel_; + } else { + right_panel = tools_panel_; + } + + // Create horizontal split between left and right + return ResizableSplitRight( + left_panel, + right_panel, + &config_.right_panel_width + ); +} + +Component UnifiedLayout::CreateBottomLayout() { + if (!config_.show_chat) { + return Renderer([] { return text(""); }); + } + + return chat_panel_; +} + +bool UnifiedLayout::HandleGlobalEvents(const Event& event) { + // Global shortcuts + if (event == Event::Character('q')) { + screen_.Exit(); + return true; + } + + if (event == Event::Character('h')) { + SwitchMainPanel(PanelType::kHelp); + return true; + } + + if (event == Event::Character('c')) { + ToggleChat(); + return true; + } + + if (event == Event::Character('s')) { + ToggleStatus(); + return true; + } + + if (event == Event::Character('t')) { + SwitchMainPanel(PanelType::kRomTools); + return true; + } + + if (event == Event::Character('m')) { + SwitchMainPanel(PanelType::kMainMenu); + return true; + } + + return false; +} + +bool UnifiedLayout::HandlePanelEvents(const Event& event) { + // Panel-specific event handling + return false; +} + +Element UnifiedLayout::RenderPanelHeader(PanelType panel) { + std::string title; + switch (panel) { + case PanelType::kMainMenu: + title = "šŸŽ® Main Menu"; + break; + case PanelType::kChat: + title = "šŸ¤– AI Chat"; + break; + case PanelType::kStatus: + title = "šŸ“Š Status"; + break; + case PanelType::kTools: + title = "šŸ› ļø Tools"; + break; + case PanelType::kHexViewer: + title = "šŸ” Hex Viewer"; + break; + case PanelType::kPaletteEditor: + title = "šŸŽØ Palette Editor"; + break; + case PanelType::kTodoManager: + title = "šŸ“ TODO Manager"; + break; + case PanelType::kRomTools: + title = "šŸ”§ ROM Tools"; + break; + case PanelType::kGraphicsTools: + title = "šŸŽØ Graphics Tools"; + break; + case PanelType::kSettings: + title = "āš™ļø Settings"; + break; + case PanelType::kHelp: + title = "ā“ Help"; + break; + } + + return text(title) | bold | center; +} + +Element UnifiedLayout::RenderStatusBar() { + return hbox({ + text(absl::StrFormat("Panel: %s", + state_.active_main_panel == PanelType::kMainMenu ? "Main" : "Other")) | color(Color::Cyan), + filler(), + text(absl::StrFormat("ROM: %s", + state_.current_rom_file.empty() ? "None" : state_.current_rom_file)) | color(Color::Green), + filler(), + text("q: Quit | h: Help | c: Chat | s: Status") | dim + }); +} + +} // namespace cli +} // namespace yaze diff --git a/src/cli/tui/unified_layout.h b/src/cli/tui/unified_layout.h new file mode 100644 index 00000000..2cc16f57 --- /dev/null +++ b/src/cli/tui/unified_layout.h @@ -0,0 +1,140 @@ +#ifndef YAZE_CLI_TUI_UNIFIED_LAYOUT_H +#define YAZE_CLI_TUI_UNIFIED_LAYOUT_H + +#include +#include +#include +#include +#include +#include + +#include "app/rom.h" +#include "cli/tui/chat_tui.h" + +namespace yaze { +namespace cli { +namespace tui { +class ChatTUI; +} // namespace tui +} // namespace cli +} // namespace yaze + +namespace yaze { +namespace cli { + +// Panel types for the unified layout +enum class PanelType { + kMainMenu, + kChat, + kStatus, + kTools, + kHexViewer, + kPaletteEditor, + kTodoManager, + kRomTools, + kGraphicsTools, + kSettings, + kHelp +}; + +// Layout configuration +struct LayoutConfig { + int left_panel_width = 30; // Menu/Tools panel + int right_panel_width = 40; // Status/Info panel + int bottom_panel_height = 15; // Chat panel + bool show_chat = true; + bool show_status = true; + bool show_tools = true; +}; + +// Panel state management +struct PanelState { + PanelType active_main_panel = PanelType::kMainMenu; + PanelType active_tool_panel = PanelType::kRomTools; + bool chat_focused = false; + bool status_expanded = false; + std::string current_rom_file; + std::string current_error; +}; + +class UnifiedLayout { + public: + explicit UnifiedLayout(Rom* rom_context = nullptr); + + // Main interface + void Run(); + void SetRomContext(Rom* rom_context); + + // Panel management + void SwitchMainPanel(PanelType panel); + void SwitchToolPanel(PanelType panel); + void ToggleChat(); + void ToggleStatus(); + + // Configuration + void SetLayoutConfig(const LayoutConfig& config); + LayoutConfig GetLayoutConfig() const { return config_; } + + private: + // Component creation + ftxui::Component CreateMainMenuPanel(); + ftxui::Component CreateChatPanel(); + ftxui::Component CreateStatusPanel(); + ftxui::Component CreateToolsPanel(); + ftxui::Component CreateHexViewerPanel(); + ftxui::Component CreatePaletteEditorPanel(); + ftxui::Component CreateTodoManagerPanel(); + ftxui::Component CreateRomToolsPanel(); + ftxui::Component CreateGraphicsToolsPanel(); + ftxui::Component CreateSettingsPanel(); + ftxui::Component CreateHelpPanel(); + + // Layout assembly + ftxui::Component CreateUnifiedLayout(); + ftxui::Component CreateTopLayout(); + ftxui::Component CreateBottomLayout(); + + // Event handling + bool HandleGlobalEvents(const ftxui::Event& event); + bool HandlePanelEvents(const ftxui::Event& event); + + // Rendering + ftxui::Element RenderPanelHeader(PanelType panel); + ftxui::Element RenderStatusBar(); + + // State + ftxui::ScreenInteractive screen_; + Rom* rom_context_; + LayoutConfig config_; + PanelState state_; + + // Components + std::unique_ptr chat_tui_; + + // Panel components (cached for performance) + ftxui::Component main_menu_panel_; + ftxui::Component chat_panel_; + ftxui::Component status_panel_; + ftxui::Component tools_panel_; + ftxui::Component hex_viewer_panel_; + ftxui::Component palette_editor_panel_; + ftxui::Component todo_manager_panel_; + ftxui::Component rom_tools_panel_; + ftxui::Component graphics_tools_panel_; + ftxui::Component settings_panel_; + ftxui::Component help_panel_; + + // Layout components + ftxui::Component unified_layout_; + ftxui::Component top_layout_; + ftxui::Component bottom_layout_; + + // Event handlers + std::function global_event_handler_; + std::function panel_event_handler_; +}; + +} // namespace cli +} // namespace yaze + +#endif // YAZE_CLI_TUI_UNIFIED_LAYOUT_H diff --git a/src/cli/z3ed.cmake b/src/cli/z3ed.cmake index b7c5de28..030a06d1 100644 --- a/src/cli/z3ed.cmake +++ b/src/cli/z3ed.cmake @@ -48,6 +48,9 @@ add_executable( cli/cli_main.cc cli/cli.cc cli/tui/tui.cc + cli/tui/unified_layout.cc + cli/tui/enhanced_chat_component.cc + cli/tui/enhanced_status_panel.cc cli/handlers/compress.cc cli/handlers/patch.cc cli/handlers/tile16_transfer.cc