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.
This commit is contained in:
scawful
2025-10-06 14:30:15 -04:00
parent 67a4a82e2e
commit 673201e4fd
8 changed files with 1435 additions and 139 deletions

View File

@@ -0,0 +1,263 @@
#include "cli/tui/enhanced_chat_component.h"
#include <ftxui/component/component.hpp>
#include <ftxui/component/screen_interactive.hpp>
#include <ftxui/dom/elements.hpp>
#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<int>(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<int>(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<Element> messages;
for (const auto& [sender, message] : chat_history_) {
messages.push_back(RenderChatMessage(sender, message));
}
return vbox(messages) | flex;
}
} // namespace cli
} // namespace yaze

View File

@@ -0,0 +1,84 @@
#ifndef YAZE_CLI_TUI_ENHANCED_CHAT_COMPONENT_H
#define YAZE_CLI_TUI_ENHANCED_CHAT_COMPONENT_H
#include <ftxui/component/component.hpp>
#include <ftxui/dom/elements.hpp>
#include <memory>
#include <string>
#include <vector>
#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<std::pair<std::string, std::string>> 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<bool(const ftxui::Event&)> input_event_handler_;
std::function<bool(const ftxui::Event&)> history_event_handler_;
};
} // namespace cli
} // namespace yaze
#endif // YAZE_CLI_TUI_ENHANCED_CHAT_COMPONENT_H

View File

@@ -0,0 +1,251 @@
#include "cli/tui/enhanced_status_panel.h"
#include <ftxui/component/component.hpp>
#include <ftxui/dom/elements.hpp>
#include <chrono>
#include <iomanip>
#include <sstream>
#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

View File

@@ -0,0 +1,106 @@
#ifndef YAZE_CLI_TUI_ENHANCED_STATUS_PANEL_H
#define YAZE_CLI_TUI_ENHANCED_STATUS_PANEL_H
#include <ftxui/component/component.hpp>
#include <ftxui/dom/elements.hpp>
#include <memory>
#include <string>
#include <vector>
#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<bool(const ftxui::Event&)> status_event_handler_;
};
} // namespace cli
} // namespace yaze
#endif // YAZE_CLI_TUI_ENHANCED_STATUS_PANEL_H

View File

@@ -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<std::string> 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<std::string> 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

View File

@@ -0,0 +1,569 @@
#include "cli/tui/unified_layout.h"
#include <ftxui/component/component.hpp>
#include <ftxui/component/screen_interactive.hpp>
#include <ftxui/dom/elements.hpp>
#include <ftxui/screen/terminal.hpp>
#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<tui::ChatTUI>(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<std::string> 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<Element> rows;
for (int i = 0; i < lines_to_show; ++i) {
int current_offset = offset + (i * 16);
if (current_offset >= static_cast<int>(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<int>(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<int>(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<std::string> 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<std::string> 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

View File

@@ -0,0 +1,140 @@
#ifndef YAZE_CLI_TUI_UNIFIED_LAYOUT_H
#define YAZE_CLI_TUI_UNIFIED_LAYOUT_H
#include <ftxui/component/component.hpp>
#include <ftxui/dom/elements.hpp>
#include <ftxui/screen/screen.hpp>
#include <memory>
#include <string>
#include <vector>
#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<tui::ChatTUI> 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<bool(const ftxui::Event&)> global_event_handler_;
std::function<bool(const ftxui::Event&)> panel_event_handler_;
};
} // namespace cli
} // namespace yaze
#endif // YAZE_CLI_TUI_UNIFIED_LAYOUT_H

View File

@@ -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