revamp tui menu

This commit is contained in:
scawful
2025-10-05 01:27:03 -04:00
parent fb7f443fe3
commit 9358e95116
4 changed files with 515 additions and 379 deletions

View File

@@ -16,6 +16,8 @@
#include "cli/modern_cli.h"
#include "cli/tui/command_palette.h"
#include "cli/z3ed_ascii_logo.h"
#include "cli/service/agent/simple_chat_session.h"
#include "cli/service/agent/conversational_agent_service.h"
namespace yaze {
namespace cli {
@@ -802,29 +804,23 @@ void MainMenuComponent(ftxui::ScreenInteractive &screen) {
case MainMenuEntry::kLoadRom:
SwitchComponents(screen, LayoutID::kLoadRom);
return true;
case MainMenuEntry::kApplyAsarPatch:
SwitchComponents(screen, LayoutID::kApplyAsarPatch);
case MainMenuEntry::kAIAgentChat:
SwitchComponents(screen, LayoutID::kAIAgentChat);
return true;
case MainMenuEntry::kApplyBpsPatch:
SwitchComponents(screen, LayoutID::kApplyBpsPatch);
case MainMenuEntry::kTodoManager:
SwitchComponents(screen, LayoutID::kTodoManager);
return true;
case MainMenuEntry::kExtractSymbols:
SwitchComponents(screen, LayoutID::kExtractSymbols);
case MainMenuEntry::kRomTools:
SwitchComponents(screen, LayoutID::kRomTools);
return true;
case MainMenuEntry::kValidateAssembly:
SwitchComponents(screen, LayoutID::kValidateAssembly);
case MainMenuEntry::kGraphicsTools:
SwitchComponents(screen, LayoutID::kGraphicsTools);
return true;
case MainMenuEntry::kGenerateSaveFile:
SwitchComponents(screen, LayoutID::kGenerateSaveFile);
case MainMenuEntry::kTestingTools:
SwitchComponents(screen, LayoutID::kTestingTools);
return true;
case MainMenuEntry::kPaletteEditor:
SwitchComponents(screen, LayoutID::kPaletteEditor);
return true;
case MainMenuEntry::kHexViewer:
SwitchComponents(screen, LayoutID::kHexViewer);
return true;
case MainMenuEntry::kCommandPalette:
SwitchComponents(screen, LayoutID::kCommandPalette);
case MainMenuEntry::kSettings:
SwitchComponents(screen, LayoutID::kSettings);
return true;
case MainMenuEntry::kHelp:
SwitchComponents(screen, LayoutID::kHelp);
@@ -857,6 +853,74 @@ void ShowMain() {
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: {
// TODO: Implement TODO manager TUI
app_context.error_message = "TODO Manager TUI coming soon - use CLI commands for now";
app_context.current_layout = LayoutID::kError;
} 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;
@@ -895,9 +959,9 @@ void ShowMain() {
auto error_renderer = Renderer(error_button, [&] {
return vbox({
text("Error") | center | bold | color(Color::Red),
text("⚠️ Error") | center | bold | color(Color::Red),
separator(),
text(app_context.error_message) | color(Color::Yellow),
text(app_context.error_message) | color(Color::Yellow) | center,
separator(),
error_button->Render() | center
}) | center | border;

View File

@@ -16,35 +16,37 @@ namespace yaze {
*/
namespace cli {
const std::vector<std::string> kMainMenuEntries = {
"Load ROM",
"Apply Asar Patch",
"Apply BPS Patch",
"Extract Symbols",
"Validate Assembly",
"Generate Save File",
"Palette Editor",
"Hex Viewer",
"Command Palette",
"Help",
"Exit",
"🎮 Load ROM / Quick Start",
"🤖 AI Agent Chat",
"📝 TODO Manager",
"🔧 ROM Tools",
"🎨 Graphics & Palettes",
"🧪 Testing & Validation",
"⚙️ Settings",
"❓ Help & Documentation",
"🚪 Exit",
};
enum class MainMenuEntry {
kLoadRom,
kApplyAsarPatch,
kApplyBpsPatch,
kExtractSymbols,
kValidateAssembly,
kGenerateSaveFile,
kPaletteEditor,
kHexViewer,
kCommandPalette,
kAIAgentChat,
kTodoManager,
kRomTools,
kGraphicsTools,
kTestingTools,
kSettings,
kHelp,
kExit,
};
enum class LayoutID {
kLoadRom,
kAIAgentChat,
kTodoManager,
kRomTools,
kGraphicsTools,
kTestingTools,
kSettings,
kApplyAsarPatch,
kApplyBpsPatch,
kExtractSymbols,

View File

@@ -0,0 +1,289 @@
#include "cli/tui.h"
#include <ftxui/component/component.hpp>
#include <ftxui/component/screen_interactive.hpp>
#include <ftxui/dom/elements.hpp>
#include "absl/strings/str_format.h"
#include "cli/service/agent/simple_chat_session.h"
namespace yaze {
namespace cli {
using namespace ftxui;
// Enhanced main menu with better organization and icons
Component EnhancedMainMenu(ScreenInteractive& screen, int& selected) {
auto menu_renderer = Renderer([&] {
Elements menu_items;
for (size_t i = 0; i < kMainMenuEntries.size(); ++i) {
auto item = text(kMainMenuEntries[i]);
if (i == selected) {
item = item | bold | color(Color::Cyan) | inverted;
} else {
item = item | color(Color::GreenLight);
}
menu_items.push_back(item);
}
// Show ROM status
std::string rom_status = app_context.rom.is_loaded()
? absl::StrFormat("📀 ROM: %s", app_context.rom.title())
: "⚠️ No ROM loaded";
return vbox({
// Header
text("Z3ED - Yet Another Zelda3 Editor") | bold | center | color(Color::Yellow),
text("v0.3.0") | center | color(Color::GrayDark),
separator(),
// ROM status
text(rom_status) | center | color(app_context.rom.is_loaded() ? Color::Green : Color::Red),
separator(),
// Menu
vbox(menu_items) | flex,
separator(),
// Footer with controls
hbox({
text("Navigate: ") | color(Color::GrayLight),
text("↑↓/jk") | bold | color(Color::Cyan),
text(" | Select: ") | color(Color::GrayLight),
text("Enter") | bold | color(Color::Cyan),
text(" | Quit: ") | color(Color::GrayLight),
text("q") | bold | color(Color::Red),
}) | center,
}) | border | center;
});
return CatchEvent(menu_renderer, [&](Event event) {
if (event == Event::ArrowDown || event == Event::Character('j')) {
selected = (selected + 1) % kMainMenuEntries.size();
return true;
}
if (event == Event::ArrowUp || event == Event::Character('k')) {
selected = (selected - 1 + kMainMenuEntries.size()) % kMainMenuEntries.size();
return true;
}
if (event == Event::Character('q')) {
app_context.current_layout = LayoutID::kExit;
screen.ExitLoopClosure()();
return true;
}
if (event == Event::Return) {
screen.ExitLoopClosure()();
return true;
}
return false;
});
}
// Quick ROM loader with recent files
Component QuickRomLoader(ScreenInteractive& screen) {
static std::string rom_path;
static std::vector<std::string> recent_files;
static int selected_recent = 0;
// Load recent files (TODO: from actual recent files manager)
if (recent_files.empty()) {
recent_files = {
"~/roms/zelda3.sfc",
"~/roms/alttp_modified.sfc",
"~/roms/custom_hack.sfc",
};
}
auto input = Input(&rom_path, "Enter ROM path or select below");
auto load_button = Button("Load ROM", [&] {
if (!rom_path.empty()) {
auto status = app_context.rom.LoadFromFile(rom_path);
if (status.ok()) {
app_context.current_layout = LayoutID::kMainMenu;
screen.ExitLoopClosure()();
} else {
app_context.error_message = std::string(status.message());
app_context.current_layout = LayoutID::kError;
screen.ExitLoopClosure()();
}
}
});
auto back_button = Button("Back", [&] {
app_context.current_layout = LayoutID::kMainMenu;
screen.ExitLoopClosure()();
});
auto container = Container::Vertical({input, load_button, back_button});
return Renderer(container, [&] {
Elements recent_elements;
for (size_t i = 0; i < recent_files.size(); ++i) {
auto item = text(recent_files[i]);
if (i == selected_recent) {
item = item | bold | inverted;
}
recent_elements.push_back(item);
}
return vbox({
text("🎮 Load ROM") | bold | center | color(Color::Cyan),
separator(),
hbox({
text("Path: "),
input->Render() | flex,
}),
separator(),
text("Recent ROMs:") | bold,
vbox(recent_elements),
separator(),
hbox({
load_button->Render(),
text(" "),
back_button->Render(),
}) | center,
separator(),
text("Tip: Press Enter to load, Tab to cycle, Esc to cancel") | dim | center,
}) | border | center | size(WIDTH, GREATER_THAN, 60);
});
}
// Agent chat interface in TUI
Component AgentChatTUI(ScreenInteractive& screen) {
static std::vector<std::string> messages;
static std::string input_text;
static bool is_processing = false;
auto input = Input(&input_text, "Type your message...");
auto send_button = Button("Send", [&] {
if (!input_text.empty() && !is_processing) {
messages.push_back("You: " + input_text);
// TODO: Actually call agent service
is_processing = true;
messages.push_back("Agent: [Processing...]");
input_text.clear();
// Simulate async response
// In real implementation, use SimpleChatSession
is_processing = false;
messages.back() = "Agent: I can help with that!";
}
});
auto back_button = Button("Back", [&] {
app_context.current_layout = LayoutID::kMainMenu;
screen.ExitLoopClosure()();
});
auto container = Container::Vertical({input, send_button, back_button});
return Renderer(container, [&] {
Elements message_elements;
for (const auto& msg : messages) {
Color msg_color = (msg.rfind("You:", 0) == 0) ? Color::Cyan : Color::GreenLight;
message_elements.push_back(text(msg) | color(msg_color));
}
return vbox({
text("🤖 AI Agent Chat") | bold | center | color(Color::Yellow),
text(app_context.rom.is_loaded()
? absl::StrFormat("ROM: %s", app_context.rom.title())
: "No ROM loaded") | center | dim,
separator(),
// Chat history
vbox(message_elements) | flex | vscroll_indicator | frame,
separator(),
// Input area
hbox({
text("Message: "),
input->Render() | flex,
}),
hbox({
send_button->Render(),
text(" "),
back_button->Render(),
}) | center,
separator(),
text("Shortcuts: Enter=Send | Ctrl+C=Cancel | Esc=Back") | dim | center,
}) | border | flex;
});
}
// ROM tools submenu
Component RomToolsMenu(ScreenInteractive& screen) {
static int selected = 0;
static const std::vector<std::string> tools = {
"🔍 ROM Info & Analysis",
"🔧 Apply Asar Patch",
"📦 Apply BPS Patch",
"🏷️ Extract Symbols",
"✅ Validate Assembly",
"💾 Generate Save File",
"⬅️ Back to Main Menu",
};
auto menu = Menu(&tools, &selected);
return CatchEvent(menu, [&](Event event) {
if (event == Event::Return) {
switch (selected) {
case 0: /* ROM Info */ break;
case 1: app_context.current_layout = LayoutID::kApplyAsarPatch; break;
case 2: app_context.current_layout = LayoutID::kApplyBpsPatch; break;
case 3: app_context.current_layout = LayoutID::kExtractSymbols; break;
case 4: app_context.current_layout = LayoutID::kValidateAssembly; break;
case 5: app_context.current_layout = LayoutID::kGenerateSaveFile; break;
case 6: app_context.current_layout = LayoutID::kMainMenu; break;
}
screen.ExitLoopClosure()();
return true;
}
return false;
});
}
// Graphics tools submenu
Component GraphicsToolsMenu(ScreenInteractive& screen) {
static int selected = 0;
static const std::vector<std::string> tools = {
"🎨 Palette Editor",
"🔢 Hex Viewer",
"🖼️ Graphics Sheet Viewer",
"📊 Color Analysis",
"⬅️ Back to Main Menu",
};
auto menu = Menu(&tools, &selected);
return CatchEvent(menu, [&](Event event) {
if (event == Event::Return) {
switch (selected) {
case 0: app_context.current_layout = LayoutID::kPaletteEditor; break;
case 1: app_context.current_layout = LayoutID::kHexViewer; break;
case 2: /* Graphics viewer */ break;
case 3: /* Color analysis */ break;
case 4: app_context.current_layout = LayoutID::kMainMenu; break;
}
screen.ExitLoopClosure()();
return true;
}
return false;
});
}
} // namespace cli
} // namespace yaze