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:
263
src/cli/tui/enhanced_chat_component.cc
Normal file
263
src/cli/tui/enhanced_chat_component.cc
Normal 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
|
||||
84
src/cli/tui/enhanced_chat_component.h
Normal file
84
src/cli/tui/enhanced_chat_component.h
Normal 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
|
||||
251
src/cli/tui/enhanced_status_panel.cc
Normal file
251
src/cli/tui/enhanced_status_panel.cc
Normal 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
|
||||
106
src/cli/tui/enhanced_status_panel.h
Normal file
106
src/cli/tui/enhanced_status_panel.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
569
src/cli/tui/unified_layout.cc
Normal file
569
src/cli/tui/unified_layout.cc
Normal 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
|
||||
140
src/cli/tui/unified_layout.h
Normal file
140
src/cli/tui/unified_layout.h
Normal 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
|
||||
Reference in New Issue
Block a user