feat(build-system): enhance CMake configuration and introduce new utility files
- Refactored CMakeLists.txt to streamline project configuration and improve readability. - Introduced new utility functions in `utils.cmake` for setting compiler flags and managing dependencies. - Added `dependencies.cmake` to centralize third-party dependency management, enhancing modularity. - Updated CI workflows to include new build options and improved logging for better feedback during configuration. - Implemented precompiled headers in various libraries to speed up compilation times. Benefits: - Improved maintainability and clarity of the build system. - Enhanced build performance through precompiled headers. - Streamlined dependency management for easier integration of third-party libraries.
This commit is contained in:
1199
src/CMakeLists.txt
1199
src/CMakeLists.txt
File diff suppressed because it is too large
Load Diff
@@ -1,288 +1,77 @@
|
||||
include(app/core/core_library.cmake)
|
||||
include(app/editor/editor_library.cmake)
|
||||
include(app/gfx/gfx_library.cmake)
|
||||
include(app/gui/gui_library.cmake)
|
||||
include(app/zelda3/zelda3_library.cmake)
|
||||
# This file defines the main `yaze` application executable.
|
||||
|
||||
if (APPLE)
|
||||
add_executable(
|
||||
yaze
|
||||
MACOSX_BUNDLE
|
||||
app/main.cc
|
||||
# Bundled Resources
|
||||
${YAZE_RESOURCE_FILES}
|
||||
)
|
||||
add_executable(yaze MACOSX_BUNDLE app/main.cc ${YAZE_RESOURCE_FILES})
|
||||
|
||||
# Add the app icon to the macOS bundle
|
||||
set(ICON_FILE "${CMAKE_SOURCE_DIR}/assets/yaze.icns")
|
||||
target_sources(yaze PRIVATE ${ICON_FILE})
|
||||
set_source_files_properties(${ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
|
||||
# Set macOS bundle properties
|
||||
set_target_properties(yaze PROPERTIES
|
||||
MACOSX_BUNDLE_ICON_FILE "yaze.icns"
|
||||
MACOSX_BUNDLE_BUNDLE_NAME "Yaze"
|
||||
MACOSX_BUNDLE_EXECUTABLE_NAME "yaze"
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER "com.scawful.yaze"
|
||||
MACOSX_BUNDLE_INFO_STRING "Yet Another Zelda3 Editor"
|
||||
MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_VERSION}"
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}"
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_VERSION}"
|
||||
MACOSX_BUNDLE_COPYRIGHT "Copyright © 2024 scawful. All rights reserved."
|
||||
)
|
||||
else()
|
||||
# Windows/Linux builds
|
||||
add_executable(
|
||||
yaze
|
||||
app/main.cc
|
||||
)
|
||||
|
||||
# Add asset files for Windows/Linux builds
|
||||
add_executable(yaze app/main.cc)
|
||||
if(WIN32 OR UNIX)
|
||||
target_sources(yaze PRIVATE ${YAZE_RESOURCE_FILES})
|
||||
|
||||
# Set up asset deployment for Visual Studio
|
||||
if(WIN32)
|
||||
foreach(ASSET_FILE ${YAZE_RESOURCE_FILES})
|
||||
file(RELATIVE_PATH ASSET_REL_PATH "${CMAKE_SOURCE_DIR}/assets" ${ASSET_FILE})
|
||||
get_filename_component(ASSET_DIR ${ASSET_REL_PATH} DIRECTORY)
|
||||
|
||||
set_source_files_properties(${ASSET_FILE}
|
||||
PROPERTIES
|
||||
VS_DEPLOYMENT_CONTENT 1
|
||||
VS_DEPLOYMENT_LOCATION "assets/${ASSET_DIR}"
|
||||
)
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_include_directories(
|
||||
yaze PUBLIC
|
||||
${CMAKE_SOURCE_DIR}/src/lib/
|
||||
${CMAKE_SOURCE_DIR}/src/app/
|
||||
${CMAKE_SOURCE_DIR}/src/lib/asar/src
|
||||
${CMAKE_SOURCE_DIR}/src/lib/asar/src/asar
|
||||
${CMAKE_SOURCE_DIR}/src/lib/asar/src/asar-dll-bindings/c
|
||||
${CMAKE_SOURCE_DIR}/incl/
|
||||
${CMAKE_SOURCE_DIR}/src/
|
||||
${CMAKE_SOURCE_DIR}/src/lib/imgui_test_engine
|
||||
${CMAKE_SOURCE_DIR}/third_party/httplib
|
||||
${SDL2_INCLUDE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
target_include_directories(yaze PUBLIC
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_SOURCE_DIR}/incl
|
||||
${PROJECT_BINARY_DIR}
|
||||
)
|
||||
|
||||
target_sources(yaze PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/yaze_config.h)
|
||||
set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/yaze_config.h PROPERTIES GENERATED TRUE)
|
||||
|
||||
# 4) Tell the IDE it’s generated
|
||||
set_source_files_properties(
|
||||
${CMAKE_CURRENT_BINARY_DIR}/yaze_config.h
|
||||
PROPERTIES GENERATED TRUE
|
||||
# Link modular libraries
|
||||
target_link_libraries(yaze PRIVATE
|
||||
yaze_editor
|
||||
yaze_emulator
|
||||
yaze_agent
|
||||
absl::failure_signal_handler
|
||||
absl::flags
|
||||
absl::flags_parse
|
||||
)
|
||||
|
||||
# (Optional) put it under a neat filter in VS Solution Explorer
|
||||
source_group(TREE ${CMAKE_CURRENT_BINARY_DIR}
|
||||
FILES ${CMAKE_CURRENT_BINARY_DIR}/yaze_config.h)
|
||||
|
||||
# Conditionally add PNG include dirs if available
|
||||
if(PNG_FOUND)
|
||||
target_include_directories(yaze PUBLIC ${PNG_INCLUDE_DIRS})
|
||||
if(YAZE_BUILD_TESTS AND TARGET yaze_test_support)
|
||||
target_link_libraries(yaze PRIVATE yaze_test_support)
|
||||
endif()
|
||||
|
||||
# Conditionally link nfd if available
|
||||
if(YAZE_HAS_NFD)
|
||||
target_link_libraries(yaze PRIVATE nfd)
|
||||
target_compile_definitions(yaze PRIVATE YAZE_ENABLE_NFD=1)
|
||||
else()
|
||||
target_compile_definitions(yaze PRIVATE YAZE_ENABLE_NFD=0)
|
||||
endif()
|
||||
|
||||
if(YAZE_USE_MODULAR_BUILD)
|
||||
set(_yaze_modular_links yaze_editor)
|
||||
|
||||
if(TARGET yaze_agent)
|
||||
list(APPEND _yaze_modular_links yaze_agent)
|
||||
endif()
|
||||
|
||||
if(YAZE_BUILD_EMU AND TARGET yaze_emulator)
|
||||
list(APPEND _yaze_modular_links yaze_emulator)
|
||||
endif()
|
||||
|
||||
# Link once against the editor library and allow its PUBLIC dependencies
|
||||
# (core, gfx, util, absl, etc.) to propagate transitively. This avoids
|
||||
# duplicate static archives on the link line while keeping absl symbols
|
||||
# available for main and other entry points.
|
||||
target_link_libraries(yaze PRIVATE ${_yaze_modular_links})
|
||||
else()
|
||||
target_link_libraries(yaze PRIVATE yaze_core)
|
||||
endif()
|
||||
|
||||
# Enable policy framework in main yaze target
|
||||
target_compile_definitions(yaze PRIVATE YAZE_ENABLE_POLICY_FRAMEWORK=1)
|
||||
|
||||
# Increase stack size on Windows to prevent stack overflow during asset loading
|
||||
# Windows default is 1MB, macOS/Linux is typically 8MB
|
||||
# LoadAssets() loads 223 graphics sheets and initializes multiple editors
|
||||
# Platform-specific settings
|
||||
if(WIN32)
|
||||
if(MSVC)
|
||||
# Set Windows subsystem to WINDOWS (GUI app, no console)
|
||||
# But keep main() as entry point (SDL_MAIN_HANDLED is set globally)
|
||||
target_link_options(yaze PRIVATE
|
||||
/STACK:8388608 # 8MB stack
|
||||
/SUBSYSTEM:WINDOWS # Windows GUI subsystem
|
||||
/ENTRY:mainCRTStartup # Use main() instead of WinMain()
|
||||
)
|
||||
message(STATUS "Configuring yaze as Windows GUI application with main() entry point")
|
||||
target_link_options(yaze PRIVATE /STACK:8388608 /SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup)
|
||||
elseif(MINGW OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
target_link_options(yaze PRIVATE
|
||||
-Wl,--stack,8388608 # 8MB stack
|
||||
-Wl,--subsystem,windows # Windows GUI subsystem
|
||||
-Wl,-emain # Use main() as entry point
|
||||
)
|
||||
message(STATUS "Configuring yaze as Windows GUI application with main() entry point (MinGW)")
|
||||
target_link_options(yaze PRIVATE -Wl,--stack,8388608 -Wl,--subsystem,windows -Wl,-emain)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Conditionally link ImGui Test Engine
|
||||
if(YAZE_ENABLE_UI_TESTS)
|
||||
if(TARGET ImGuiTestEngine)
|
||||
target_include_directories(yaze PUBLIC ${CMAKE_SOURCE_DIR}/src/lib/imgui_test_engine)
|
||||
target_link_libraries(yaze PUBLIC ImGuiTestEngine)
|
||||
target_compile_definitions(yaze PRIVATE
|
||||
YAZE_ENABLE_IMGUI_TEST_ENGINE=1
|
||||
${IMGUI_TEST_ENGINE_DEFINITIONS})
|
||||
else()
|
||||
target_compile_definitions(yaze PRIVATE YAZE_ENABLE_IMGUI_TEST_ENGINE=0)
|
||||
endif()
|
||||
else()
|
||||
target_compile_definitions(yaze PRIVATE YAZE_ENABLE_IMGUI_TEST_ENGINE=0)
|
||||
endif()
|
||||
|
||||
# Link Google Test if available for integrated testing (but NOT gtest_main to avoid main() conflicts)
|
||||
if(YAZE_BUILD_TESTS AND TARGET gtest)
|
||||
target_link_libraries(yaze PRIVATE gtest)
|
||||
target_compile_definitions(yaze PRIVATE YAZE_ENABLE_GTEST=1)
|
||||
target_compile_definitions(yaze PRIVATE YAZE_ENABLE_TESTING=1)
|
||||
else()
|
||||
target_compile_definitions(yaze PRIVATE YAZE_ENABLE_GTEST=0)
|
||||
target_compile_definitions(yaze PRIVATE YAZE_ENABLE_TESTING=0)
|
||||
endif()
|
||||
|
||||
# Conditionally link PNG if available
|
||||
if(PNG_FOUND)
|
||||
target_link_libraries(yaze PUBLIC ${PNG_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
target_link_libraries(yaze PUBLIC ${COCOA_LIBRARY})
|
||||
endif()
|
||||
|
||||
# Post-build step to copy assets to output directory
|
||||
if(APPLE)
|
||||
# macOS: Copy to bundle Resources
|
||||
target_link_libraries(yaze PUBLIC "-framework Cocoa")
|
||||
endif()
|
||||
|
||||
# Post-build asset copying for non-macOS platforms
|
||||
if(NOT APPLE)
|
||||
add_custom_command(TARGET yaze POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory
|
||||
$<TARGET_FILE_DIR:yaze>/../Resources/agent
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_SOURCE_DIR}/assets/agent
|
||||
$<TARGET_FILE_DIR:yaze>/../Resources/agent
|
||||
COMMENT "Copying agent assets to macOS bundle"
|
||||
)
|
||||
elseif(NOT APPLE)
|
||||
# Add post-build commands directly to the yaze target
|
||||
# Copy fonts
|
||||
add_custom_command(TARGET yaze POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory
|
||||
$<TARGET_FILE_DIR:yaze>/assets/font
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_SOURCE_DIR}/assets/font
|
||||
$<TARGET_FILE_DIR:yaze>/assets/font
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/assets/font $<TARGET_FILE_DIR:yaze>/assets/font
|
||||
COMMENT "Copying font assets"
|
||||
)
|
||||
|
||||
# Copy themes
|
||||
add_custom_command(TARGET yaze POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory
|
||||
$<TARGET_FILE_DIR:yaze>/assets/themes
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_SOURCE_DIR}/assets/themes
|
||||
$<TARGET_FILE_DIR:yaze>/assets/themes
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/assets/themes $<TARGET_FILE_DIR:yaze>/assets/themes
|
||||
COMMENT "Copying theme assets"
|
||||
)
|
||||
|
||||
# Copy agent assets (system prompts, etc.)
|
||||
if(EXISTS ${CMAKE_SOURCE_DIR}/assets/agent)
|
||||
add_custom_command(TARGET yaze POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory
|
||||
$<TARGET_FILE_DIR:yaze>/assets/agent
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_SOURCE_DIR}/assets/agent
|
||||
$<TARGET_FILE_DIR:yaze>/assets/agent
|
||||
COMMENT "Copying agent assets (prompts, schemas)"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Copy other assets if they exist
|
||||
if(EXISTS ${CMAKE_SOURCE_DIR}/assets/layouts)
|
||||
add_custom_command(TARGET yaze POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory
|
||||
$<TARGET_FILE_DIR:yaze>/assets/layouts
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_SOURCE_DIR}/assets/layouts
|
||||
$<TARGET_FILE_DIR:yaze>/assets/layouts
|
||||
COMMENT "Copying layout assets"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(EXISTS ${CMAKE_SOURCE_DIR}/assets/lib)
|
||||
add_custom_command(TARGET yaze POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory
|
||||
$<TARGET_FILE_DIR:yaze>/assets/lib
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_SOURCE_DIR}/assets/lib
|
||||
$<TARGET_FILE_DIR:yaze>/assets/lib
|
||||
COMMENT "Copying library assets"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/assets/agent $<TARGET_FILE_DIR:yaze>/assets/agent
|
||||
COMMENT "Copying agent assets"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# ============================================================================
|
||||
# Optional gRPC Support for ImGuiTestHarness
|
||||
# ============================================================================
|
||||
if(YAZE_WITH_GRPC)
|
||||
message(STATUS "Adding gRPC ImGuiTestHarness to yaze target")
|
||||
|
||||
target_include_directories(yaze PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/third_party/json/include)
|
||||
target_compile_definitions(yaze PRIVATE YAZE_WITH_JSON)
|
||||
|
||||
if(NOT YAZE_USE_MODULAR_BUILD)
|
||||
# Generate C++ code from .proto using the helper function from cmake/grpc.cmake
|
||||
target_add_protobuf(yaze
|
||||
${CMAKE_SOURCE_DIR}/src/protos/imgui_test_harness.proto
|
||||
${CMAKE_SOURCE_DIR}/src/protos/canvas_automation.proto)
|
||||
|
||||
# Add service implementation sources
|
||||
target_sources(yaze PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/src/app/core/service/imgui_test_harness_service.cc
|
||||
${CMAKE_SOURCE_DIR}/src/app/core/service/imgui_test_harness_service.h
|
||||
${CMAKE_SOURCE_DIR}/src/app/core/service/screenshot_utils.cc
|
||||
${CMAKE_SOURCE_DIR}/src/app/core/service/screenshot_utils.h
|
||||
${CMAKE_SOURCE_DIR}/src/app/core/service/widget_discovery_service.cc
|
||||
${CMAKE_SOURCE_DIR}/src/app/core/service/widget_discovery_service.h
|
||||
${CMAKE_SOURCE_DIR}/src/app/core/testing/test_recorder.cc
|
||||
${CMAKE_SOURCE_DIR}/src/app/core/testing/test_recorder.h
|
||||
${CMAKE_SOURCE_DIR}/src/app/core/testing/test_script_parser.cc
|
||||
${CMAKE_SOURCE_DIR}/src/app/core/testing/test_script_parser.h)
|
||||
endif()
|
||||
|
||||
# Link gRPC libraries
|
||||
target_link_libraries(yaze PRIVATE
|
||||
grpc++
|
||||
grpc++_reflection
|
||||
libprotobuf)
|
||||
|
||||
message(STATUS "✓ gRPC ImGuiTestHarness integrated")
|
||||
message(STATUS "✓ AI Agent services integrated into yaze GUI")
|
||||
endif()
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
set(
|
||||
YAZE_APP_CORE_SRC
|
||||
app/core/asar_wrapper.cc
|
||||
app/core/controller.cc
|
||||
app/emu/emulator.cc
|
||||
app/core/project.cc
|
||||
app/core/window.cc
|
||||
app/core/asar_wrapper.cc
|
||||
)
|
||||
|
||||
if (WIN32 OR MINGW OR (UNIX AND NOT APPLE))
|
||||
@@ -16,13 +15,37 @@ endif()
|
||||
|
||||
if(APPLE)
|
||||
list(APPEND YAZE_APP_CORE_SRC
|
||||
app/platform/file_dialog.mm
|
||||
app/platform/app_delegate.mm
|
||||
app/platform/font_loader.cc
|
||||
app/platform/font_loader.mm
|
||||
app/platform/asset_loader.cc
|
||||
)
|
||||
|
||||
set(YAZE_APPLE_OBJCXX_SRC
|
||||
app/platform/file_dialog.mm
|
||||
app/platform/app_delegate.mm
|
||||
app/platform/font_loader.mm
|
||||
)
|
||||
|
||||
add_library(yaze_core_objcxx OBJECT ${YAZE_APPLE_OBJCXX_SRC})
|
||||
set_target_properties(yaze_core_objcxx PROPERTIES
|
||||
OBJCXX_STANDARD 20
|
||||
OBJCXX_STANDARD_REQUIRED ON
|
||||
)
|
||||
|
||||
target_include_directories(yaze_core_objcxx PUBLIC
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_SOURCE_DIR}/src/app
|
||||
${CMAKE_SOURCE_DIR}/src/lib
|
||||
${CMAKE_SOURCE_DIR}/src/lib/imgui
|
||||
${CMAKE_SOURCE_DIR}/src/lib/asar/src
|
||||
${CMAKE_SOURCE_DIR}/src/lib/asar/src/asar
|
||||
${CMAKE_SOURCE_DIR}/src/lib/asar/src/asar-dll-bindings/c
|
||||
${CMAKE_SOURCE_DIR}/incl
|
||||
${SDL2_INCLUDE_DIR}
|
||||
${PROJECT_BINARY_DIR}
|
||||
)
|
||||
target_link_libraries(yaze_core_objcxx PUBLIC ${ABSL_TARGETS} yaze_util)
|
||||
target_compile_definitions(yaze_core_objcxx PUBLIC MACOS)
|
||||
|
||||
find_library(COCOA_LIBRARY Cocoa)
|
||||
if(NOT COCOA_LIBRARY)
|
||||
message(FATAL_ERROR "Cocoa not found")
|
||||
@@ -48,6 +71,11 @@ endif()
|
||||
add_library(yaze_core_lib STATIC
|
||||
app/rom.cc
|
||||
${YAZE_APP_CORE_SRC}
|
||||
$<$<BOOL:${APPLE}>:$<TARGET_OBJECTS:yaze_core_objcxx>>
|
||||
)
|
||||
|
||||
target_precompile_headers(yaze_core_lib PRIVATE
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_SOURCE_DIR}/src/yaze_pch.h>"
|
||||
)
|
||||
|
||||
target_include_directories(yaze_core_lib PUBLIC
|
||||
|
||||
@@ -17,7 +17,8 @@ namespace {
|
||||
constexpr absl::Duration kTestCompletionTimeout = absl::Seconds(10);
|
||||
constexpr absl::Duration kPollInterval = absl::Milliseconds(50);
|
||||
|
||||
const char* HarnessStatusToString(HarnessTestStatus status) {
|
||||
#if defined(YAZE_WITH_GRPC)
|
||||
const char* HarnessStatusToString(test::HarnessTestStatus status) {
|
||||
switch (status) {
|
||||
case HarnessTestStatus::kQueued:
|
||||
return "queued";
|
||||
@@ -31,9 +32,10 @@ const char* HarnessStatusToString(HarnessTestStatus status) {
|
||||
return "timeout";
|
||||
case HarnessTestStatus::kUnspecified:
|
||||
default:
|
||||
return "unknown";
|
||||
return "unspecified";
|
||||
}
|
||||
}
|
||||
#endif // defined(YAZE_WITH_GRPC)
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -152,7 +154,11 @@ absl::StatusOr<TestRecorder::StopRecordingSummary> TestRecorder::StopLocked(
|
||||
script_step.region = step.region;
|
||||
script_step.format = step.format;
|
||||
script_step.expect_success = step.success;
|
||||
#if defined(YAZE_WITH_GRPC)
|
||||
script_step.expect_status = ::yaze::test::HarnessStatusToString(step.final_status);
|
||||
#else
|
||||
script_step.expect_status.clear();
|
||||
#endif
|
||||
if (!step.final_error_message.empty()) {
|
||||
script_step.expect_message = step.final_error_message;
|
||||
} else {
|
||||
@@ -194,6 +200,9 @@ absl::Status TestRecorder::PopulateFinalStatusLocked() {
|
||||
return absl::FailedPreconditionError("TestManager unavailable");
|
||||
}
|
||||
|
||||
#if !defined(YAZE_WITH_GRPC)
|
||||
return absl::OkStatus();
|
||||
#else
|
||||
for (auto& step : steps_) {
|
||||
if (step.test_id.empty()) {
|
||||
continue;
|
||||
@@ -201,7 +210,7 @@ absl::Status TestRecorder::PopulateFinalStatusLocked() {
|
||||
|
||||
const absl::Time deadline = absl::Now() + kTestCompletionTimeout;
|
||||
while (absl::Now() < deadline) {
|
||||
absl::StatusOr<HarnessTestExecution> execution =
|
||||
absl::StatusOr<test::HarnessTestExecution> execution =
|
||||
test_manager_->GetHarnessTestExecution(step.test_id);
|
||||
if (!execution.ok()) {
|
||||
absl::SleepFor(kPollInterval);
|
||||
@@ -213,8 +222,8 @@ absl::Status TestRecorder::PopulateFinalStatusLocked() {
|
||||
step.assertion_failures = execution->assertion_failures;
|
||||
step.metrics = execution->metrics;
|
||||
|
||||
if (execution->status == HarnessTestStatus::kQueued ||
|
||||
execution->status == HarnessTestStatus::kRunning) {
|
||||
if (execution->status == test::HarnessTestStatus::kQueued ||
|
||||
execution->status == test::HarnessTestStatus::kRunning) {
|
||||
absl::SleepFor(kPollInterval);
|
||||
continue;
|
||||
}
|
||||
@@ -223,6 +232,7 @@ absl::Status TestRecorder::PopulateFinalStatusLocked() {
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
#endif // defined(YAZE_WITH_GRPC)
|
||||
}
|
||||
|
||||
std::string TestRecorder::GenerateRecordingId() {
|
||||
|
||||
@@ -52,7 +52,9 @@ class TestRecorder {
|
||||
std::vector<std::string> assertion_failures;
|
||||
std::string expected_value;
|
||||
std::string actual_value;
|
||||
#if defined(YAZE_WITH_GRPC)
|
||||
HarnessTestStatus final_status = HarnessTestStatus::kUnspecified;
|
||||
#endif
|
||||
std::string final_error_message;
|
||||
std::map<std::string, int32_t> metrics;
|
||||
absl::Time captured_at = absl::InfinitePast();
|
||||
|
||||
@@ -38,9 +38,9 @@ AgentUITheme AgentUITheme::FromCurrentTheme() {
|
||||
);
|
||||
|
||||
// Content colors
|
||||
theme.json_text_color = ImVec4(0.78f, 0.83f, 0.90f, 1.0f);
|
||||
theme.command_text_color = ImVec4(1.0f, 0.647f, 0.0f, 1.0f);
|
||||
theme.code_bg_color = ImVec4(0.08f, 0.08f, 0.10f, 0.95f);
|
||||
theme.json_text_color = ConvertColorToImVec4(current.text_secondary);
|
||||
theme.command_text_color = ConvertColorToImVec4(current.accent);
|
||||
theme.code_bg_color = ConvertColorToImVec4(current.code_background);
|
||||
|
||||
theme.text_secondary_color = ConvertColorToImVec4(current.text_secondary);
|
||||
|
||||
|
||||
@@ -1,46 +1,55 @@
|
||||
set(
|
||||
YAZE_APP_EDITOR_SRC
|
||||
app/editor/editor_manager.cc
|
||||
app/editor/ui/menu_builder.cc
|
||||
app/editor/ui/editor_selection_dialog.cc
|
||||
app/editor/ui/welcome_screen.cc
|
||||
app/editor/ui/workspace_manager.cc
|
||||
app/editor/system/user_settings.cc
|
||||
app/editor/ui/background_renderer.cc
|
||||
app/editor/dungeon/dungeon_editor_v2.cc
|
||||
app/editor/dungeon/dungeon_room_selector.cc
|
||||
app/editor/dungeon/dungeon_canvas_viewer.cc
|
||||
app/editor/dungeon/dungeon_object_selector.cc
|
||||
app/editor/dungeon/dungeon_toolset.cc
|
||||
app/editor/dungeon/dungeon_object_interaction.cc
|
||||
app/editor/dungeon/dungeon_room_loader.cc
|
||||
app/editor/dungeon/dungeon_usage_tracker.cc
|
||||
app/editor/dungeon/object_editor_card.cc
|
||||
app/editor/overworld/overworld_editor.cc
|
||||
app/editor/overworld/scratch_space.cc
|
||||
app/editor/sprite/sprite_editor.cc
|
||||
app/editor/music/music_editor.cc
|
||||
app/editor/message/message_editor.cc
|
||||
app/editor/message/message_data.cc
|
||||
app/editor/message/message_preview.cc
|
||||
app/editor/agent/agent_chat_history_codec.cc
|
||||
app/editor/agent/agent_chat_history_popup.cc
|
||||
app/editor/agent/agent_chat_widget.cc
|
||||
app/editor/agent/agent_collaboration_coordinator.cc
|
||||
app/editor/agent/agent_editor.cc
|
||||
app/editor/agent/agent_ui_theme.cc
|
||||
app/editor/agent/automation_bridge.cc
|
||||
app/editor/agent/network_collaboration_coordinator.cc
|
||||
app/editor/code/assembly_editor.cc
|
||||
app/editor/code/memory_editor.cc
|
||||
app/editor/code/project_file_editor.cc
|
||||
app/editor/graphics/screen_editor.cc
|
||||
app/editor/dungeon/dungeon_canvas_viewer.cc
|
||||
app/editor/dungeon/dungeon_editor_v2.cc
|
||||
app/editor/dungeon/dungeon_object_interaction.cc
|
||||
app/editor/dungeon/dungeon_object_selector.cc
|
||||
app/editor/dungeon/dungeon_room_loader.cc
|
||||
app/editor/dungeon/dungeon_room_selector.cc
|
||||
app/editor/dungeon/dungeon_toolset.cc
|
||||
app/editor/dungeon/dungeon_usage_tracker.cc
|
||||
app/editor/dungeon/object_editor_card.cc
|
||||
app/editor/editor_manager.cc
|
||||
app/editor/graphics/gfx_group_editor.cc
|
||||
app/editor/graphics/graphics_editor.cc
|
||||
app/editor/graphics/palette_editor.cc
|
||||
app/editor/overworld/tile16_editor.cc
|
||||
app/editor/overworld/map_properties.cc
|
||||
app/editor/graphics/gfx_group_editor.cc
|
||||
app/editor/graphics/screen_editor.cc
|
||||
app/editor/message/message_data.cc
|
||||
app/editor/message/message_editor.cc
|
||||
app/editor/message/message_preview.cc
|
||||
app/editor/music/music_editor.cc
|
||||
app/editor/overworld/entity.cc
|
||||
app/editor/overworld/map_properties.cc
|
||||
app/editor/overworld/overworld_editor.cc
|
||||
app/editor/overworld/overworld_entity_renderer.cc
|
||||
app/editor/system/settings_editor.cc
|
||||
app/editor/overworld/scratch_space.cc
|
||||
app/editor/overworld/tile16_editor.cc
|
||||
app/editor/sprite/sprite_editor.cc
|
||||
app/editor/system/command_manager.cc
|
||||
app/editor/system/command_palette.cc
|
||||
app/editor/system/extension_manager.cc
|
||||
app/editor/system/shortcut_manager.cc
|
||||
app/editor/system/popup_manager.cc
|
||||
app/editor/system/proposal_drawer.cc
|
||||
app/editor/agent/agent_chat_history_codec.cc
|
||||
app/editor/system/settings_editor.cc
|
||||
app/editor/system/shortcut_manager.cc
|
||||
app/editor/system/user_settings.cc
|
||||
app/editor/ui/background_renderer.cc
|
||||
app/editor/ui/editor_selection_dialog.cc
|
||||
app/editor/ui/menu_builder.cc
|
||||
app/editor/ui/menu_manager.cc
|
||||
app/editor/ui/welcome_screen.cc
|
||||
app/editor/ui/workspace_manager.cc
|
||||
)
|
||||
|
||||
if(YAZE_WITH_GRPC)
|
||||
@@ -76,12 +85,7 @@ endif()
|
||||
add_library(yaze_editor STATIC ${YAZE_APP_EDITOR_SRC})
|
||||
|
||||
target_precompile_headers(yaze_editor PRIVATE
|
||||
<array>
|
||||
<cstdint>
|
||||
<memory>
|
||||
<set>
|
||||
<string>
|
||||
<vector>
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_SOURCE_DIR}/src/yaze_pch.h>"
|
||||
)
|
||||
|
||||
target_include_directories(yaze_editor PUBLIC
|
||||
|
||||
@@ -1337,9 +1337,71 @@ void EditorManager::BuildModernMenu() {
|
||||
menu_builder_.Draw();
|
||||
}
|
||||
|
||||
void EditorManager::DrawMenuBarExtras() {
|
||||
auto* current_rom = GetCurrentRom();
|
||||
std::string version_text = absl::StrFormat("v%s", version_.c_str());
|
||||
float version_width = ImGui::CalcTextSize(version_text.c_str()).x;
|
||||
float session_rom_area_width = 280.0f;
|
||||
|
||||
SameLine(ImGui::GetWindowWidth() - version_width - 10 - session_rom_area_width);
|
||||
|
||||
if (GetActiveSessionCount() > 1) {
|
||||
if (ImGui::SmallButton(absl::StrFormat("%s%zu", ICON_MD_TAB, GetActiveSessionCount()).c_str())) {
|
||||
ShowSessionSwitcher();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Sessions: %zu active\nClick to switch", GetActiveSessionCount());
|
||||
}
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
if (current_rom && current_rom->is_loaded()) {
|
||||
if (ImGui::SmallButton(ICON_MD_APPS)) {
|
||||
ShowEditorSelection();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(ICON_MD_DASHBOARD " Editor Selection (Ctrl+E)");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton(ICON_MD_DISPLAY_SETTINGS)) {
|
||||
ShowDisplaySettings();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(ICON_MD_TUNE " Display Settings");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Separator();
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
if (current_rom && current_rom->is_loaded()) {
|
||||
std::string rom_display = current_rom->title();
|
||||
if (rom_display.length() > 22) {
|
||||
rom_display = rom_display.substr(0, 19) + "...";
|
||||
}
|
||||
if (ImGui::SmallButton(absl::StrFormat("%s%s", rom_display.c_str(), current_rom->dirty() ? "*" : "").c_str())) {
|
||||
ImGui::OpenPopup("ROM Details");
|
||||
}
|
||||
} else {
|
||||
ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "No ROM");
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
SameLine(ImGui::GetWindowWidth() - version_width - 10);
|
||||
ImGui::Text("%s", version_text.c_str());
|
||||
}
|
||||
|
||||
void EditorManager::ShowSessionSwitcher() { show_session_switcher_ = true; }
|
||||
|
||||
void EditorManager::ShowEditorSelection() { show_editor_selection_ = true; }
|
||||
|
||||
void EditorManager::ShowDisplaySettings() { if (popup_manager_) popup_manager_->Show("Display Settings"); }
|
||||
|
||||
void EditorManager::DrawMenuBar() {
|
||||
static bool show_display_settings = false;
|
||||
static bool save_as_menu = false;
|
||||
std::string version_text = absl::StrFormat("v%s", version_.c_str());
|
||||
float version_width = ImGui::CalcTextSize(version_text.c_str()).x;
|
||||
|
||||
if (BeginMenuBar()) {
|
||||
BuildModernMenu();
|
||||
@@ -1347,173 +1409,7 @@ void EditorManager::DrawMenuBar() {
|
||||
// Inline ROM selector and status
|
||||
status_ = DrawRomSelector();
|
||||
|
||||
// Calculate proper right-side positioning
|
||||
std::string version_text = absl::StrFormat("v%s", version_.c_str());
|
||||
float version_width = CalcTextSize(version_text.c_str()).x;
|
||||
|
||||
// Allocate space for ROM status and sessions (wider for better ROM title display)
|
||||
float session_rom_area_width = 280.0f; // Increased for wider ROM title
|
||||
SameLine(GetWindowWidth() - version_width - 10 - session_rom_area_width);
|
||||
|
||||
// Multi-session indicator
|
||||
if (sessions_.size() > 1) {
|
||||
if (SmallButton(absl::StrFormat("%s%zu", ICON_MD_TAB, sessions_.size())
|
||||
.c_str())) {
|
||||
show_session_switcher_ = true;
|
||||
}
|
||||
if (IsItemHovered()) {
|
||||
SetTooltip("Sessions: %zu active\nClick to switch", sessions_.size());
|
||||
}
|
||||
SameLine();
|
||||
}
|
||||
|
||||
// Editor selection and display settings quick buttons (when ROM loaded)
|
||||
if (current_rom_ && current_rom_->is_loaded()) {
|
||||
// Editor selection button
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.3f, 0.3f, 0.4f, 0.8f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.4f, 0.4f, 0.5f, 1.0f));
|
||||
if (SmallButton(ICON_MD_APPS)) {
|
||||
show_editor_selection_ = true;
|
||||
}
|
||||
ImGui::PopStyleColor(2);
|
||||
if (IsItemHovered()) {
|
||||
SetTooltip(ICON_MD_DASHBOARD " Editor Selection (Ctrl+E)");
|
||||
}
|
||||
SameLine();
|
||||
|
||||
// Display settings button
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.3f, 0.3f, 0.4f, 0.8f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.4f, 0.4f, 0.5f, 1.0f));
|
||||
if (SmallButton(ICON_MD_DISPLAY_SETTINGS)) {
|
||||
show_display_settings = !show_display_settings;
|
||||
}
|
||||
ImGui::PopStyleColor(2);
|
||||
if (IsItemHovered()) {
|
||||
SetTooltip(ICON_MD_TUNE " Display Settings");
|
||||
}
|
||||
SameLine();
|
||||
|
||||
ImGui::Separator();
|
||||
SameLine();
|
||||
}
|
||||
|
||||
// Compact ROM status with metadata popup
|
||||
if (current_rom_ && current_rom_->is_loaded()) {
|
||||
// Truncate long ROM titles for wider display
|
||||
std::string rom_display = current_rom_->title();
|
||||
if (rom_display.length() > 22) {
|
||||
rom_display = rom_display.substr(0, 19) + "...";
|
||||
}
|
||||
|
||||
ImVec4 status_color =
|
||||
current_rom_->dirty() ? ImVec4(1.0f, 0.5f, 0.0f, 1.0f)
|
||||
: // Orange for modified
|
||||
ImVec4(0.0f, 0.8f, 0.0f, 1.0f); // Green for clean
|
||||
|
||||
// Compact ROM status button
|
||||
if (SmallButton(absl::StrFormat("%s%s", rom_display.c_str(),
|
||||
current_rom_->dirty() ? "*" : "")
|
||||
.c_str())) {
|
||||
ImGui::OpenPopup("ROM Details");
|
||||
}
|
||||
|
||||
// Enhanced tooltip on hover
|
||||
if (IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::Text("%s ROM Information", ICON_MD_INFO);
|
||||
ImGui::Separator();
|
||||
ImGui::Text("%s Title: %s", ICON_MD_TITLE,
|
||||
current_rom_->title().c_str());
|
||||
ImGui::Text("%s File: %s", ICON_MD_FOLDER_OPEN,
|
||||
current_rom_->filename().c_str());
|
||||
ImGui::Text("%s Size: %.1f MB (%zu bytes)", ICON_MD_STORAGE,
|
||||
current_rom_->size() / 1048576.0f, current_rom_->size());
|
||||
ImGui::Text("%s Status: %s",
|
||||
current_rom_->dirty() ? ICON_MD_EDIT : ICON_MD_CHECK_CIRCLE,
|
||||
current_rom_->dirty() ? "Modified" : "Clean");
|
||||
ImGui::Text("%s Click for detailed view", ICON_MD_LAUNCH);
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
// Detailed ROM popup
|
||||
if (ImGui::BeginPopup("ROM Details")) {
|
||||
ImGui::Text("%s ROM Detailed Information", ICON_MD_INFO);
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
// Basic info with icons
|
||||
if (ImGui::BeginTable("ROMDetailsTable", 2,
|
||||
ImGuiTableFlags_SizingFixedFit)) {
|
||||
ImGui::TableSetupColumn("Property", ImGuiTableColumnFlags_WidthFixed,
|
||||
120);
|
||||
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s Title", ICON_MD_TITLE);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", current_rom_->title().c_str());
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s File", ICON_MD_FOLDER_OPEN);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", current_rom_->filename().c_str());
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s Size", ICON_MD_STORAGE);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%.1f MB (%zu bytes)", current_rom_->size() / 1048576.0f,
|
||||
current_rom_->size());
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s Status", current_rom_->dirty()
|
||||
? ICON_MD_EDIT
|
||||
: ICON_MD_CHECK_CIRCLE);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextColored(status_color, "%s",
|
||||
current_rom_->dirty() ? "Modified" : "Clean");
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s Session", ICON_MD_TAB);
|
||||
ImGui::TableNextColumn();
|
||||
size_t current_session_idx = GetCurrentSessionIndex();
|
||||
ImGui::Text("Session %zu of %zu", current_session_idx + 1,
|
||||
sessions_.size());
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
|
||||
// Quick actions
|
||||
ImGui::Text("%s Quick Actions", ICON_MD_FLASH_ON);
|
||||
if (ImGui::Button(absl::StrFormat("%s Save ROM", ICON_MD_SAVE).c_str(),
|
||||
ImVec2(120, 0))) {
|
||||
status_ = SaveRom();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(
|
||||
absl::StrFormat("%s Switch Session", ICON_MD_SWITCH_ACCOUNT)
|
||||
.c_str(),
|
||||
ImVec2(120, 0))) {
|
||||
show_session_switcher_ = true;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
SameLine();
|
||||
} else {
|
||||
TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "No ROM");
|
||||
SameLine();
|
||||
}
|
||||
DrawMenuBarExtras();
|
||||
|
||||
// Version display on far right
|
||||
SameLine(GetWindowWidth() - version_width - 10);
|
||||
|
||||
@@ -122,6 +122,11 @@ class EditorManager {
|
||||
auto emulator() -> emu::Emulator& { return emulator_; }
|
||||
auto quit() const { return quit_; }
|
||||
auto version() const { return version_; }
|
||||
void DrawMenuBarExtras();
|
||||
MenuBuilder& menu_builder() { return menu_builder_; }
|
||||
void ShowSessionSwitcher();
|
||||
void ShowEditorSelection();
|
||||
void ShowDisplaySettings();
|
||||
|
||||
absl::Status SetCurrentRom(Rom* rom);
|
||||
auto GetCurrentRom() -> Rom* { return current_rom_; }
|
||||
|
||||
@@ -11,63 +11,13 @@ MenuManager::MenuManager(EditorManager* editor_manager)
|
||||
: editor_manager_(editor_manager) {}
|
||||
|
||||
void MenuManager::BuildAndDraw() {
|
||||
if (!editor_manager_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenuBar()) {
|
||||
editor_manager_->BuildModernMenu(); // This contains the menu_builder_ logic
|
||||
editor_manager_->menu_builder_.Draw();
|
||||
|
||||
// This is the logic from the second half of DrawMenuBar
|
||||
auto* current_rom = editor_manager_->GetCurrentRom();
|
||||
std::string version_text = absl::StrFormat("v%s", editor_manager_->version().c_str());
|
||||
float version_width = ImGui::CalcTextSize(version_text.c_str()).x;
|
||||
float session_rom_area_width = 280.0f;
|
||||
ImGui::SameLine(ImGui::GetWindowWidth() - version_width - 10 - session_rom_area_width);
|
||||
|
||||
if (editor_manager_->GetActiveSessionCount() > 1) {
|
||||
if (ImGui::SmallButton(absl::StrFormat("%s%zu", ICON_MD_TAB, editor_manager_->GetActiveSessionCount()).c_str())) {
|
||||
editor_manager_->show_session_switcher_ = true;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Sessions: %zu active\nClick to switch", editor_manager_->GetActiveSessionCount());
|
||||
}
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
if (current_rom && current_rom->is_loaded()) {
|
||||
if (ImGui::SmallButton(ICON_MD_APPS)) {
|
||||
editor_manager_->show_editor_selection_ = true;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(ICON_MD_DASHBOARD " Editor Selection (Ctrl+E)");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton(ICON_MD_DISPLAY_SETTINGS)) {
|
||||
editor_manager_->popup_manager_->Show("Display Settings");
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(ICON_MD_TUNE " Display Settings");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Separator();
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
if (current_rom && current_rom->is_loaded()) {
|
||||
std::string rom_display = current_rom->title();
|
||||
if (rom_display.length() > 22) {
|
||||
rom_display = rom_display.substr(0, 19) + "...";
|
||||
}
|
||||
ImVec4 status_color = current_rom->dirty() ? ImVec4(1.0f, 0.5f, 0.0f, 1.0f) : ImVec4(0.0f, 0.8f, 0.0f, 1.0f);
|
||||
if (ImGui::SmallButton(absl::StrFormat("%s%s", rom_display.c_str(), current_rom->dirty() ? "*" : "").c_str())) {
|
||||
ImGui::OpenPopup("ROM Details");
|
||||
}
|
||||
// ... (rest of the popup logic from DrawMenuBar)
|
||||
} else {
|
||||
ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "No ROM");
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
ImGui::SameLine(ImGui::GetWindowWidth() - version_width - 10);
|
||||
ImGui::Text("%s", version_text.c_str());
|
||||
editor_manager_->BuildModernMenu();
|
||||
editor_manager_->DrawMenuBarExtras();
|
||||
ImGui::EndMenuBar();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ class MenuManager {
|
||||
|
||||
private:
|
||||
EditorManager* editor_manager_;
|
||||
MenuBuilder menu_builder_;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
|
||||
@@ -1,76 +1,55 @@
|
||||
# Yaze Emulator Standalone Application (skip in minimal builds)
|
||||
if (NOT YAZE_MINIMAL_BUILD AND APPLE)
|
||||
add_executable(
|
||||
yaze_emu
|
||||
MACOSX_BUNDLE
|
||||
app/main.cc
|
||||
app/rom.cc
|
||||
app/platform/app_delegate.mm
|
||||
${YAZE_APP_EMU_SRC}
|
||||
${YAZE_APP_CORE_SRC}
|
||||
${YAZE_APP_EDITOR_SRC}
|
||||
${YAZE_APP_GFX_SRC}
|
||||
${YAZE_APP_ZELDA3_SRC}
|
||||
${YAZE_UTIL_SRC}
|
||||
${YAZE_GUI_SRC}
|
||||
${IMGUI_SRC}
|
||||
# CLI service sources (needed for ProposalDrawer)
|
||||
cli/service/planning/proposal_registry.cc
|
||||
cli/service/rom/rom_sandbox_manager.cc
|
||||
)
|
||||
target_link_libraries(yaze_emu PUBLIC ${COCOA_LIBRARY})
|
||||
elseif(NOT YAZE_MINIMAL_BUILD)
|
||||
add_executable(
|
||||
yaze_emu
|
||||
app/rom.cc
|
||||
app/emu/emu.cc
|
||||
${YAZE_APP_EMU_SRC}
|
||||
${YAZE_APP_CORE_SRC}
|
||||
${YAZE_APP_EDITOR_SRC}
|
||||
${YAZE_APP_GFX_SRC}
|
||||
${YAZE_APP_ZELDA3_SRC}
|
||||
${YAZE_UTIL_SRC}
|
||||
${YAZE_GUI_SRC}
|
||||
${IMGUI_SRC}
|
||||
# CLI service sources (needed for ProposalDrawer)
|
||||
cli/service/planning/proposal_registry.cc
|
||||
cli/service/rom/rom_sandbox_manager.cc
|
||||
)
|
||||
endif()
|
||||
# This file defines the yaze_emu standalone executable.
|
||||
# Note: The yaze_emulator library is ALWAYS built (via emu_library.cmake)
|
||||
# because it's used by the main yaze app and test suites.
|
||||
# This file only controls the STANDALONE emulator executable.
|
||||
|
||||
# Only configure emulator target if it was created
|
||||
if(NOT YAZE_MINIMAL_BUILD)
|
||||
target_include_directories(
|
||||
yaze_emu PUBLIC
|
||||
${CMAKE_SOURCE_DIR}/src/lib/
|
||||
${CMAKE_SOURCE_DIR}/src/app/
|
||||
${CMAKE_SOURCE_DIR}/src/lib/asar/src
|
||||
${CMAKE_SOURCE_DIR}/src/lib/asar/src/asar
|
||||
${CMAKE_SOURCE_DIR}/src/lib/asar/src/asar-dll-bindings/c
|
||||
${CMAKE_SOURCE_DIR}/incl/
|
||||
${CMAKE_SOURCE_DIR}/src/
|
||||
${CMAKE_SOURCE_DIR}/src/lib/imgui_test_engine
|
||||
${PNG_INCLUDE_DIRS}
|
||||
${SDL2_INCLUDE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
if(YAZE_BUILD_EMU AND NOT YAZE_MINIMAL_BUILD)
|
||||
if(APPLE)
|
||||
add_executable(yaze_emu MACOSX_BUNDLE app/emu/emu.cc app/platform/app_delegate.mm)
|
||||
target_link_libraries(yaze_emu PUBLIC "-framework Cocoa")
|
||||
else()
|
||||
add_executable(yaze_emu app/emu/emu.cc)
|
||||
endif()
|
||||
|
||||
target_include_directories(yaze_emu PUBLIC
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_SOURCE_DIR}/incl
|
||||
${PROJECT_BINARY_DIR}
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
yaze_emu PUBLIC
|
||||
${ABSL_TARGETS}
|
||||
${SDL_TARGETS}
|
||||
${PNG_LIBRARIES}
|
||||
${CMAKE_DL_LIBS}
|
||||
ImGui
|
||||
asar-static
|
||||
target_link_libraries(yaze_emu PRIVATE
|
||||
yaze_editor
|
||||
yaze_emulator
|
||||
yaze_agent
|
||||
yaze_test_support
|
||||
absl::flags
|
||||
absl::flags_parse
|
||||
absl::failure_signal_handler
|
||||
)
|
||||
|
||||
# Conditionally link ImGui Test Engine
|
||||
if(YAZE_ENABLE_UI_TESTS)
|
||||
target_link_libraries(yaze_emu PUBLIC ImGuiTestEngine)
|
||||
target_compile_definitions(yaze_emu PRIVATE YAZE_ENABLE_IMGUI_TEST_ENGINE=1)
|
||||
else()
|
||||
target_compile_definitions(yaze_emu PRIVATE YAZE_ENABLE_IMGUI_TEST_ENGINE=0)
|
||||
endif()
|
||||
|
||||
# Headless Emulator Test Harness
|
||||
add_executable(yaze_emu_test emu_test.cc)
|
||||
target_include_directories(yaze_emu_test PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_SOURCE_DIR}/incl
|
||||
${PROJECT_BINARY_DIR}
|
||||
)
|
||||
target_link_libraries(yaze_emu_test PRIVATE
|
||||
yaze_emulator
|
||||
yaze_util
|
||||
absl::flags
|
||||
absl::flags_parse
|
||||
absl::status
|
||||
absl::strings
|
||||
absl::str_format
|
||||
)
|
||||
message(STATUS "✓ yaze_emu_test: Headless emulator test harness configured")
|
||||
message(STATUS "✓ yaze_emu: Standalone emulator executable configured")
|
||||
else()
|
||||
message(STATUS "○ Standalone emulator builds disabled (YAZE_BUILD_EMU=OFF or YAZE_MINIMAL_BUILD=ON)")
|
||||
message(STATUS " Note: yaze_emulator library still available for main app and tests")
|
||||
endif()
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
|
||||
add_library(yaze_emulator STATIC ${YAZE_APP_EMU_SRC})
|
||||
|
||||
target_precompile_headers(yaze_emulator PRIVATE
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_SOURCE_DIR}/src/yaze_pch.h>"
|
||||
)
|
||||
|
||||
target_include_directories(yaze_emulator PUBLIC
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_SOURCE_DIR}/src/app
|
||||
|
||||
@@ -36,9 +36,7 @@ set(
|
||||
add_library(yaze_gfx STATIC ${YAZE_APP_GFX_SRC})
|
||||
|
||||
target_precompile_headers(yaze_gfx PRIVATE
|
||||
<vector>
|
||||
<string>
|
||||
<memory>
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_SOURCE_DIR}/src/yaze_pch.h>"
|
||||
)
|
||||
|
||||
target_include_directories(yaze_gfx PUBLIC
|
||||
|
||||
@@ -1,33 +1,34 @@
|
||||
set(
|
||||
YAZE_GUI_SRC
|
||||
app/gui/canvas.cc
|
||||
app/gui/canvas/bpp_format_ui.cc
|
||||
app/gui/canvas/canvas_automation_api.cc
|
||||
app/gui/canvas/canvas_context_menu.cc
|
||||
app/gui/canvas/canvas_interaction_handler.cc
|
||||
app/gui/canvas/canvas_modals.cc
|
||||
app/gui/canvas/canvas_performance_integration.cc
|
||||
app/gui/canvas/canvas_usage_tracker.cc
|
||||
app/gui/canvas/canvas_utils.cc
|
||||
app/gui/color.cc
|
||||
app/gui/editor_card_manager.cc
|
||||
app/gui/editor_layout.cc
|
||||
app/gui/input.cc
|
||||
app/gui/modules/asset_browser.cc
|
||||
app/gui/modules/text_editor.cc
|
||||
app/gui/widgets/agent_chat_widget.cc
|
||||
app/gui/widgets/dungeon_object_emulator_preview.cc
|
||||
app/gui/widgets/collaboration_panel.cc
|
||||
app/gui/canvas.cc
|
||||
app/gui/canvas/canvas_utils.cc
|
||||
app/gui/widgets/palette_widget.cc
|
||||
app/gui/widgets/palette_editor_widget.cc
|
||||
app/gui/input.cc
|
||||
app/gui/style.cc
|
||||
app/gui/color.cc
|
||||
app/gui/theme_manager.cc
|
||||
app/gui/canvas/bpp_format_ui.cc
|
||||
app/gui/widgets/widget_id_registry.cc
|
||||
app/gui/widgets/widget_auto_register.cc
|
||||
app/gui/widgets/widget_state_capture.cc
|
||||
app/gui/ui_helpers.cc
|
||||
app/gui/editor_layout.cc
|
||||
app/gui/editor_card_manager.cc
|
||||
# Canvas system components
|
||||
app/gui/canvas/canvas_modals.cc
|
||||
app/gui/canvas/canvas_context_menu.cc
|
||||
app/gui/canvas/canvas_usage_tracker.cc
|
||||
app/gui/canvas/canvas_performance_integration.cc
|
||||
app/gui/canvas/canvas_interaction_handler.cc
|
||||
app/gui/canvas/canvas_automation_api.cc
|
||||
app/gui/widgets/agent_chat_widget.cc
|
||||
app/gui/widgets/collaboration_panel.cc
|
||||
app/gui/widgets/dungeon_object_emulator_preview.cc
|
||||
app/gui/widgets/palette_editor_widget.cc
|
||||
app/gui/widgets/palette_widget.cc
|
||||
app/gui/widgets/tile_selector_widget.cc
|
||||
app/gui/widgets/widget_auto_register.cc
|
||||
app/gui/widgets/widget_id_registry.cc
|
||||
app/gui/widgets/widget_measurement.cc
|
||||
app/gui/widgets/widget_state_capture.cc
|
||||
# Canvas system components
|
||||
)
|
||||
|
||||
# ==============================================================================
|
||||
@@ -47,12 +48,7 @@ set(
|
||||
add_library(yaze_gui STATIC ${YAZE_GUI_SRC})
|
||||
|
||||
target_precompile_headers(yaze_gui PRIVATE
|
||||
<array>
|
||||
<memory>
|
||||
<set>
|
||||
<string>
|
||||
<string_view>
|
||||
<vector>
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_SOURCE_DIR}/src/yaze_pch.h>"
|
||||
)
|
||||
|
||||
target_include_directories(yaze_gui PUBLIC
|
||||
|
||||
@@ -67,6 +67,8 @@ void EnhancedTheme::ApplyToImGui() const {
|
||||
colors[ImGuiCol_Tab] = ConvertColorToImVec4(tab);
|
||||
colors[ImGuiCol_TabHovered] = ConvertColorToImVec4(tab_hovered);
|
||||
colors[ImGuiCol_TabSelected] = ConvertColorToImVec4(tab_active);
|
||||
colors[ImGuiCol_TabUnfocused] = ConvertColorToImVec4(tab_unfocused);
|
||||
colors[ImGuiCol_TabUnfocusedActive] = ConvertColorToImVec4(tab_unfocused_active);
|
||||
colors[ImGuiCol_DockingPreview] = ConvertColorToImVec4(docking_preview);
|
||||
colors[ImGuiCol_DockingEmptyBg] = ConvertColorToImVec4(docking_empty_bg);
|
||||
|
||||
@@ -170,6 +172,8 @@ void ThemeManager::CreateFallbackYazeClassic() {
|
||||
theme.tab = RGBA(46, 66, 46); // alttpDarkGreen
|
||||
theme.tab_hovered = RGBA(71, 92, 71); // alttpMidGreen
|
||||
theme.tab_active = RGBA(89, 119, 89); // TabActive
|
||||
theme.tab_unfocused = RGBA(37, 52, 37); // Darker version of tab
|
||||
theme.tab_unfocused_active = RGBA(62, 83, 62); // Darker version of tab_active
|
||||
|
||||
// Complete all remaining ImGui colors from original ColorsYaze() function
|
||||
theme.title_bg = RGBA(71, 92, 71); // alttpMidGreen
|
||||
@@ -875,6 +879,8 @@ void ThemeManager::ApplyClassicYazeTheme() {
|
||||
classic_theme.tab = RGBA(46, 66, 46); // alttpDarkGreen
|
||||
classic_theme.tab_hovered = RGBA(71, 92, 71); // alttpMidGreen
|
||||
classic_theme.tab_active = RGBA(89, 119, 89); // TabActive
|
||||
classic_theme.tab_unfocused = RGBA(37, 52, 37); // Darker version of tab
|
||||
classic_theme.tab_unfocused_active = RGBA(62, 83, 62); // Darker version of tab_active
|
||||
|
||||
// Complete all remaining ImGui colors from original ColorsYaze() function
|
||||
classic_theme.title_bg = RGBA(71, 92, 71); // alttpMidGreen
|
||||
@@ -1432,6 +1438,8 @@ void ThemeManager::ShowSimpleThemeEditor(bool* p_open) {
|
||||
{"Tab", &edit_theme.tab},
|
||||
{"Tab Hovered", &edit_theme.tab_hovered},
|
||||
{"Tab Active", &edit_theme.tab_active},
|
||||
{"Tab Unfocused", &edit_theme.tab_unfocused},
|
||||
{"Tab Unfocused Active", &edit_theme.tab_unfocused_active},
|
||||
{"Tab Dimmed", &edit_theme.tab_dimmed},
|
||||
{"Tab Dimmed Selected", &edit_theme.tab_dimmed_selected},
|
||||
{"Title Background", &edit_theme.title_bg},
|
||||
|
||||
@@ -108,6 +108,8 @@ struct EnhancedTheme {
|
||||
Color tree_lines;
|
||||
|
||||
// Additional ImGui colors for complete coverage
|
||||
Color tab_unfocused;
|
||||
Color tab_unfocused_active;
|
||||
Color tab_dimmed;
|
||||
Color tab_dimmed_selected;
|
||||
Color tab_dimmed_selected_overline;
|
||||
|
||||
@@ -23,6 +23,10 @@ endif()
|
||||
|
||||
add_library(yaze_net STATIC ${YAZE_NET_SRC})
|
||||
|
||||
target_precompile_headers(yaze_net PRIVATE
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_SOURCE_DIR}/src/yaze_pch.h>"
|
||||
)
|
||||
|
||||
target_include_directories(yaze_net PUBLIC
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_SOURCE_DIR}/src/lib
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
#import "util/file_util.h"
|
||||
#import "app/editor/editor.h"
|
||||
#import "app/rom.h"
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
using std::span;
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
/* Apple OSX and iOS (Darwin). */
|
||||
|
||||
@@ -18,16 +18,13 @@ set(YAZE_TEST_SOURCES
|
||||
add_library(yaze_test_support STATIC ${YAZE_TEST_SOURCES})
|
||||
|
||||
target_precompile_headers(yaze_test_support PRIVATE
|
||||
<memory>
|
||||
<set>
|
||||
<string>
|
||||
<string_view>
|
||||
<vector>
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_SOURCE_DIR}/src/yaze_pch.h>"
|
||||
)
|
||||
|
||||
target_include_directories(yaze_test_support PUBLIC
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_SOURCE_DIR}/incl
|
||||
${CMAKE_SOURCE_DIR}/src/lib
|
||||
${PROJECT_BINARY_DIR}
|
||||
)
|
||||
|
||||
@@ -41,11 +38,17 @@ target_link_libraries(yaze_test_support PUBLIC
|
||||
yaze_common
|
||||
)
|
||||
|
||||
# Link agent library if gRPC is enabled (for z3ed test suites)
|
||||
# Link agent library if available (for z3ed test suites)
|
||||
# yaze_agent contains all the CLI service code (tile16_proposal_generator, gui_automation_client, etc.)
|
||||
if(YAZE_WITH_GRPC)
|
||||
if(TARGET yaze_agent)
|
||||
target_link_libraries(yaze_test_support PUBLIC yaze_agent)
|
||||
message(STATUS "✓ z3ed test suites enabled (YAZE_WITH_GRPC=ON)")
|
||||
if(YAZE_WITH_GRPC)
|
||||
message(STATUS "✓ z3ed test suites enabled with gRPC support")
|
||||
else()
|
||||
message(STATUS "✓ z3ed test suites enabled (without gRPC)")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "○ z3ed test suites disabled (yaze_agent not built)")
|
||||
endif()
|
||||
|
||||
message(STATUS "✓ yaze_test_support library configured")
|
||||
@@ -1675,9 +1675,9 @@ absl::Status TestManager::TestRomDataIntegrity(Rom* rom) {
|
||||
});
|
||||
}
|
||||
|
||||
#if defined(YAZE_WITH_GRPC)
|
||||
std::string TestManager::RegisterHarnessTest(const std::string& name,
|
||||
const std::string& category) {
|
||||
#if defined(YAZE_WITH_GRPC)
|
||||
absl::MutexLock lock(&harness_history_mutex_);
|
||||
std::string test_id = absl::StrCat("harness_", absl::ToUnixMicros(absl::Now()), "_", harness_history_.size());
|
||||
HarnessTestExecution execution;
|
||||
@@ -1693,12 +1693,15 @@ std::string TestManager::RegisterHarnessTest(const std::string& name,
|
||||
aggregate.latest_execution = execution;
|
||||
harness_history_order_.push_back(test_id);
|
||||
return test_id;
|
||||
}
|
||||
#else
|
||||
std::string TestManager::RegisterHarnessTest(const std::string& name,
|
||||
const std::string& category) {
|
||||
(void)name;
|
||||
(void)category;
|
||||
return {};
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(YAZE_WITH_GRPC)
|
||||
void TestManager::MarkHarnessTestRunning(const std::string& test_id) {
|
||||
@@ -1852,10 +1855,12 @@ void TestManager::CaptureFailureContext(const std::string& test_id) {
|
||||
if (harness_listener_) {
|
||||
harness_listener_->OnHarnessTestUpdated(execution);
|
||||
}
|
||||
#else
|
||||
(void)test_id;
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
void TestManager::CaptureFailureContext(const std::string& test_id) {
|
||||
(void)test_id;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(YAZE_WITH_GRPC)
|
||||
void TestManager::TrimHarnessHistoryLocked() {
|
||||
@@ -1870,14 +1875,6 @@ absl::Status TestManager::ReplayLastPlan() {
|
||||
return absl::FailedPreconditionError("Harness plan replay not available");
|
||||
}
|
||||
|
||||
absl::Status TestManager::ShowHarnessDashboard() {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status TestManager::ShowHarnessActiveTests() {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void TestManager::SetHarnessListener(HarnessListener* listener) {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
harness_listener_ = listener;
|
||||
@@ -1886,15 +1883,24 @@ void TestManager::SetHarnessListener(HarnessListener* listener) {
|
||||
absl::Status TestManager::ReplayLastPlan() {
|
||||
return absl::UnimplementedError("Harness features require YAZE_WITH_GRPC");
|
||||
}
|
||||
#endif
|
||||
|
||||
absl::Status TestManager::ShowHarnessDashboard() {
|
||||
// These methods are always available, but may return unimplemented without GRPC
|
||||
#if defined(YAZE_WITH_GRPC)
|
||||
return absl::OkStatus();
|
||||
#else
|
||||
return absl::UnimplementedError("Harness features require YAZE_WITH_GRPC");
|
||||
#endif
|
||||
}
|
||||
|
||||
absl::Status TestManager::ShowHarnessActiveTests() {
|
||||
#if defined(YAZE_WITH_GRPC)
|
||||
return absl::OkStatus();
|
||||
#else
|
||||
return absl::UnimplementedError("Harness features require YAZE_WITH_GRPC");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void TestManager::RecordPlanSummary(const std::string& summary) {
|
||||
(void)summary;
|
||||
|
||||
@@ -299,8 +299,16 @@ class TestManager {
|
||||
|
||||
void SetHarnessListener(HarnessListener* listener);
|
||||
|
||||
absl::Status ReplayLastPlan();
|
||||
#else
|
||||
// Stub implementations when GRPC is not available
|
||||
std::string RegisterHarnessTest(const std::string& name,
|
||||
const std::string& category);
|
||||
void CaptureFailureContext(const std::string& test_id);
|
||||
absl::Status ReplayLastPlan();
|
||||
#endif
|
||||
|
||||
// These methods are always available
|
||||
absl::Status ShowHarnessDashboard();
|
||||
absl::Status ShowHarnessActiveTests();
|
||||
void RecordPlanSummary(const std::string& summary);
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
set(
|
||||
YAZE_APP_ZELDA3_SRC
|
||||
app/zelda3/hyrule_magic.cc
|
||||
app/zelda3/zelda3_labels.cc
|
||||
app/zelda3/overworld/overworld_map.cc
|
||||
app/zelda3/overworld/overworld.cc
|
||||
app/zelda3/screen/inventory.cc
|
||||
app/zelda3/screen/title_screen.cc
|
||||
app/zelda3/screen/dungeon_map.cc
|
||||
app/zelda3/sprite/sprite.cc
|
||||
app/zelda3/sprite/sprite_builder.cc
|
||||
app/zelda3/music/tracker.cc
|
||||
app/zelda3/dungeon/room.cc
|
||||
app/zelda3/dungeon/room_object.cc
|
||||
app/zelda3/dungeon/object_parser.cc
|
||||
app/zelda3/dungeon/object_drawer.cc
|
||||
app/zelda3/dungeon/dungeon_editor_system.cc
|
||||
app/zelda3/dungeon/dungeon_object_editor.cc
|
||||
app/zelda3/dungeon/object_drawer.cc
|
||||
app/zelda3/dungeon/object_parser.cc
|
||||
app/zelda3/dungeon/room.cc
|
||||
app/zelda3/dungeon/room_layout.cc
|
||||
app/zelda3/dungeon/room_object.cc
|
||||
app/zelda3/hyrule_magic.cc
|
||||
app/zelda3/music/tracker.cc
|
||||
app/zelda3/overworld/overworld.cc
|
||||
app/zelda3/overworld/overworld_map.cc
|
||||
app/zelda3/screen/dungeon_map.cc
|
||||
app/zelda3/screen/inventory.cc
|
||||
app/zelda3/screen/title_screen.cc
|
||||
app/zelda3/sprite/sprite.cc
|
||||
app/zelda3/sprite/sprite_builder.cc
|
||||
app/zelda3/zelda3_labels.cc
|
||||
)
|
||||
|
||||
# ==============================================================================
|
||||
@@ -34,12 +35,7 @@ set(
|
||||
add_library(yaze_zelda3 STATIC ${YAZE_APP_ZELDA3_SRC})
|
||||
|
||||
target_precompile_headers(yaze_zelda3 PRIVATE
|
||||
<array>
|
||||
<memory>
|
||||
<set>
|
||||
<string>
|
||||
<string_view>
|
||||
<vector>
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_SOURCE_DIR}/src/yaze_pch.h>"
|
||||
)
|
||||
|
||||
target_include_directories(yaze_zelda3 PUBLIC
|
||||
|
||||
@@ -1,69 +1,3 @@
|
||||
include(FetchContent)
|
||||
|
||||
function(_yaze_ensure_yaml_cpp _out_target)
|
||||
if(TARGET yaml-cpp::yaml-cpp)
|
||||
set(${_out_target} yaml-cpp::yaml-cpp PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(TARGET yaml-cpp)
|
||||
set(${_out_target} yaml-cpp PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
find_package(yaml-cpp CONFIG QUIET)
|
||||
|
||||
if(TARGET yaml-cpp::yaml-cpp)
|
||||
set(${_out_target} yaml-cpp::yaml-cpp PARENT_SCOPE)
|
||||
return()
|
||||
elseif(TARGET yaml-cpp)
|
||||
set(${_out_target} yaml-cpp PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
message(STATUS "yaml-cpp not found via package config, fetching from source")
|
||||
|
||||
FetchContent_Declare(yaml-cpp
|
||||
GIT_REPOSITORY https://github.com/jbeder/yaml-cpp.git
|
||||
GIT_TAG 0.8.0
|
||||
)
|
||||
|
||||
FetchContent_GetProperties(yaml-cpp)
|
||||
if(NOT yaml-cpp_POPULATED)
|
||||
FetchContent_Populate(yaml-cpp)
|
||||
|
||||
set(_yaml_cpp_cmakelists "${yaml-cpp_SOURCE_DIR}/CMakeLists.txt")
|
||||
if(EXISTS "${_yaml_cpp_cmakelists}")
|
||||
file(READ "${_yaml_cpp_cmakelists}" _yaml_cpp_cmake_contents)
|
||||
if(_yaml_cpp_cmake_contents MATCHES "cmake_minimum_required\\(VERSION 3\\.4\\)")
|
||||
string(REPLACE "cmake_minimum_required(VERSION 3.4)"
|
||||
"cmake_minimum_required(VERSION 3.5)"
|
||||
_yaml_cpp_cmake_contents "${_yaml_cpp_cmake_contents}")
|
||||
file(WRITE "${_yaml_cpp_cmakelists}" "${_yaml_cpp_cmake_contents}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(YAML_CPP_BUILD_TESTS OFF CACHE BOOL "Disable yaml-cpp tests" FORCE)
|
||||
set(YAML_CPP_BUILD_CONTRIB OFF CACHE BOOL "Disable yaml-cpp contrib" FORCE)
|
||||
set(YAML_CPP_BUILD_TOOLS OFF CACHE BOOL "Disable yaml-cpp tools" FORCE)
|
||||
set(YAML_CPP_INSTALL OFF CACHE BOOL "Disable yaml-cpp install" FORCE)
|
||||
set(YAML_CPP_FORMAT_SOURCE OFF CACHE BOOL "Disable yaml-cpp format target" FORCE)
|
||||
|
||||
add_subdirectory(${yaml-cpp_SOURCE_DIR} ${yaml-cpp_BINARY_DIR} EXCLUDE_FROM_ALL)
|
||||
|
||||
if(NOT TARGET yaml-cpp)
|
||||
message(FATAL_ERROR "yaml-cpp target was not created after fetching")
|
||||
endif()
|
||||
|
||||
# Ensure the fetched target exposes its public headers
|
||||
target_include_directories(yaml-cpp PUBLIC ${yaml-cpp_SOURCE_DIR}/include)
|
||||
endif()
|
||||
|
||||
set(${_out_target} yaml-cpp PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
_yaze_ensure_yaml_cpp(YAZE_YAML_CPP_TARGET)
|
||||
|
||||
set(YAZE_AGENT_SOURCES
|
||||
cli/service/agent/proposal_executor.cc
|
||||
cli/handlers/agent/todo_commands.cc
|
||||
@@ -73,6 +7,7 @@ set(YAZE_AGENT_SOURCES
|
||||
cli/service/agent/tool_dispatcher.cc
|
||||
cli/service/agent/learned_knowledge_service.cc
|
||||
cli/service/agent/todo_manager.cc
|
||||
cli/service/agent/vim_mode.cc
|
||||
cli/service/ai/ai_service.cc
|
||||
cli/service/ai/ai_action_parser.cc
|
||||
cli/service/ai/vision_action_refiner.cc
|
||||
@@ -91,8 +26,9 @@ set(YAZE_AGENT_SOURCES
|
||||
cli/service/resources/resource_context_builder.cc
|
||||
cli/service/resources/command_context.cc
|
||||
cli/service/resources/command_handler.cc
|
||||
cli/handlers/command_wrappers.cc
|
||||
cli/handlers/agent.cc
|
||||
cli/handlers/command_handlers.cc
|
||||
cli/handlers/agent/simple_chat_command.cc
|
||||
cli/handlers/game/overworld_inspect.cc
|
||||
cli/handlers/game/message.cc
|
||||
cli/handlers/rom/mock_rom.cc
|
||||
@@ -107,6 +43,7 @@ set(YAZE_AGENT_SOURCES
|
||||
cli/handlers/graphics/palette_commands.cc
|
||||
cli/handlers/tools/emulator_commands.cc
|
||||
cli/handlers/game/message_commands.cc
|
||||
cli/handlers/graphics/sprite_commands.cc
|
||||
# ROM commands
|
||||
cli/handlers/rom/rom_commands.cc
|
||||
cli/handlers/rom/project_commands.cc
|
||||
@@ -122,8 +59,14 @@ add_library(yaze_agent STATIC ${YAZE_AGENT_SOURCES})
|
||||
|
||||
set(_yaze_agent_link_targets
|
||||
yaze_common
|
||||
yaze_util
|
||||
yaze_gfx
|
||||
yaze_gui
|
||||
yaze_core_lib
|
||||
yaze_zelda3
|
||||
yaze_emulator
|
||||
${ABSL_TARGETS}
|
||||
${YAZE_YAML_CPP_TARGET}
|
||||
yaml-cpp
|
||||
)
|
||||
|
||||
target_link_libraries(yaze_agent PUBLIC ${_yaze_agent_link_targets})
|
||||
@@ -135,15 +78,9 @@ target_include_directories(yaze_agent
|
||||
${CMAKE_SOURCE_DIR}/third_party/httplib
|
||||
${CMAKE_SOURCE_DIR}/third_party/json/include
|
||||
${CMAKE_SOURCE_DIR}/src/lib
|
||||
${CMAKE_SOURCE_DIR}/src/cli/handlers
|
||||
)
|
||||
|
||||
if(YAZE_YAML_CPP_TARGET)
|
||||
get_target_property(_yaze_yaml_include_dirs ${YAZE_YAML_CPP_TARGET} INTERFACE_INCLUDE_DIRECTORIES)
|
||||
if(_yaze_yaml_include_dirs)
|
||||
target_include_directories(yaze_agent PUBLIC ${_yaze_yaml_include_dirs})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(SDL2_INCLUDE_DIR)
|
||||
target_include_directories(yaze_agent PUBLIC ${SDL2_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
1370
src/cli/cli.cc
1370
src/cli/cli.cc
File diff suppressed because it is too large
Load Diff
202
src/cli/cli.h
202
src/cli/cli.h
@@ -13,6 +13,10 @@
|
||||
#include "app/snes.h"
|
||||
#include "util/macro.h"
|
||||
|
||||
#include "cli/service/resources/command_handler.h"
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
// Forward declarations
|
||||
namespace ftxui {
|
||||
class ScreenInteractive;
|
||||
@@ -24,215 +28,21 @@ namespace cli {
|
||||
// Forward declaration
|
||||
class TuiComponent;
|
||||
|
||||
class CommandHandler {
|
||||
public:
|
||||
CommandHandler() = default;
|
||||
virtual ~CommandHandler() = default;
|
||||
virtual absl::Status Run(const std::vector<std::string>& arg_vec) = 0;
|
||||
virtual void RunTUI(ftxui::ScreenInteractive& screen) {
|
||||
// Default implementation does nothing
|
||||
}
|
||||
|
||||
Rom rom_;
|
||||
};
|
||||
|
||||
struct CommandInfo {
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::string usage;
|
||||
std::function<absl::Status(const std::vector<std::string>&)> handler;
|
||||
};
|
||||
|
||||
class ModernCLI {
|
||||
public:
|
||||
ModernCLI();
|
||||
absl::Status Run(int argc, char* argv[]);
|
||||
CommandHandler* GetCommandHandler(const std::string& name);
|
||||
void PrintTopLevelHelp() const;
|
||||
void PrintCategoryHelp(const std::string& category) const;
|
||||
void PrintCommandSummary() const;
|
||||
|
||||
std::map<std::string, CommandInfo> commands_;
|
||||
|
||||
private:
|
||||
void SetupCommands();
|
||||
void ShowHelp();
|
||||
void ShowCategoryHelp(const std::string& category);
|
||||
void ShowCategoryHelp(const std::string& category) const;
|
||||
void ShowCommandSummary() const;
|
||||
|
||||
// Command Handlers
|
||||
absl::Status HandleAsarPatchCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleBpsPatchCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleExtractSymbolsCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleAgentCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleCollabCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleProjectBuildCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleProjectInitCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleRomInfoCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleRomGenerateGoldenCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleRomDiffCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleDungeonExportCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleDungeonListObjectsCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleGfxExportCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleGfxImportCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleCommandPaletteCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandlePaletteExportCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandlePaletteImportCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandlePaletteCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleRomValidateCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleOverworldGetTileCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleOverworldFindTileCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleOverworldDescribeMapCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleOverworldListWarpsCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleOverworldSetTileCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleOverworldSelectRectCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleOverworldScrollToCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleOverworldSetZoomCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleOverworldGetVisibleRegionCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleSpriteCreateCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleChatEntryCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleProposalCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleWidgetCommand(const std::vector<std::string>& args);
|
||||
};
|
||||
|
||||
// Legacy command classes removed - using new CommandHandler system
|
||||
// See TODO comments in cli.cc for implementation roadmap
|
||||
|
||||
class Open : public CommandHandler {
|
||||
public:
|
||||
absl::Status Run(const std::vector<std::string>& arg_vec) override {
|
||||
auto const& arg = arg_vec[0];
|
||||
RETURN_IF_ERROR(rom_.LoadFromFile(arg, RomLoadOptions::CliDefaults()))
|
||||
std::cout << "Title: " << rom_.title() << std::endl;
|
||||
std::cout << "Size: 0x" << std::hex << rom_.size() << std::endl;
|
||||
return absl::OkStatus();
|
||||
}
|
||||
};
|
||||
|
||||
class Backup : public CommandHandler {
|
||||
public:
|
||||
absl::Status Run(const std::vector<std::string>& arg_vec) override {
|
||||
RETURN_IF_ERROR(rom_.LoadFromFile(arg_vec[0]))
|
||||
Rom::SaveSettings settings;
|
||||
settings.backup = true;
|
||||
if (arg_vec.size() == 2) {
|
||||
// Optional filename added
|
||||
settings.filename = arg_vec[1];
|
||||
}
|
||||
RETURN_IF_ERROR(rom_.SaveToFile(settings))
|
||||
return absl::OkStatus();
|
||||
}
|
||||
};
|
||||
|
||||
class Compress : public CommandHandler {
|
||||
public:
|
||||
absl::Status Run(const std::vector<std::string>& arg_vec) override;
|
||||
};
|
||||
|
||||
class Decompress : public CommandHandler {
|
||||
public:
|
||||
absl::Status Run(const std::vector<std::string>& arg_vec) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Convert a SNES address to a PC address.
|
||||
|
||||
* @param arg_vec `-s <address>`
|
||||
* @return absl::Status
|
||||
*/
|
||||
class SnesToPcCommand : public CommandHandler {
|
||||
public:
|
||||
absl::Status Run(const std::vector<std::string>& arg_vec) override {
|
||||
auto arg = arg_vec[0];
|
||||
std::stringstream ss(arg.data());
|
||||
uint32_t snes_address;
|
||||
ss >> std::hex >> snes_address;
|
||||
uint32_t pc_address = SnesToPc(snes_address);
|
||||
std::cout << std::hex << pc_address << std::endl;
|
||||
return absl::OkStatus();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Convert a PC address to a SNES address.
|
||||
|
||||
* @param arg_vec `-p <address>`
|
||||
* @return absl::Status
|
||||
*/
|
||||
class PcToSnesCommand : public CommandHandler {
|
||||
public:
|
||||
absl::Status Run(const std::vector<std::string>& arg_vec) override {
|
||||
auto arg = arg_vec[0];
|
||||
std::stringstream ss(arg.data());
|
||||
uint32_t pc_address;
|
||||
ss >> std::hex >> pc_address;
|
||||
uint32_t snes_address = PcToSnes(pc_address);
|
||||
std::cout << "SNES LoROM Address: ";
|
||||
std::cout << "$" << std::uppercase << std::hex << snes_address << "\n";
|
||||
return absl::OkStatus();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Read from a Rom file.
|
||||
|
||||
* @param arg_vec `-r <rom_file> <address> <optional:length, default: 0x01>`
|
||||
* @return absl::Status
|
||||
*/
|
||||
class ReadFromRom : public CommandHandler {
|
||||
public:
|
||||
absl::Status Run(const std::vector<std::string>& arg_vec) override {
|
||||
RETURN_IF_ERROR(rom_.LoadFromFile(arg_vec[0]))
|
||||
|
||||
std::stringstream ss(arg_vec[1].data());
|
||||
uint32_t offset;
|
||||
ss >> std::hex >> offset;
|
||||
uint32_t length = 0x01;
|
||||
if (!arg_vec[2].empty()) {
|
||||
length = std::stoi(arg_vec[2]);
|
||||
}
|
||||
|
||||
if (length > 1) {
|
||||
auto returned_bytes_status = rom_.ReadByteVector(offset, length);
|
||||
if (!returned_bytes_status.ok()) {
|
||||
return returned_bytes_status.status();
|
||||
}
|
||||
auto returned_bytes = returned_bytes_status.value();
|
||||
for (const auto& each : returned_bytes) {
|
||||
std::cout << each;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
} else {
|
||||
auto byte = rom_.ReadByte(offset);
|
||||
std::cout << std::hex << byte.value() << std::endl;
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Expand a Rom file.
|
||||
|
||||
* @param arg_vec `-x <rom_file> <file_size>`
|
||||
* @return absl::Status
|
||||
*/
|
||||
class Expand : public CommandHandler {
|
||||
public:
|
||||
absl::Status Run(const std::vector<std::string>& arg_vec) override {
|
||||
RETURN_IF_ERROR(rom_.LoadFromFile(arg_vec[0]))
|
||||
|
||||
std::stringstream ss(arg_vec[1].data());
|
||||
uint32_t size;
|
||||
ss >> std::hex >> size;
|
||||
|
||||
rom_.Expand(size);
|
||||
|
||||
std::cout << "Successfully expanded ROM to " << std::hex << size
|
||||
<< std::endl;
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
std::map<std::string, std::unique_ptr<resources::CommandHandler>> commands_;
|
||||
};
|
||||
|
||||
} // namespace cli
|
||||
|
||||
320
src/cli/handlers/README.md
Normal file
320
src/cli/handlers/README.md
Normal file
@@ -0,0 +1,320 @@
|
||||
# YAZE Modern Command Handler Architecture
|
||||
|
||||
This directory contains the modern command handler system that provides a consistent interface for both CLI and AI agent interactions with ROM data.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
The command handler system follows a clean, layered architecture:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ CLI / Agent Interface │
|
||||
│ (cli.cc, agent.cc, simple-chat, etc) │
|
||||
└─────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Command Handler Base Class │
|
||||
│ (resources/command_handler.h) │
|
||||
│ - Argument parsing │
|
||||
│ - ROM context management │
|
||||
│ - Output formatting │
|
||||
│ - Error handling │
|
||||
└─────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Concrete Command Handlers │
|
||||
│ (handlers/*/*) │
|
||||
│ - SpriteListCommandHandler │
|
||||
│ - DungeonDescribeRoomCommandHandler │
|
||||
│ - OverworldFindTileCommandHandler │
|
||||
│ - PaletteGetColorsCommandHandler │
|
||||
│ - etc... │
|
||||
└─────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Core YAZE Libraries │
|
||||
│ - zelda3/ (overworld, dungeon, sprite) │
|
||||
│ - gfx/ (graphics, palette) │
|
||||
│ - app/editor/ (ROM operations) │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Namespace Structure
|
||||
|
||||
All command handlers are organized under a simplified namespace:
|
||||
|
||||
```cpp
|
||||
namespace yaze {
|
||||
namespace cli {
|
||||
namespace handlers {
|
||||
// All command handler classes live here
|
||||
class SpriteListCommandHandler : public resources::CommandHandler { ... };
|
||||
class DungeonDescribeRoomCommandHandler : public resources::CommandHandler { ... };
|
||||
// etc.
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Directory Organization
|
||||
|
||||
```
|
||||
handlers/
|
||||
├── README.md (this file)
|
||||
├── commands.h // Legacy command function declarations
|
||||
├── command_wrappers.cc // Wrapper functions for backward compatibility
|
||||
├── command_handlers.h // Forward declarations and factory functions
|
||||
├── command_handlers.cc // Factory implementations
|
||||
│
|
||||
├── graphics/ // Graphics-related commands
|
||||
│ ├── sprite_commands.h/.cc // Sprite listing and properties
|
||||
│ ├── palette_commands.h/.cc // Palette manipulation
|
||||
│ ├── hex_commands.h/.cc // Raw hex data access
|
||||
│ └── gfx.cc // Legacy graphics commands
|
||||
│
|
||||
├── game/ // Game data inspection
|
||||
│ ├── dungeon_commands.h/.cc // Dungeon room inspection
|
||||
│ ├── overworld_commands.h/.cc // Overworld map inspection
|
||||
│ ├── music_commands.h/.cc // Music track information
|
||||
│ ├── dialogue_commands.h/.cc // Dialogue/message search
|
||||
│ └── message_commands.h/.cc // Message data access
|
||||
│
|
||||
├── tools/ // Development tools
|
||||
│ ├── resource_commands.h/.cc // Resource label inspection
|
||||
│ ├── gui_commands.h/.cc // GUI automation
|
||||
│ └── emulator_commands.h/.cc // Emulator/debugger control
|
||||
│
|
||||
└── agent/ // AI agent specific
|
||||
├── general_commands.cc // Agent command routing
|
||||
├── test_commands.cc // Test harness
|
||||
└── todo_commands.h/.cc // Task management
|
||||
```
|
||||
|
||||
## Creating a New Command Handler
|
||||
|
||||
### 1. Define the Handler Class
|
||||
|
||||
Create a header file (e.g., `new_feature_commands.h`):
|
||||
|
||||
```cpp
|
||||
#ifndef YAZE_SRC_CLI_HANDLERS_NEW_FEATURE_COMMANDS_H_
|
||||
#define YAZE_SRC_CLI_HANDLERS_NEW_FEATURE_COMMANDS_H_
|
||||
|
||||
#include "cli/service/resources/command_handler.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace cli {
|
||||
namespace handlers {
|
||||
|
||||
class NewFeatureCommandHandler : public resources::CommandHandler {
|
||||
public:
|
||||
std::string GetName() const { return "new-feature"; }
|
||||
|
||||
std::string GetDescription() const {
|
||||
return "Brief description of what this command does";
|
||||
}
|
||||
|
||||
std::string GetUsage() const {
|
||||
return "new-feature --arg1 <value> [--optional-arg2 <value>]";
|
||||
}
|
||||
|
||||
absl::Status ValidateArgs(const resources::ArgumentParser& parser) override {
|
||||
// Validate required arguments
|
||||
return parser.RequireArgs({"arg1"});
|
||||
}
|
||||
|
||||
absl::Status Execute(Rom* rom, const resources::ArgumentParser& parser,
|
||||
resources::OutputFormatter& formatter) override;
|
||||
};
|
||||
|
||||
} // namespace handlers
|
||||
} // namespace cli
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_SRC_CLI_HANDLERS_NEW_FEATURE_COMMANDS_H_
|
||||
```
|
||||
|
||||
### 2. Implement the Handler
|
||||
|
||||
Create the implementation file (e.g., `new_feature_commands.cc`):
|
||||
|
||||
```cpp
|
||||
#include "cli/handlers/new_feature_commands.h"
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace cli {
|
||||
namespace handlers {
|
||||
|
||||
absl::Status NewFeatureCommandHandler::Execute(
|
||||
Rom* rom, const resources::ArgumentParser& parser,
|
||||
resources::OutputFormatter& formatter) {
|
||||
|
||||
// Parse arguments
|
||||
auto arg1 = parser.GetString("arg1").value();
|
||||
auto arg2 = parser.GetString("optional-arg2").value_or("default");
|
||||
|
||||
// Begin output
|
||||
formatter.BeginObject("New Feature Result");
|
||||
formatter.AddField("input_arg", arg1);
|
||||
|
||||
// Do work with ROM
|
||||
// ... use rom->read(), zelda3 classes, etc.
|
||||
|
||||
// Add results to formatter
|
||||
formatter.AddField("result", "success");
|
||||
formatter.BeginArray("items");
|
||||
// ... add items
|
||||
formatter.EndArray();
|
||||
formatter.EndObject();
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace handlers
|
||||
} // namespace cli
|
||||
} // namespace yaze
|
||||
```
|
||||
|
||||
### 3. Register in Factory
|
||||
|
||||
Add to `command_handlers.cc`:
|
||||
|
||||
```cpp
|
||||
#include "cli/handlers/new_feature_commands.h"
|
||||
|
||||
std::vector<std::unique_ptr<resources::CommandHandler>> CreateCliCommandHandlers() {
|
||||
// ... existing handlers ...
|
||||
handlers.push_back(std::make_unique<NewFeatureCommandHandler>());
|
||||
return handlers;
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Add Forward Declaration
|
||||
|
||||
Add to `command_handlers.h`:
|
||||
|
||||
```cpp
|
||||
// Forward declarations for command handler classes
|
||||
class NewFeatureCommandHandler;
|
||||
```
|
||||
|
||||
## Command Handler Base Class
|
||||
|
||||
The `resources::CommandHandler` base class provides:
|
||||
|
||||
### Lifecycle Methods
|
||||
|
||||
- `Run(args, rom_context)` - Main entry point that orchestrates the full command execution
|
||||
- `ValidateArgs(parser)` - Override to validate command arguments
|
||||
- `Execute(rom, parser, formatter)` - Override to implement command logic
|
||||
|
||||
### Helper Methods
|
||||
|
||||
- `GetUsage()` - Return usage string for help
|
||||
- `GetName()` - Return command name
|
||||
- `GetDescription()` - Return brief description
|
||||
- `RequiresLabels()` - Return true if command needs ROM labels loaded
|
||||
- `GetDefaultFormat()` - Return "json" or "text" for default output
|
||||
- `GetOutputTitle()` - Return title for output object
|
||||
|
||||
## Argument Parsing
|
||||
|
||||
The `ArgumentParser` class handles common CLI patterns:
|
||||
|
||||
```cpp
|
||||
// Get string argument
|
||||
auto value = parser.GetString("arg_name").value_or("default");
|
||||
|
||||
// Get integer (supports hex with 0x prefix)
|
||||
auto count = parser.GetInt("count").value_or(10);
|
||||
|
||||
// Get hex value
|
||||
auto address = parser.GetHex("address").value();
|
||||
|
||||
// Check for flag
|
||||
if (parser.HasFlag("verbose")) { ... }
|
||||
|
||||
// Get positional arguments
|
||||
auto positional = parser.GetPositional();
|
||||
|
||||
// Require specific arguments
|
||||
RETURN_IF_ERROR(parser.RequireArgs({"required1", "required2"}));
|
||||
```
|
||||
|
||||
## Output Formatting
|
||||
|
||||
The `OutputFormatter` class provides consistent JSON/text output:
|
||||
|
||||
```cpp
|
||||
// Begin object
|
||||
formatter.BeginObject("Title");
|
||||
|
||||
// Add fields
|
||||
formatter.AddField("string_field", "value");
|
||||
formatter.AddField("int_field", 42);
|
||||
formatter.AddField("bool_field", true);
|
||||
formatter.AddHexField("address", 0x1234, 4); // Width in digits
|
||||
|
||||
// Arrays
|
||||
formatter.BeginArray("items");
|
||||
for (const auto& item : items) {
|
||||
formatter.AddArrayItem(absl::StrFormat("Item %d", i));
|
||||
}
|
||||
formatter.EndArray();
|
||||
|
||||
// Nested objects
|
||||
formatter.BeginObject("nested");
|
||||
formatter.AddField("nested_field", "value");
|
||||
formatter.EndObject();
|
||||
|
||||
// End object
|
||||
formatter.EndObject();
|
||||
```
|
||||
|
||||
## Integration with Public API
|
||||
|
||||
Command handlers are designed to work alongside the public C API defined in `incl/yaze.h` and `incl/zelda.h`.
|
||||
|
||||
- Handlers use internal C++ classes from `app/zelda3/`
|
||||
- Output structures align with C API data types where possible
|
||||
- Future: C API bridge will expose commands to external applications
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Keep handlers focused** - One command per handler class
|
||||
2. **Use existing zelda3 classes** - Don't duplicate ROM parsing logic
|
||||
3. **Validate inputs early** - Use `ValidateArgs()` to catch errors
|
||||
4. **Provide good error messages** - Return descriptive `absl::Status` errors
|
||||
5. **Support both JSON and text** - Format output using `OutputFormatter`
|
||||
6. **Document parameters** - Include full usage string in `GetUsage()`
|
||||
7. **Test with agents** - Commands should be AI-friendly
|
||||
8. **Mark unused rom parameter** - Use `Rom* /*rom*/` if not needed
|
||||
|
||||
## Testing
|
||||
|
||||
Test commands using the CLI:
|
||||
|
||||
```bash
|
||||
# Direct command execution
|
||||
./build/bin/z3ed agent sprite-list --limit 10 --format json --rom zelda3.sfc
|
||||
|
||||
# Via simple-chat interface
|
||||
./build/bin/z3ed agent simple-chat --rom zelda3.sfc
|
||||
> sprite-list --limit 5
|
||||
|
||||
# In agent test suite
|
||||
./build/bin/z3ed agent test-conversation --rom zelda3.sfc
|
||||
```
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
- [ ] C API bridge for external language bindings
|
||||
- [ ] Command auto-discovery and registration
|
||||
- [ ] Per-command help system
|
||||
- [ ] Command aliases and shortcuts
|
||||
- [ ] Batch command execution
|
||||
- [ ] Command pipelines (output of one → input of another)
|
||||
- [ ] Interactive command REPL improvements
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include "cli/handlers/commands.h"
|
||||
#include "cli/handlers/agent/todo_commands.h"
|
||||
#include "cli/cli.h"
|
||||
|
||||
@@ -8,19 +7,70 @@
|
||||
#include "absl/flags/declare.h"
|
||||
#include "absl/flags/flag.h"
|
||||
#include "absl/status/status.h"
|
||||
#include "cli/handlers/agent/common.h"
|
||||
#include "cli/handlers/agent/todo_commands.h"
|
||||
#include "cli/handlers/agent/simple_chat_command.h"
|
||||
#include "cli/handlers/tools/resource_commands.h"
|
||||
#include "cli/handlers/game/dungeon_commands.h"
|
||||
#include "cli/handlers/game/overworld_commands.h"
|
||||
#include "cli/handlers/tools/gui_commands.h"
|
||||
#include "cli/handlers/tools/emulator_commands.h"
|
||||
|
||||
ABSL_DECLARE_FLAG(bool, quiet);
|
||||
|
||||
namespace yaze {
|
||||
namespace cli {
|
||||
|
||||
// Forward declarations from general_commands.cc
|
||||
namespace agent {
|
||||
absl::Status HandlePlanCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleTestCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleTestConversationCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleGuiCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleLearnCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleListCommand();
|
||||
absl::Status HandleDescribeCommand(const std::vector<std::string>& args);
|
||||
|
||||
// Wrapper functions to call CommandHandlers
|
||||
absl::Status HandleResourceListCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
handlers::ResourceListCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleResourceSearchCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
handlers::ResourceSearchCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleDungeonListSpritesCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
handlers::DungeonListSpritesCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleDungeonDescribeRoomCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
handlers::DungeonDescribeRoomCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleOverworldFindTileCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
handlers::OverworldFindTileCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleOverworldDescribeMapCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
handlers::OverworldDescribeMapCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleOverworldListWarpsCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
handlers::OverworldListWarpsCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
} // namespace agent
|
||||
|
||||
namespace {
|
||||
|
||||
// Forward declarations for functions implemented in other files
|
||||
// Function declarations moved to commands.h
|
||||
|
||||
// Use handlers from command_wrappers.cc
|
||||
using namespace yaze::cli::handlers;
|
||||
|
||||
constexpr absl::string_view kUsage =
|
||||
"Usage: agent <subcommand> [options]\n"
|
||||
"\n"
|
||||
@@ -98,13 +148,11 @@ absl::Status HandleAgentCommand(const std::vector<std::string>& arg_vec) {
|
||||
const std::string& subcommand = arg_vec[0];
|
||||
std::vector<std::string> subcommand_args(arg_vec.begin() + 1, arg_vec.end());
|
||||
|
||||
// TODO: Implement proper ROM context handling
|
||||
// For now, return unimplemented for commands that require ROM context
|
||||
if (subcommand == "run") {
|
||||
return absl::UnimplementedError("Agent run command requires ROM context - not yet implemented");
|
||||
}
|
||||
if (subcommand == "plan") {
|
||||
return HandlePlanCommand(subcommand_args);
|
||||
return agent::HandlePlanCommand(subcommand_args);
|
||||
}
|
||||
if (subcommand == "diff") {
|
||||
return absl::UnimplementedError("Agent diff command requires ROM context - not yet implemented");
|
||||
@@ -113,19 +161,19 @@ absl::Status HandleAgentCommand(const std::vector<std::string>& arg_vec) {
|
||||
return absl::UnimplementedError("Agent accept command requires ROM context - not yet implemented");
|
||||
}
|
||||
if (subcommand == "test") {
|
||||
return HandleTestCommand(subcommand_args);
|
||||
return agent::HandleTestCommand(subcommand_args);
|
||||
}
|
||||
if (subcommand == "test-conversation") {
|
||||
return HandleTestConversationCommand(subcommand_args);
|
||||
return agent::HandleTestConversationCommand(subcommand_args);
|
||||
}
|
||||
if (subcommand == "gui") {
|
||||
return HandleGuiCommand(subcommand_args);
|
||||
return agent::HandleGuiCommand(subcommand_args);
|
||||
}
|
||||
if (subcommand == "learn") {
|
||||
return HandleLearnCommand(subcommand_args);
|
||||
return agent::HandleLearnCommand(subcommand_args);
|
||||
}
|
||||
if (subcommand == "list") {
|
||||
return HandleListCommand();
|
||||
return agent::HandleListCommand();
|
||||
}
|
||||
if (subcommand == "commit") {
|
||||
return absl::UnimplementedError("Agent commit command requires ROM context - not yet implemented");
|
||||
@@ -134,60 +182,57 @@ absl::Status HandleAgentCommand(const std::vector<std::string>& arg_vec) {
|
||||
return absl::UnimplementedError("Agent revert command requires ROM context - not yet implemented");
|
||||
}
|
||||
if (subcommand == "describe") {
|
||||
return HandleDescribeCommand(subcommand_args);
|
||||
return agent::HandleDescribeCommand(subcommand_args);
|
||||
}
|
||||
if (subcommand == "resource-list") {
|
||||
return HandleResourceListCommand(subcommand_args, nullptr);
|
||||
return agent::HandleResourceListCommand(subcommand_args, nullptr);
|
||||
}
|
||||
if (subcommand == "resource-search") {
|
||||
return HandleResourceSearchCommand(subcommand_args, nullptr);
|
||||
return agent::HandleResourceSearchCommand(subcommand_args, nullptr);
|
||||
}
|
||||
if (subcommand == "dungeon-list-sprites") {
|
||||
return HandleDungeonListSpritesCommand(subcommand_args, nullptr);
|
||||
return agent::HandleDungeonListSpritesCommand(subcommand_args, nullptr);
|
||||
}
|
||||
if (subcommand == "dungeon-describe-room") {
|
||||
return HandleDungeonDescribeRoomCommand(subcommand_args, nullptr);
|
||||
return agent::HandleDungeonDescribeRoomCommand(subcommand_args, nullptr);
|
||||
}
|
||||
if (subcommand == "overworld-find-tile") {
|
||||
return HandleOverworldFindTileCommand(subcommand_args, nullptr);
|
||||
return agent::HandleOverworldFindTileCommand(subcommand_args, nullptr);
|
||||
}
|
||||
if (subcommand == "overworld-describe-map") {
|
||||
return HandleOverworldDescribeMapCommand(subcommand_args, nullptr);
|
||||
return agent::HandleOverworldDescribeMapCommand(subcommand_args, nullptr);
|
||||
}
|
||||
if (subcommand == "overworld-list-warps") {
|
||||
return HandleOverworldListWarpsCommand(subcommand_args, nullptr);
|
||||
}
|
||||
if (subcommand == "chat") {
|
||||
return absl::UnimplementedError("Agent chat command requires ROM context - not yet implemented");
|
||||
}
|
||||
if (subcommand == "simple-chat") {
|
||||
return absl::UnimplementedError("Agent simple-chat command requires ROM context - not yet implemented");
|
||||
}
|
||||
if (subcommand == "todo") {
|
||||
return handlers::HandleTodoCommand(subcommand_args);
|
||||
return agent::HandleOverworldListWarpsCommand(subcommand_args, nullptr);
|
||||
}
|
||||
// if (subcommand == "chat") {
|
||||
// return absl::UnimplementedError("Agent chat command requires ROM context - not yet implemented");
|
||||
// }
|
||||
// if (subcommand == "todo") {
|
||||
// return handlers::HandleTodoCommand(subcommand_args);
|
||||
// }
|
||||
|
||||
// Hex manipulation commands
|
||||
if (subcommand == "hex-read") {
|
||||
return HandleHexRead(subcommand_args, nullptr);
|
||||
}
|
||||
if (subcommand == "hex-write") {
|
||||
return HandleHexWrite(subcommand_args, nullptr);
|
||||
}
|
||||
if (subcommand == "hex-search") {
|
||||
return HandleHexSearch(subcommand_args, nullptr);
|
||||
}
|
||||
// // Hex manipulation commands
|
||||
// if (subcommand == "hex-read") {
|
||||
// return HandleHexRead(subcommand_args, nullptr);
|
||||
// }
|
||||
// if (subcommand == "hex-write") {
|
||||
// return HandleHexWrite(subcommand_args, nullptr);
|
||||
// }
|
||||
// if (subcommand == "hex-search") {
|
||||
// return HandleHexSearch(subcommand_args, nullptr);
|
||||
// }
|
||||
|
||||
// Palette manipulation commands
|
||||
if (subcommand == "palette-get-colors") {
|
||||
return HandlePaletteGetColors(subcommand_args, nullptr);
|
||||
}
|
||||
if (subcommand == "palette-set-color") {
|
||||
return HandlePaletteSetColor(subcommand_args, nullptr);
|
||||
}
|
||||
if (subcommand == "palette-analyze") {
|
||||
return HandlePaletteAnalyze(subcommand_args, nullptr);
|
||||
}
|
||||
// // Palette manipulation commands
|
||||
// if (subcommand == "palette-get-colors") {
|
||||
// return HandlePaletteGetColors(subcommand_args, nullptr);
|
||||
// }
|
||||
// if (subcommand == "palette-set-color") {
|
||||
// return HandlePaletteSetColor(subcommand_args, nullptr);
|
||||
// }
|
||||
// if (subcommand == "palette-analyze") {
|
||||
// return HandlePaletteAnalyze(subcommand_args, nullptr);
|
||||
// }
|
||||
|
||||
return absl::InvalidArgumentError(std::string(kUsage));
|
||||
}
|
||||
|
||||
37
src/cli/handlers/agent/simple_chat_command.cc
Normal file
37
src/cli/handlers/agent/simple_chat_command.cc
Normal file
@@ -0,0 +1,37 @@
|
||||
#include "cli/handlers/agent/simple_chat_command.h"
|
||||
#include "cli/service/agent/simple_chat_session.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace cli {
|
||||
namespace handlers {
|
||||
|
||||
absl::Status SimpleChatCommandHandler::Execute(
|
||||
Rom* rom, const resources::ArgumentParser& parser,
|
||||
resources::OutputFormatter& formatter) {
|
||||
|
||||
agent::SimpleChatSession session;
|
||||
session.SetRomContext(rom);
|
||||
|
||||
// Configure session from parser
|
||||
agent::AgentConfig config;
|
||||
if (parser.HasFlag("verbose")) {
|
||||
config.verbose = true;
|
||||
}
|
||||
if (auto format = parser.GetString("format")) {
|
||||
// Simplified format handling
|
||||
}
|
||||
session.SetConfig(config);
|
||||
|
||||
if (auto file = parser.GetString("file")) {
|
||||
return session.RunBatch(*file);
|
||||
} else if (auto prompt = parser.GetString("prompt")) {
|
||||
std::string response;
|
||||
return session.SendAndWaitForResponse(*prompt, &response);
|
||||
} else {
|
||||
return session.RunInteractive();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace handlers
|
||||
} // namespace cli
|
||||
} // namespace yaze
|
||||
31
src/cli/handlers/agent/simple_chat_command.h
Normal file
31
src/cli/handlers/agent/simple_chat_command.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef YAZE_CLI_HANDLERS_AGENT_SIMPLE_CHAT_COMMAND_H_
|
||||
#define YAZE_CLI_HANDLERS_AGENT_SIMPLE_CHAT_COMMAND_H_
|
||||
|
||||
#include "cli/service/resources/command_handler.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace cli {
|
||||
namespace handlers {
|
||||
|
||||
class SimpleChatCommandHandler : public resources::CommandHandler {
|
||||
public:
|
||||
std::string GetName() const { return "simple-chat"; }
|
||||
std::string GetDescription() const { return "Simple text-based chat with the AI agent."; }
|
||||
std::string GetUsage() const override {
|
||||
return "simple-chat [--prompt <message>] [--file <path>] [--format <format>]";
|
||||
}
|
||||
|
||||
protected:
|
||||
absl::Status ValidateArgs(const resources::ArgumentParser& parser) override {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status Execute(Rom* rom, const resources::ArgumentParser& parser,
|
||||
resources::OutputFormatter& formatter) override;
|
||||
};
|
||||
|
||||
} // namespace handlers
|
||||
} // namespace cli
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_CLI_HANDLERS_AGENT_SIMPLE_CHAT_COMMAND_H_
|
||||
@@ -60,9 +60,14 @@ std::vector<std::unique_ptr<resources::CommandHandler>> CreateCliCommandHandlers
|
||||
return handlers;
|
||||
}
|
||||
|
||||
#include "cli/handlers/agent/simple_chat_command.h"
|
||||
|
||||
std::vector<std::unique_ptr<resources::CommandHandler>> CreateAgentCommandHandlers() {
|
||||
std::vector<std::unique_ptr<resources::CommandHandler>> handlers;
|
||||
|
||||
// Add simple-chat command handler
|
||||
handlers.push_back(std::make_unique<SimpleChatCommandHandler>());
|
||||
|
||||
// Resource inspection tools
|
||||
handlers.push_back(std::make_unique<ResourceListCommandHandler>());
|
||||
handlers.push_back(std::make_unique<ResourceSearchCommandHandler>());
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include "cli/service/resources/command_handler.h"
|
||||
|
||||
#include "cli/handlers/agent/simple_chat_command.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace cli {
|
||||
namespace handlers {
|
||||
|
||||
@@ -1,328 +0,0 @@
|
||||
#include "cli/handlers/commands.h"
|
||||
|
||||
#include "cli/handlers/tools/resource_commands.h"
|
||||
#include "cli/handlers/game/dungeon_commands.h"
|
||||
#include "cli/handlers/game/overworld_commands.h"
|
||||
#include "cli/handlers/game/message_commands.h"
|
||||
#include "cli/handlers/game/dialogue_commands.h"
|
||||
#include "cli/handlers/game/music_commands.h"
|
||||
#include "cli/handlers/graphics/hex_commands.h"
|
||||
#include "cli/handlers/graphics/palette_commands.h"
|
||||
// #include "cli/handlers/graphics/sprite_commands.h" // Implementations not available
|
||||
#include "cli/handlers/tools/gui_commands.h"
|
||||
#include "cli/handlers/tools/emulator_commands.h"
|
||||
// #include "cli/handlers/rom/rom_commands.h" // Not used in stubs
|
||||
// #include "cli/handlers/rom/project_commands.h" // Not used in stubs
|
||||
|
||||
namespace yaze {
|
||||
namespace cli {
|
||||
namespace handlers {
|
||||
|
||||
// Resource commands
|
||||
absl::Status HandleResourceListCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
ResourceListCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleResourceSearchCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
ResourceSearchCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
// Dungeon commands
|
||||
absl::Status HandleDungeonListSpritesCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
DungeonListSpritesCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleDungeonDescribeRoomCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
DungeonDescribeRoomCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleDungeonExportRoomCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
DungeonExportRoomCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleDungeonListObjectsCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
DungeonListObjectsCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleDungeonGetRoomTilesCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
DungeonGetRoomTilesCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleDungeonSetRoomPropertyCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
DungeonSetRoomPropertyCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
// Overworld commands
|
||||
absl::Status HandleOverworldFindTileCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
OverworldFindTileCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleOverworldDescribeMapCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
OverworldDescribeMapCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleOverworldListWarpsCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
OverworldListWarpsCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleOverworldListSpritesCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
OverworldListSpritesCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleOverworldGetEntranceCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
OverworldGetEntranceCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleOverworldTileStatsCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
OverworldTileStatsCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
// Message commands
|
||||
absl::Status HandleMessageListCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
MessageListCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleMessageReadCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
MessageReadCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleMessageSearchCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
MessageSearchCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
// Dialogue commands
|
||||
absl::Status HandleDialogueListCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
DialogueListCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleDialogueReadCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
DialogueReadCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleDialogueSearchCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
DialogueSearchCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
// Music commands
|
||||
absl::Status HandleMusicListCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
MusicListCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleMusicInfoCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
MusicInfoCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleMusicTracksCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
MusicTracksCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
// Sprite commands (stubs - implementations not available)
|
||||
absl::Status HandleSpriteListCommand(const std::vector<std::string>& /*args*/, Rom* /*rom*/) {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status HandleSpritePropertiesCommand(const std::vector<std::string>& /*args*/, Rom* /*rom*/) {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status HandleSpritePaletteCommand(const std::vector<std::string>& /*args*/, Rom* /*rom*/) {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
// GUI commands
|
||||
absl::Status HandleGuiPlaceTileCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
GuiPlaceTileCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleGuiClickCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
GuiClickCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleGuiDiscoverToolCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
GuiDiscoverToolCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleGuiScreenshotCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
GuiScreenshotCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
// Emulator commands
|
||||
absl::Status HandleEmulatorStepCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
EmulatorStepCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleEmulatorRunCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
EmulatorRunCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleEmulatorPauseCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
EmulatorPauseCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleEmulatorResetCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
EmulatorResetCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleEmulatorGetStateCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
EmulatorGetStateCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleEmulatorSetBreakpointCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
EmulatorSetBreakpointCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleEmulatorClearBreakpointCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
EmulatorClearBreakpointCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleEmulatorListBreakpointsCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
EmulatorListBreakpointsCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleEmulatorReadMemoryCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
EmulatorReadMemoryCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleEmulatorWriteMemoryCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
EmulatorWriteMemoryCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleEmulatorGetRegistersCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
EmulatorGetRegistersCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleEmulatorGetMetricsCommand(const std::vector<std::string>& args, Rom* rom) {
|
||||
EmulatorGetMetricsCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
// Hex commands
|
||||
absl::Status HandleHexRead(const std::vector<std::string>& args, Rom* rom) {
|
||||
HexReadCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleHexWrite(const std::vector<std::string>& args, Rom* rom) {
|
||||
HexWriteCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandleHexSearch(const std::vector<std::string>& args, Rom* rom) {
|
||||
HexSearchCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
// Palette commands
|
||||
absl::Status HandlePaletteGetColors(const std::vector<std::string>& args, Rom* rom) {
|
||||
PaletteGetColorsCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandlePaletteSetColor(const std::vector<std::string>& args, Rom* rom) {
|
||||
PaletteSetColorCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
absl::Status HandlePaletteAnalyze(const std::vector<std::string>& args, Rom* rom) {
|
||||
PaletteAnalyzeCommandHandler handler;
|
||||
return handler.Run(args, rom);
|
||||
}
|
||||
|
||||
// Agent-specific commands (stubs for now)
|
||||
absl::Status HandleRunCommand(const std::vector<std::string>& /*args*/, Rom& /*rom*/) {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status HandlePlanCommand(const std::vector<std::string>& /*args*/) {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status HandleDiffCommand(Rom& /*rom*/, const std::vector<std::string>& /*args*/) {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status HandleAcceptCommand(const std::vector<std::string>& /*args*/, Rom& /*rom*/) {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status HandleTestCommand(const std::vector<std::string>& /*args*/) {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status HandleGuiCommand(const std::vector<std::string>& /*args*/) {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status HandleLearnCommand(const std::vector<std::string>& /*args*/) {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status HandleListCommand() {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status HandleCommitCommand(Rom& /*rom*/) {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status HandleRevertCommand(Rom& /*rom*/) {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status HandleDescribeCommand(const std::vector<std::string>& /*arg_vec*/) {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status HandleChatCommand(Rom& /*rom*/) {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status HandleSimpleChatCommand(const std::vector<std::string>&, Rom* /*rom*/, bool /*quiet*/) {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status HandleTestConversationCommand(const std::vector<std::string>& /*arg_vec*/) {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace handlers
|
||||
} // namespace cli
|
||||
} // namespace yaze
|
||||
@@ -1,194 +0,0 @@
|
||||
#ifndef YAZE_CLI_HANDLERS_COMMANDS_H_
|
||||
#define YAZE_CLI_HANDLERS_COMMANDS_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
|
||||
namespace yaze {
|
||||
class Rom;
|
||||
|
||||
namespace cli {
|
||||
namespace handlers {
|
||||
|
||||
absl::Status HandleRunCommand(const std::vector<std::string>& args,
|
||||
Rom& rom);
|
||||
absl::Status HandlePlanCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleDiffCommand(Rom& rom,
|
||||
const std::vector<std::string>& args);
|
||||
absl::Status HandleAcceptCommand(const std::vector<std::string>& args, Rom& rom);
|
||||
absl::Status HandleTestCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleGuiCommand(const std::vector<std::string>& args);
|
||||
absl::Status HandleLearnCommand(const std::vector<std::string>& args = {});
|
||||
absl::Status HandleListCommand();
|
||||
absl::Status HandleCommitCommand(Rom& rom);
|
||||
absl::Status HandleRevertCommand(Rom& rom);
|
||||
absl::Status HandleDescribeCommand(const std::vector<std::string>& arg_vec);
|
||||
absl::Status HandleResourceListCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleResourceSearchCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleDungeonListSpritesCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleDungeonDescribeRoomCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleOverworldFindTileCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleOverworldDescribeMapCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleOverworldListWarpsCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleOverworldListSpritesCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleOverworldGetEntranceCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleOverworldTileStatsCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleMessageListCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleMessageReadCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleMessageSearchCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
|
||||
// GUI Automation Tools
|
||||
absl::Status HandleGuiPlaceTileCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleGuiClickCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleGuiDiscoverToolCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleGuiScreenshotCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
|
||||
// Dialogue Inspection Tools
|
||||
absl::Status HandleDialogueListCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleDialogueReadCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleDialogueSearchCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
|
||||
// Music Data Tools
|
||||
absl::Status HandleMusicListCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleMusicInfoCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleMusicTracksCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
|
||||
// Sprite Property Tools
|
||||
absl::Status HandleSpriteListCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleSpritePropertiesCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleSpritePaletteCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleChatCommand(Rom& rom);
|
||||
absl::Status HandleSimpleChatCommand(const std::vector<std::string>&, Rom* rom, bool quiet);
|
||||
absl::Status HandleTestConversationCommand(
|
||||
const std::vector<std::string>& arg_vec);
|
||||
|
||||
// Agent command handler
|
||||
absl::Status HandleAgentCommand(const std::vector<std::string>& arg_vec);
|
||||
|
||||
// Hex manipulation commands
|
||||
absl::Status HandleHexRead(const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleHexWrite(const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleHexSearch(const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
|
||||
// Palette manipulation commands
|
||||
absl::Status HandlePaletteGetColors(const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandlePaletteSetColor(const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandlePaletteAnalyze(const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
|
||||
// Dungeon editing commands
|
||||
absl::Status HandleDungeonExportRoomCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleDungeonListObjectsCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleDungeonGetRoomTilesCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleDungeonSetRoomPropertyCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
|
||||
// Emulator & Debugger commands
|
||||
absl::Status HandleEmulatorStepCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleEmulatorRunCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleEmulatorPauseCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleEmulatorResetCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleEmulatorGetStateCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleEmulatorSetBreakpointCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleEmulatorClearBreakpointCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleEmulatorListBreakpointsCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleEmulatorReadMemoryCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleEmulatorWriteMemoryCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleEmulatorGetRegistersCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleEmulatorGetMetricsCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
|
||||
} // namespace handlers
|
||||
} // namespace cli
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_CLI_HANDLERS_COMMANDS_H_
|
||||
@@ -183,6 +183,7 @@ ConversationalAgentService::ConversationalAgentService(const AgentConfig& config
|
||||
|
||||
void ConversationalAgentService::SetRomContext(Rom* rom) {
|
||||
rom_context_ = rom;
|
||||
|
||||
tool_dispatcher_.SetRomContext(rom_context_);
|
||||
if (ai_service_) {
|
||||
ai_service_->SetRomContext(rom_context_);
|
||||
|
||||
@@ -6,11 +6,12 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/time/time.h"
|
||||
#include "cli/service/ai/ai_service.h"
|
||||
#include "cli/service/agent/tool_dispatcher.h"
|
||||
#include "cli/service/agent/proposal_executor.h"
|
||||
#include "cli/service/agent/tool_dispatcher.h"
|
||||
|
||||
namespace yaze {
|
||||
|
||||
|
||||
@@ -1,139 +1,282 @@
|
||||
#include "cli/service/agent/tool_dispatcher.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/match.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "cli/handlers/commands.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "cli/handlers/game/dialogue_commands.h"
|
||||
#include "cli/handlers/game/dungeon_commands.h"
|
||||
#include "cli/handlers/game/message_commands.h"
|
||||
#include "cli/handlers/game/music_commands.h"
|
||||
#include "cli/handlers/game/overworld_commands.h"
|
||||
#include "cli/handlers/graphics/sprite_commands.h"
|
||||
#include "cli/handlers/tools/emulator_commands.h"
|
||||
#include "cli/handlers/tools/gui_commands.h"
|
||||
#include "cli/handlers/tools/resource_commands.h"
|
||||
#include "cli/service/resources/command_context.h"
|
||||
#include "cli/util/terminal_colors.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace cli {
|
||||
namespace agent {
|
||||
|
||||
absl::StatusOr<std::string> ToolDispatcher::Dispatch(
|
||||
const ToolCall& tool_call) {
|
||||
namespace {
|
||||
|
||||
// Map tool name to handler type
|
||||
ToolCallType GetToolCallType(const std::string& tool_name) {
|
||||
// Resource commands
|
||||
if (tool_name == "resource-list") return ToolCallType::kResourceList;
|
||||
if (tool_name == "resource-search") return ToolCallType::kResourceSearch;
|
||||
|
||||
// Dungeon commands
|
||||
if (tool_name == "dungeon-list-sprites") return ToolCallType::kDungeonListSprites;
|
||||
if (tool_name == "dungeon-describe-room") return ToolCallType::kDungeonDescribeRoom;
|
||||
if (tool_name == "dungeon-export-room") return ToolCallType::kDungeonExportRoom;
|
||||
if (tool_name == "dungeon-list-objects") return ToolCallType::kDungeonListObjects;
|
||||
if (tool_name == "dungeon-get-room-tiles") return ToolCallType::kDungeonGetRoomTiles;
|
||||
if (tool_name == "dungeon-set-room-property") return ToolCallType::kDungeonSetRoomProperty;
|
||||
|
||||
// Overworld commands
|
||||
if (tool_name == "overworld-find-tile") return ToolCallType::kOverworldFindTile;
|
||||
if (tool_name == "overworld-describe-map") return ToolCallType::kOverworldDescribeMap;
|
||||
if (tool_name == "overworld-list-warps") return ToolCallType::kOverworldListWarps;
|
||||
if (tool_name == "overworld-list-sprites") return ToolCallType::kOverworldListSprites;
|
||||
if (tool_name == "overworld-get-entrance") return ToolCallType::kOverworldGetEntrance;
|
||||
if (tool_name == "overworld-tile-stats") return ToolCallType::kOverworldTileStats;
|
||||
|
||||
// Message & Dialogue commands
|
||||
if (tool_name == "message-list") return ToolCallType::kMessageList;
|
||||
if (tool_name == "message-read") return ToolCallType::kMessageRead;
|
||||
if (tool_name == "message-search") return ToolCallType::kMessageSearch;
|
||||
if (tool_name == "dialogue-list") return ToolCallType::kDialogueList;
|
||||
if (tool_name == "dialogue-read") return ToolCallType::kDialogueRead;
|
||||
if (tool_name == "dialogue-search") return ToolCallType::kDialogueSearch;
|
||||
|
||||
// GUI Automation commands
|
||||
if (tool_name == "gui-place-tile") return ToolCallType::kGuiPlaceTile;
|
||||
if (tool_name == "gui-click") return ToolCallType::kGuiClick;
|
||||
if (tool_name == "gui-discover-tool") return ToolCallType::kGuiDiscover;
|
||||
if (tool_name == "gui-screenshot") return ToolCallType::kGuiScreenshot;
|
||||
|
||||
// Music commands
|
||||
if (tool_name == "music-list") return ToolCallType::kMusicList;
|
||||
if (tool_name == "music-info") return ToolCallType::kMusicInfo;
|
||||
if (tool_name == "music-tracks") return ToolCallType::kMusicTracks;
|
||||
|
||||
// Sprite commands
|
||||
if (tool_name == "sprite-list") return ToolCallType::kSpriteList;
|
||||
if (tool_name == "sprite-properties") return ToolCallType::kSpriteProperties;
|
||||
if (tool_name == "sprite-palette") return ToolCallType::kSpritePalette;
|
||||
|
||||
// Emulator & Debugger commands
|
||||
if (tool_name == "emulator-step") return ToolCallType::kEmulatorStep;
|
||||
if (tool_name == "emulator-run") return ToolCallType::kEmulatorRun;
|
||||
if (tool_name == "emulator-pause") return ToolCallType::kEmulatorPause;
|
||||
if (tool_name == "emulator-reset") return ToolCallType::kEmulatorReset;
|
||||
if (tool_name == "emulator-get-state") return ToolCallType::kEmulatorGetState;
|
||||
if (tool_name == "emulator-set-breakpoint") return ToolCallType::kEmulatorSetBreakpoint;
|
||||
if (tool_name == "emulator-clear-breakpoint") return ToolCallType::kEmulatorClearBreakpoint;
|
||||
if (tool_name == "emulator-list-breakpoints") return ToolCallType::kEmulatorListBreakpoints;
|
||||
if (tool_name == "emulator-read-memory") return ToolCallType::kEmulatorReadMemory;
|
||||
if (tool_name == "emulator-write-memory") return ToolCallType::kEmulatorWriteMemory;
|
||||
if (tool_name == "emulator-get-registers") return ToolCallType::kEmulatorGetRegisters;
|
||||
if (tool_name == "emulator-get-metrics") return ToolCallType::kEmulatorGetMetrics;
|
||||
|
||||
return ToolCallType::kUnknown;
|
||||
}
|
||||
|
||||
// Create the appropriate command handler for a tool call type
|
||||
std::unique_ptr<resources::CommandHandler> CreateHandler(ToolCallType type) {
|
||||
using namespace yaze::cli::handlers;
|
||||
|
||||
std::vector<std::string> args;
|
||||
switch (type) {
|
||||
// Resource commands
|
||||
case ToolCallType::kResourceList:
|
||||
return std::make_unique<ResourceListCommandHandler>();
|
||||
case ToolCallType::kResourceSearch:
|
||||
return std::make_unique<ResourceSearchCommandHandler>();
|
||||
|
||||
// Dungeon commands
|
||||
case ToolCallType::kDungeonListSprites:
|
||||
return std::make_unique<DungeonListSpritesCommandHandler>();
|
||||
case ToolCallType::kDungeonDescribeRoom:
|
||||
return std::make_unique<DungeonDescribeRoomCommandHandler>();
|
||||
case ToolCallType::kDungeonExportRoom:
|
||||
return std::make_unique<DungeonExportRoomCommandHandler>();
|
||||
case ToolCallType::kDungeonListObjects:
|
||||
return std::make_unique<DungeonListObjectsCommandHandler>();
|
||||
case ToolCallType::kDungeonGetRoomTiles:
|
||||
return std::make_unique<DungeonGetRoomTilesCommandHandler>();
|
||||
case ToolCallType::kDungeonSetRoomProperty:
|
||||
return std::make_unique<DungeonSetRoomPropertyCommandHandler>();
|
||||
|
||||
// Overworld commands
|
||||
case ToolCallType::kOverworldFindTile:
|
||||
return std::make_unique<OverworldFindTileCommandHandler>();
|
||||
case ToolCallType::kOverworldDescribeMap:
|
||||
return std::make_unique<OverworldDescribeMapCommandHandler>();
|
||||
case ToolCallType::kOverworldListWarps:
|
||||
return std::make_unique<OverworldListWarpsCommandHandler>();
|
||||
case ToolCallType::kOverworldListSprites:
|
||||
return std::make_unique<OverworldListSpritesCommandHandler>();
|
||||
case ToolCallType::kOverworldGetEntrance:
|
||||
return std::make_unique<OverworldGetEntranceCommandHandler>();
|
||||
case ToolCallType::kOverworldTileStats:
|
||||
return std::make_unique<OverworldTileStatsCommandHandler>();
|
||||
|
||||
// Message & Dialogue commands
|
||||
case ToolCallType::kMessageList:
|
||||
return std::make_unique<MessageListCommandHandler>();
|
||||
case ToolCallType::kMessageRead:
|
||||
return std::make_unique<MessageReadCommandHandler>();
|
||||
case ToolCallType::kMessageSearch:
|
||||
return std::make_unique<MessageSearchCommandHandler>();
|
||||
case ToolCallType::kDialogueList:
|
||||
return std::make_unique<DialogueListCommandHandler>();
|
||||
case ToolCallType::kDialogueRead:
|
||||
return std::make_unique<DialogueReadCommandHandler>();
|
||||
case ToolCallType::kDialogueSearch:
|
||||
return std::make_unique<DialogueSearchCommandHandler>();
|
||||
|
||||
// GUI Automation commands
|
||||
case ToolCallType::kGuiPlaceTile:
|
||||
return std::make_unique<GuiPlaceTileCommandHandler>();
|
||||
case ToolCallType::kGuiClick:
|
||||
return std::make_unique<GuiClickCommandHandler>();
|
||||
case ToolCallType::kGuiDiscover:
|
||||
return std::make_unique<GuiDiscoverToolCommandHandler>();
|
||||
case ToolCallType::kGuiScreenshot:
|
||||
return std::make_unique<GuiScreenshotCommandHandler>();
|
||||
|
||||
// Music commands
|
||||
case ToolCallType::kMusicList:
|
||||
return std::make_unique<MusicListCommandHandler>();
|
||||
case ToolCallType::kMusicInfo:
|
||||
return std::make_unique<MusicInfoCommandHandler>();
|
||||
case ToolCallType::kMusicTracks:
|
||||
return std::make_unique<MusicTracksCommandHandler>();
|
||||
|
||||
// Sprite commands
|
||||
case ToolCallType::kSpriteList:
|
||||
return std::make_unique<SpriteListCommandHandler>();
|
||||
case ToolCallType::kSpriteProperties:
|
||||
return std::make_unique<SpritePropertiesCommandHandler>();
|
||||
case ToolCallType::kSpritePalette:
|
||||
return std::make_unique<SpritePaletteCommandHandler>();
|
||||
|
||||
// Emulator & Debugger commands
|
||||
case ToolCallType::kEmulatorStep:
|
||||
return std::make_unique<EmulatorStepCommandHandler>();
|
||||
case ToolCallType::kEmulatorRun:
|
||||
return std::make_unique<EmulatorRunCommandHandler>();
|
||||
case ToolCallType::kEmulatorPause:
|
||||
return std::make_unique<EmulatorPauseCommandHandler>();
|
||||
case ToolCallType::kEmulatorReset:
|
||||
return std::make_unique<EmulatorResetCommandHandler>();
|
||||
case ToolCallType::kEmulatorGetState:
|
||||
return std::make_unique<EmulatorGetStateCommandHandler>();
|
||||
case ToolCallType::kEmulatorSetBreakpoint:
|
||||
return std::make_unique<EmulatorSetBreakpointCommandHandler>();
|
||||
case ToolCallType::kEmulatorClearBreakpoint:
|
||||
return std::make_unique<EmulatorClearBreakpointCommandHandler>();
|
||||
case ToolCallType::kEmulatorListBreakpoints:
|
||||
return std::make_unique<EmulatorListBreakpointsCommandHandler>();
|
||||
case ToolCallType::kEmulatorReadMemory:
|
||||
return std::make_unique<EmulatorReadMemoryCommandHandler>();
|
||||
case ToolCallType::kEmulatorWriteMemory:
|
||||
return std::make_unique<EmulatorWriteMemoryCommandHandler>();
|
||||
case ToolCallType::kEmulatorGetRegisters:
|
||||
return std::make_unique<EmulatorGetRegistersCommandHandler>();
|
||||
case ToolCallType::kEmulatorGetMetrics:
|
||||
return std::make_unique<EmulatorGetMetricsCommandHandler>();
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert tool call arguments map to command-line style vector
|
||||
std::vector<std::string> ConvertArgsToVector(
|
||||
const std::map<std::string, std::string>& args) {
|
||||
std::vector<std::string> result;
|
||||
|
||||
for (const auto& [key, value] : args) {
|
||||
// Convert to --key=value format
|
||||
result.push_back(absl::StrCat("--", key, "=", value));
|
||||
}
|
||||
|
||||
// Always request JSON format for tool calls (easier for AI to parse)
|
||||
bool has_format = false;
|
||||
for (const auto& [key, value] : tool_call.args) {
|
||||
args.push_back(absl::StrFormat("--%s", key));
|
||||
args.push_back(value);
|
||||
if (absl::EqualsIgnoreCase(key, "format")) {
|
||||
for (const auto& arg : result) {
|
||||
if (arg.find("--format=") == 0) {
|
||||
has_format = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_format) {
|
||||
args.push_back("--format");
|
||||
args.push_back("json");
|
||||
result.push_back("--format=json");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Capture stdout
|
||||
std::stringstream buffer;
|
||||
auto old_cout_buf = std::cout.rdbuf();
|
||||
std::cout.rdbuf(buffer.rdbuf());
|
||||
} // namespace
|
||||
|
||||
absl::Status status;
|
||||
if (tool_call.tool_name == "resource-list") {
|
||||
status = HandleResourceListCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "resource-search") {
|
||||
status = HandleResourceSearchCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "dungeon-list-sprites") {
|
||||
status = HandleDungeonListSpritesCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "dungeon-describe-room") {
|
||||
status = HandleDungeonDescribeRoomCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "overworld-find-tile") {
|
||||
status = HandleOverworldFindTileCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "overworld-describe-map") {
|
||||
status = HandleOverworldDescribeMapCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "overworld-list-warps") {
|
||||
status = HandleOverworldListWarpsCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "overworld-list-sprites") {
|
||||
status = HandleOverworldListSpritesCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "overworld-get-entrance") {
|
||||
status = HandleOverworldGetEntranceCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "overworld-tile-stats") {
|
||||
status = HandleOverworldTileStatsCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "message-list") {
|
||||
status = HandleMessageListCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "message-read") {
|
||||
status = HandleMessageReadCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "message-search") {
|
||||
status = HandleMessageSearchCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "gui-place-tile") {
|
||||
// GUI automation tool for placing tiles via test harness
|
||||
status = HandleGuiPlaceTileCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "gui-click") {
|
||||
status = HandleGuiClickCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "gui-discover") {
|
||||
status = HandleGuiDiscoverToolCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "gui-screenshot") {
|
||||
status = HandleGuiScreenshotCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "dialogue-list") {
|
||||
status = HandleDialogueListCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "dialogue-read") {
|
||||
status = HandleDialogueReadCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "dialogue-search") {
|
||||
status = HandleDialogueSearchCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "music-list") {
|
||||
status = HandleMusicListCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "music-info") {
|
||||
status = HandleMusicInfoCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "music-tracks") {
|
||||
status = HandleMusicTracksCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "sprite-list") {
|
||||
status = HandleSpriteListCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "sprite-properties") {
|
||||
status = HandleSpritePropertiesCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "sprite-palette") {
|
||||
status = HandleSpritePaletteCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "dungeon-export-room") {
|
||||
status = HandleDungeonExportRoomCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "dungeon-list-objects") {
|
||||
status = HandleDungeonListObjectsCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "dungeon-get-room-tiles") {
|
||||
status = HandleDungeonGetRoomTilesCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "dungeon-set-room-property") {
|
||||
status = HandleDungeonSetRoomPropertyCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "emulator-step") {
|
||||
status = HandleEmulatorStepCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "emulator-run") {
|
||||
status = HandleEmulatorRunCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "emulator-pause") {
|
||||
status = HandleEmulatorPauseCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "emulator-reset") {
|
||||
status = HandleEmulatorResetCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "emulator-get-state") {
|
||||
status = HandleEmulatorGetStateCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "emulator-set-breakpoint") {
|
||||
status = HandleEmulatorSetBreakpointCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "emulator-clear-breakpoint") {
|
||||
status = HandleEmulatorClearBreakpointCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "emulator-list-breakpoints") {
|
||||
status = HandleEmulatorListBreakpointsCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "emulator-read-memory") {
|
||||
status = HandleEmulatorReadMemoryCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "emulator-write-memory") {
|
||||
status = HandleEmulatorWriteMemoryCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "emulator-get-registers") {
|
||||
status = HandleEmulatorGetRegistersCommand(args, rom_context_);
|
||||
} else if (tool_call.tool_name == "emulator-get-metrics") {
|
||||
status = HandleEmulatorGetMetricsCommand(args, rom_context_);
|
||||
} else {
|
||||
status = absl::UnimplementedError(
|
||||
absl::StrFormat("Unknown tool: %s", tool_call.tool_name));
|
||||
absl::StatusOr<std::string> ToolDispatcher::Dispatch(const ToolCall& call) {
|
||||
// Determine tool call type
|
||||
ToolCallType type = GetToolCallType(call.tool_name);
|
||||
|
||||
if (type == ToolCallType::kUnknown) {
|
||||
return absl::InvalidArgumentError(
|
||||
absl::StrCat("Unknown tool: ", call.tool_name));
|
||||
}
|
||||
|
||||
|
||||
// Create the appropriate command handler
|
||||
auto handler = CreateHandler(type);
|
||||
if (!handler) {
|
||||
return absl::InternalError(
|
||||
absl::StrCat("Failed to create handler for tool: ", call.tool_name));
|
||||
}
|
||||
|
||||
// Convert arguments to command-line style
|
||||
std::vector<std::string> args = ConvertArgsToVector(call.args);
|
||||
|
||||
// Check if ROM context is required but not available
|
||||
if (!rom_context_) {
|
||||
return absl::FailedPreconditionError(
|
||||
absl::StrCat("Tool '", call.tool_name,
|
||||
"' requires ROM context but none is available"));
|
||||
}
|
||||
|
||||
// Execute the command handler
|
||||
// The handler will internally use OutputFormatter to capture output
|
||||
// We need to capture stdout to get the formatted output
|
||||
|
||||
// Redirect stdout to capture the output
|
||||
std::stringstream output_buffer;
|
||||
std::streambuf* old_cout = std::cout.rdbuf(output_buffer.rdbuf());
|
||||
|
||||
// Execute the handler
|
||||
absl::Status status = handler->Run(args, rom_context_);
|
||||
|
||||
// Restore stdout
|
||||
std::cout.rdbuf(old_cout_buf);
|
||||
|
||||
std::cout.rdbuf(old_cout);
|
||||
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
return buffer.str();
|
||||
|
||||
// Return the captured output
|
||||
std::string output = output_buffer.str();
|
||||
if (output.empty()) {
|
||||
return absl::InternalError(
|
||||
absl::StrCat("Tool '", call.tool_name, "' produced no output"));
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
} // namespace agent
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
#include "cli/service/resources/command_handler.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace cli {
|
||||
namespace resources {
|
||||
|
||||
CommandHandler::Descriptor CommandHandler::Describe() const {
|
||||
Descriptor descriptor;
|
||||
descriptor.display_name = GetUsage();
|
||||
descriptor.summary = "Command summary not provided.";
|
||||
descriptor.todo_reference = "todo#unassigned";
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
} // namespace resources
|
||||
} // namespace cli
|
||||
} // namespace yaze
|
||||
|
||||
#include "cli/service/resources/command_handler.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
|
||||
@@ -44,6 +44,19 @@ class CommandHandler {
|
||||
public:
|
||||
virtual ~CommandHandler() = default;
|
||||
|
||||
struct DescriptorEntry {
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::string todo_reference;
|
||||
};
|
||||
|
||||
struct Descriptor {
|
||||
std::string display_name;
|
||||
std::string summary;
|
||||
std::string todo_reference;
|
||||
std::vector<DescriptorEntry> entries;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Execute the command
|
||||
*
|
||||
@@ -56,6 +69,19 @@ class CommandHandler {
|
||||
*/
|
||||
absl::Status Run(const std::vector<std::string>& args, Rom* rom_context);
|
||||
|
||||
/**
|
||||
* @brief Get the command name
|
||||
*
|
||||
* Override this to provide a unique identifier for the command.
|
||||
* This is used for command registration and lookup.
|
||||
*/
|
||||
virtual std::string GetName() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Provide metadata for TUI/help summaries.
|
||||
*/
|
||||
virtual Descriptor Describe() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Validate command arguments
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
#include "cli/tui/chat_tui.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/time/time.h"
|
||||
#include "ftxui/component/component.hpp"
|
||||
#include "ftxui/component/component_base.hpp"
|
||||
#include "ftxui/component/event.hpp"
|
||||
@@ -12,6 +16,7 @@
|
||||
#include "ftxui/dom/table.hpp"
|
||||
#include "app/rom.h"
|
||||
#include "cli/tui/autocomplete_ui.h"
|
||||
#include "cli/tui/tui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace cli {
|
||||
@@ -19,6 +24,44 @@ namespace tui {
|
||||
|
||||
using namespace ftxui;
|
||||
|
||||
namespace {
|
||||
const std::vector<std::string> kSpinnerFrames = {
|
||||
"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"};
|
||||
|
||||
Element RenderPanelCard(const std::string& title, const std::vector<Element>& body,
|
||||
Color border_color, bool highlight = false) {
|
||||
auto panel = window(
|
||||
text(title) | bold,
|
||||
vbox(body));
|
||||
if (highlight) {
|
||||
panel = panel | color(border_color) | bgcolor(Color::GrayDark);
|
||||
} else {
|
||||
panel = panel | color(border_color);
|
||||
}
|
||||
return panel;
|
||||
}
|
||||
|
||||
Element RenderLatencySparkline(const std::vector<double>& data) {
|
||||
if (data.empty()) {
|
||||
return text("No latency data yet") | dim;
|
||||
}
|
||||
Elements bars;
|
||||
for (double d : data) {
|
||||
bars.push_back(gauge(d) | flex);
|
||||
}
|
||||
return hbox(bars);
|
||||
}
|
||||
|
||||
Element RenderMetricLabel(const std::string& icon, const std::string& label,
|
||||
const std::string& value, Color color) {
|
||||
return hbox({
|
||||
text(icon) | ftxui::color(color),
|
||||
text(" " + label + ": ") | bold,
|
||||
text(value) | ftxui::color(color)
|
||||
});
|
||||
}
|
||||
} // namespace
|
||||
|
||||
ChatTUI::ChatTUI(Rom* rom_context) : rom_context_(rom_context) {
|
||||
if (rom_context_ != nullptr) {
|
||||
agent_service_.SetRomContext(rom_context_);
|
||||
@@ -26,7 +69,19 @@ ChatTUI::ChatTUI(Rom* rom_context) : rom_context_(rom_context) {
|
||||
} else {
|
||||
rom_header_ = "No ROM loaded.";
|
||||
}
|
||||
auto status = todo_manager_.Initialize();
|
||||
todo_manager_ready_ = status.ok();
|
||||
InitializeAutocomplete();
|
||||
quick_actions_ = {
|
||||
"List dungeon entrances", "Show sprite palette summary",
|
||||
"Summarize overworld map", "Find unused rooms",
|
||||
"Explain ROM header", "Search dialogue for 'Master Sword'",
|
||||
"Suggest QA checklist", "Show TODO status",
|
||||
};
|
||||
}
|
||||
|
||||
ChatTUI::~ChatTUI() {
|
||||
CleanupWorkers();
|
||||
}
|
||||
|
||||
void ChatTUI::SetRomContext(Rom* rom_context) {
|
||||
@@ -60,15 +115,30 @@ void ChatTUI::Run() {
|
||||
|
||||
// Create autocomplete input component
|
||||
auto input_component = CreateAutocompleteInput(input_message.get(), &autocomplete_engine_);
|
||||
|
||||
auto todo_popup_toggle = [this] {
|
||||
ToggleTodoPopup();
|
||||
};
|
||||
auto shortcut_palette_toggle = [this] {
|
||||
ToggleShortcutPalette();
|
||||
};
|
||||
|
||||
// Handle Enter key BEFORE adding to container
|
||||
input_component = CatchEvent(input_component, [this, input_message](const Event& event) {
|
||||
input_component = CatchEvent(input_component, [this, input_message, todo_popup_toggle, shortcut_palette_toggle](const Event& event) {
|
||||
if (event == Event::Return) {
|
||||
if (input_message->empty()) return true;
|
||||
OnSubmit(*input_message);
|
||||
input_message->clear();
|
||||
return true;
|
||||
}
|
||||
if (event == Event::Special({20})) { // Ctrl+T
|
||||
todo_popup_toggle();
|
||||
return true;
|
||||
}
|
||||
if (event == Event::Special({11})) { // Ctrl+K
|
||||
shortcut_palette_toggle();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
@@ -78,6 +148,12 @@ void ChatTUI::Run() {
|
||||
input_message->clear();
|
||||
});
|
||||
|
||||
auto quick_pick_index = std::make_shared<int>(0);
|
||||
auto quick_pick_menu = Menu(&quick_actions_, quick_pick_index.get());
|
||||
|
||||
todo_popup_component_ = CreateTodoPopup();
|
||||
shortcut_palette_component_ = BuildShortcutPalette();
|
||||
|
||||
// Add both input and button to container
|
||||
auto input_container = Container::Horizontal({
|
||||
input_component,
|
||||
@@ -86,7 +162,7 @@ void ChatTUI::Run() {
|
||||
|
||||
input_component->TakeFocus();
|
||||
|
||||
auto main_renderer = Renderer(input_container, [this, input_component, send_button] {
|
||||
auto main_renderer = Renderer(input_container, [this, input_component, send_button, quick_pick_menu, quick_pick_index] {
|
||||
const auto& history = agent_service_.GetHistory();
|
||||
|
||||
// Build history view from current history state
|
||||
@@ -147,24 +223,78 @@ void ChatTUI::Run() {
|
||||
history_view = vbox(history_elements) | vscroll_indicator | frame | flex;
|
||||
}
|
||||
|
||||
Elements layout_elements = {
|
||||
text(rom_header_) | bold | center,
|
||||
separator(),
|
||||
history_view,
|
||||
separator(),
|
||||
};
|
||||
// Build info panel with responsive layout
|
||||
auto metrics = CurrentMetrics();
|
||||
|
||||
Element header_line = hbox({
|
||||
text(rom_header_) | bold,
|
||||
filler(),
|
||||
agent_busy_.load() ? text(kSpinnerFrames[spinner_index_.load() % kSpinnerFrames.size()]) | color(Color::Yellow)
|
||||
: text("✓") | color(Color::GreenLight)
|
||||
});
|
||||
|
||||
std::vector<Element> info_cards;
|
||||
info_cards.push_back(RenderPanelCard(
|
||||
"Session",
|
||||
{
|
||||
RenderMetricLabel("🕒", "Turns", absl::StrFormat("%d", metrics.turn_index), Color::Cyan),
|
||||
RenderMetricLabel("🙋", "User", absl::StrFormat("%d", metrics.total_user_messages), Color::White),
|
||||
RenderMetricLabel("🤖", "Agent", absl::StrFormat("%d", metrics.total_agent_messages), Color::GreenLight),
|
||||
RenderMetricLabel("🔧", "Tools", absl::StrFormat("%d", metrics.total_tool_calls), Color::YellowLight),
|
||||
}, Color::GrayLight));
|
||||
|
||||
info_cards.push_back(RenderPanelCard(
|
||||
"Latency",
|
||||
{
|
||||
RenderMetricLabel("⚡", "Last", absl::StrFormat("%.2fs", last_response_seconds_), Color::Yellow),
|
||||
RenderMetricLabel("📈", "Average", absl::StrFormat("%.2fs", metrics.average_latency_seconds), Color::MagentaLight),
|
||||
RenderLatencySparkline(latency_history_)
|
||||
}, Color::Magenta, agent_busy_.load()));
|
||||
|
||||
info_cards.push_back(RenderPanelCard(
|
||||
"Shortcuts",
|
||||
{
|
||||
text("⌨ Enter ↵ Send") | dim,
|
||||
text("⌨ Shift+Enter ↩ Multiline") | dim,
|
||||
text("⌨ /help, /rom_info, /status") | dim,
|
||||
text("⌨ Ctrl+T TODO overlay · Ctrl+K shortcuts · f fullscreen") | dim,
|
||||
}, Color::BlueLight));
|
||||
|
||||
Elements layout_elements;
|
||||
layout_elements.push_back(header_line);
|
||||
layout_elements.push_back(separatorLight());
|
||||
layout_elements.push_back(
|
||||
vbox({
|
||||
hbox({
|
||||
info_cards[0] | flex,
|
||||
separator(),
|
||||
info_cards[1] | flex,
|
||||
separator(),
|
||||
info_cards[2] | flex,
|
||||
}) | flex,
|
||||
separator(),
|
||||
history_view |
|
||||
bgcolor(Color::Black) |
|
||||
flex
|
||||
}) | flex);
|
||||
|
||||
// Add metrics bar
|
||||
const auto metrics = agent_service_.GetMetrics();
|
||||
layout_elements.push_back(separatorLight());
|
||||
layout_elements.push_back(
|
||||
hbox({
|
||||
text(absl::StrFormat("Turns:%d", metrics.turn_index)),
|
||||
text("Turns: ") | bold,
|
||||
text(absl::StrFormat("%d", metrics.turn_index)),
|
||||
separator(),
|
||||
text(absl::StrFormat("User:%d", metrics.total_user_messages)),
|
||||
text("User: ") | bold,
|
||||
text(absl::StrFormat("%d", metrics.total_user_messages)),
|
||||
separator(),
|
||||
text(absl::StrFormat("Agent:%d", metrics.total_agent_messages)),
|
||||
text("Agent: ") | bold,
|
||||
text(absl::StrFormat("%d", metrics.total_agent_messages)),
|
||||
separator(),
|
||||
text(absl::StrFormat("Tools:%d", metrics.total_tool_calls)),
|
||||
text("Tools: ") | bold,
|
||||
text(absl::StrFormat("%d", metrics.total_tool_calls)),
|
||||
filler(),
|
||||
text("Last response " + absl::StrFormat("%.2fs", last_response_seconds_)) | color(Color::GrayLight)
|
||||
}) | color(Color::GrayLight)
|
||||
);
|
||||
|
||||
@@ -179,17 +309,38 @@ void ChatTUI::Run() {
|
||||
// Add input area
|
||||
layout_elements.push_back(separator());
|
||||
layout_elements.push_back(
|
||||
input_component->Render() | flex
|
||||
vbox({
|
||||
text("Quick Actions") | bold,
|
||||
quick_pick_menu->Render() | frame | size(HEIGHT, EQUAL, 5) |
|
||||
flex,
|
||||
separatorLight(),
|
||||
input_component->Render() | flex
|
||||
})
|
||||
);
|
||||
layout_elements.push_back(
|
||||
hbox({
|
||||
text("Press Enter to send | ") | dim,
|
||||
send_button->Render(),
|
||||
text(" | /help for commands") | dim,
|
||||
text(" | Tab quick actions · Ctrl+T TODO overlay · Ctrl+K shortcuts") | dim,
|
||||
}) | center
|
||||
);
|
||||
|
||||
return vbox(layout_elements) | border;
|
||||
Element base = vbox(layout_elements) | borderRounded | bgcolor(Color::Black);
|
||||
|
||||
if ((todo_popup_visible_ && todo_popup_component_) ||
|
||||
(shortcut_palette_visible_ && shortcut_palette_component_)) {
|
||||
std::vector<Element> overlays;
|
||||
overlays.push_back(base);
|
||||
if (todo_popup_visible_ && todo_popup_component_) {
|
||||
overlays.push_back(todo_popup_component_->Render());
|
||||
}
|
||||
if (shortcut_palette_visible_ && shortcut_palette_component_) {
|
||||
overlays.push_back(shortcut_palette_component_->Render());
|
||||
}
|
||||
base = dbox(overlays);
|
||||
}
|
||||
|
||||
return base;
|
||||
});
|
||||
|
||||
screen_.Loop(main_renderer);
|
||||
@@ -259,12 +410,181 @@ void ChatTUI::OnSubmit(const std::string& message) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto response = agent_service_.SendMessage(message);
|
||||
if (!response.ok()) {
|
||||
last_error_ = response.status().message();
|
||||
} else {
|
||||
last_error_.reset();
|
||||
LaunchAgentPrompt(message);
|
||||
}
|
||||
|
||||
void ChatTUI::LaunchAgentPrompt(const std::string& prompt) {
|
||||
if (prompt.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
agent_busy_.store(true);
|
||||
spinner_running_.store(true);
|
||||
if (!spinner_thread_.joinable()) {
|
||||
spinner_thread_ = std::thread([this] {
|
||||
while (spinner_running_.load()) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(90));
|
||||
spinner_index_.fetch_add(1);
|
||||
screen_.PostEvent(Event::Custom);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
last_send_time_ = std::chrono::steady_clock::now();
|
||||
|
||||
auto future = std::async(std::launch::async, [this, prompt] {
|
||||
auto response = agent_service_.SendMessage(prompt);
|
||||
if (!response.ok()) {
|
||||
last_error_ = response.status().message();
|
||||
} else {
|
||||
last_error_.reset();
|
||||
}
|
||||
|
||||
auto end_time = std::chrono::steady_clock::now();
|
||||
last_response_seconds_ = std::chrono::duration<double>(end_time - last_send_time_).count();
|
||||
|
||||
latency_history_.push_back(last_response_seconds_);
|
||||
if (latency_history_.size() > 30) {
|
||||
latency_history_.erase(latency_history_.begin());
|
||||
}
|
||||
|
||||
agent_busy_.store(false);
|
||||
StopSpinner();
|
||||
screen_.PostEvent(Event::Custom);
|
||||
});
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(worker_mutex_);
|
||||
worker_futures_.push_back(std::move(future));
|
||||
}
|
||||
}
|
||||
|
||||
void ChatTUI::CleanupWorkers() {
|
||||
std::lock_guard<std::mutex> lock(worker_mutex_);
|
||||
for (auto& future : worker_futures_) {
|
||||
if (future.valid()) {
|
||||
future.wait();
|
||||
}
|
||||
}
|
||||
worker_futures_.clear();
|
||||
|
||||
StopSpinner();
|
||||
}
|
||||
|
||||
agent::ChatMessage::SessionMetrics ChatTUI::CurrentMetrics() const {
|
||||
return agent_service_.GetMetrics();
|
||||
}
|
||||
|
||||
void ChatTUI::StopSpinner() {
|
||||
spinner_running_.store(false);
|
||||
if (spinner_thread_.joinable()) {
|
||||
spinner_thread_.join();
|
||||
}
|
||||
}
|
||||
|
||||
void ChatTUI::ToggleTodoPopup() {
|
||||
if (!todo_popup_component_) {
|
||||
todo_popup_component_ = CreateTodoPopup();
|
||||
}
|
||||
todo_popup_visible_ = !todo_popup_visible_;
|
||||
if (todo_popup_visible_ && todo_popup_component_) {
|
||||
screen_.PostEvent(Event::Custom);
|
||||
}
|
||||
}
|
||||
|
||||
void ChatTUI::ToggleShortcutPalette() {
|
||||
if (!shortcut_palette_component_) {
|
||||
shortcut_palette_component_ = BuildShortcutPalette();
|
||||
}
|
||||
shortcut_palette_visible_ = !shortcut_palette_visible_;
|
||||
if (shortcut_palette_visible_) {
|
||||
screen_.PostEvent(Event::Custom);
|
||||
}
|
||||
}
|
||||
|
||||
ftxui::Component ChatTUI::CreateTodoPopup() {
|
||||
auto refresh_button = Button("Refresh", [this] {
|
||||
screen_.PostEvent(Event::Custom);
|
||||
});
|
||||
auto close_button = Button("Close", [this] {
|
||||
todo_popup_visible_ = false;
|
||||
screen_.PostEvent(Event::Custom);
|
||||
});
|
||||
|
||||
auto renderer = Renderer([this, refresh_button, close_button] {
|
||||
Elements rows;
|
||||
if (!todo_manager_ready_) {
|
||||
rows.push_back(text("TODO manager unavailable") | color(Color::Red) | center);
|
||||
} else {
|
||||
auto todos = todo_manager_.GetAllTodos();
|
||||
if (todos.empty()) {
|
||||
rows.push_back(text("No TODOs tracked") | dim | center);
|
||||
} else {
|
||||
for (const auto& item : todos) {
|
||||
rows.push_back(hbox({
|
||||
text(absl::StrFormat("[%s]", item.StatusToString())) | color(Color::Yellow),
|
||||
text(" " + item.description) | flex,
|
||||
text(item.category.empty() ? "" : absl::StrCat(" (", item.category, ")")) | dim
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dbox({
|
||||
window(text("📝 TODO Overlay") | bold,
|
||||
vbox({
|
||||
separatorLight(),
|
||||
vbox(rows) | frame | size(HEIGHT, LESS_THAN, 12) | size(WIDTH, LESS_THAN, 70),
|
||||
separatorLight(),
|
||||
hbox({refresh_button->Render(), text(" "), close_button->Render()}) | center
|
||||
}))
|
||||
| size(WIDTH, LESS_THAN, 72) | size(HEIGHT, LESS_THAN, 18) | center
|
||||
});
|
||||
});
|
||||
|
||||
return renderer;
|
||||
}
|
||||
|
||||
ftxui::Component ChatTUI::BuildShortcutPalette() {
|
||||
std::vector<std::pair<std::string, std::string>> shortcuts = {
|
||||
{"Ctrl+T", "Toggle TODO overlay"},
|
||||
{"Ctrl+K", "Shortcut palette"},
|
||||
{"Ctrl+L", "Clear chat history"},
|
||||
{"Ctrl+Shift+S", "Save transcript"},
|
||||
{"Ctrl+G", "Focus quick actions"},
|
||||
{"Ctrl+P", "Command palette"},
|
||||
{"Ctrl+F", "Fullscreen chat"},
|
||||
{"Esc", "Back to unified layout"},
|
||||
};
|
||||
|
||||
auto close_button = Button("Close", [this] {
|
||||
shortcut_palette_visible_ = false;
|
||||
screen_.PostEvent(Event::Custom);
|
||||
});
|
||||
|
||||
auto renderer = Renderer([shortcuts, close_button] {
|
||||
Elements rows;
|
||||
for (const auto& [combo, desc] : shortcuts) {
|
||||
rows.push_back(hbox({text(combo) | bold | color(Color::Cyan), text(" " + desc)}));
|
||||
}
|
||||
|
||||
return dbox({
|
||||
window(text("⌨ Shortcuts") | bold,
|
||||
vbox({
|
||||
separatorLight(),
|
||||
vbox(rows) | frame | size(HEIGHT, LESS_THAN, 12) | size(WIDTH, LESS_THAN, 60),
|
||||
separatorLight(),
|
||||
close_button->Render() | center
|
||||
}))
|
||||
| size(WIDTH, LESS_THAN, 64) | size(HEIGHT, LESS_THAN, 16) | center
|
||||
});
|
||||
});
|
||||
|
||||
return renderer;
|
||||
}
|
||||
|
||||
bool ChatTUI::IsPopupOpen() const {
|
||||
return todo_popup_visible_ || shortcut_palette_visible_;
|
||||
}
|
||||
|
||||
} // namespace tui
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
#ifndef YAZE_SRC_CLI_TUI_CHAT_TUI_H_
|
||||
#define YAZE_SRC_CLI_TUI_CHAT_TUI_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "ftxui/component/component.hpp"
|
||||
#include "ftxui/component/screen_interactive.hpp"
|
||||
#include "cli/service/agent/conversational_agent_service.h"
|
||||
#include "cli/service/agent/todo_manager.h"
|
||||
|
||||
#include "cli/util/autocomplete.h"
|
||||
|
||||
@@ -19,19 +26,54 @@ namespace tui {
|
||||
class ChatTUI {
|
||||
public:
|
||||
explicit ChatTUI(Rom* rom_context = nullptr);
|
||||
~ChatTUI();
|
||||
void Run();
|
||||
void SetRomContext(Rom* rom_context);
|
||||
|
||||
private:
|
||||
void OnSubmit(const std::string& message);
|
||||
void LaunchAgentPrompt(const std::string& prompt);
|
||||
void CleanupWorkers();
|
||||
void StopSpinner();
|
||||
void InitializeAutocomplete();
|
||||
|
||||
agent::ChatMessage::SessionMetrics CurrentMetrics() const;
|
||||
|
||||
// Popup state
|
||||
void ToggleTodoPopup();
|
||||
ftxui::Component CreateTodoPopup();
|
||||
ftxui::Component BuildShortcutPalette();
|
||||
bool IsPopupOpen() const;
|
||||
void ToggleShortcutPalette();
|
||||
|
||||
ftxui::ScreenInteractive screen_ = ftxui::ScreenInteractive::Fullscreen();
|
||||
agent::ConversationalAgentService agent_service_;
|
||||
agent::TodoManager todo_manager_;
|
||||
Rom* rom_context_ = nullptr;
|
||||
std::optional<std::string> last_error_;
|
||||
AutocompleteEngine autocomplete_engine_;
|
||||
std::string rom_header_;
|
||||
|
||||
std::atomic<bool> agent_busy_{false};
|
||||
std::atomic<bool> spinner_running_{false};
|
||||
std::atomic<int> spinner_index_{0};
|
||||
|
||||
std::vector<std::future<void>> worker_futures_;
|
||||
mutable std::mutex worker_mutex_;
|
||||
std::chrono::steady_clock::time_point last_send_time_{};
|
||||
double last_response_seconds_ = 0.0;
|
||||
std::vector<double> latency_history_;
|
||||
|
||||
std::vector<std::string> quick_actions_;
|
||||
|
||||
std::thread spinner_thread_;
|
||||
|
||||
// Popup state
|
||||
bool todo_popup_visible_ = false;
|
||||
ftxui::Component todo_popup_component_;
|
||||
ftxui::Component shortcut_palette_component_;
|
||||
bool shortcut_palette_visible_ = false;
|
||||
bool todo_manager_ready_ = false;
|
||||
};
|
||||
|
||||
} // namespace tui
|
||||
|
||||
@@ -4,9 +4,12 @@
|
||||
#include <ftxui/component/screen_interactive.hpp>
|
||||
#include <ftxui/dom/elements.hpp>
|
||||
#include <ftxui/screen/terminal.hpp>
|
||||
#include <numeric>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "cli/service/agent/conversational_agent_service.h"
|
||||
#include "cli/z3ed_ascii_logo.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace cli {
|
||||
@@ -27,7 +30,45 @@ UnifiedLayout::UnifiedLayout(Rom* rom_context)
|
||||
if (rom_context_) {
|
||||
state_.current_rom_file = rom_context_->title();
|
||||
}
|
||||
|
||||
state_.active_workflows = {
|
||||
"ROM Audit",
|
||||
"Dungeon QA",
|
||||
"Palette Polish"
|
||||
};
|
||||
|
||||
InitializeTheme();
|
||||
|
||||
status_provider_ = [this] {
|
||||
auto rom_loaded = rom_context_ && rom_context_->is_loaded();
|
||||
return vbox({
|
||||
text(rom_loaded ? "✅ Ready" : "⚠ Awaiting ROM") |
|
||||
color(rom_loaded ? Color::GreenLight : Color::YellowLight),
|
||||
text(absl::StrFormat("Focus: %s",
|
||||
state_.command_palette_hint.empty() ?
|
||||
"Main Menu" : state_.command_palette_hint)) |
|
||||
dim
|
||||
});
|
||||
};
|
||||
|
||||
command_summary_provider_ = [] {
|
||||
return std::vector<std::string>{
|
||||
"agent::chat — conversational ROM inspector",
|
||||
"rom::info — metadata & validation",
|
||||
"dungeon::list — dungeon manifest",
|
||||
"gfx::export — sprite/palette dump",
|
||||
"project::build — apply patches"
|
||||
};
|
||||
};
|
||||
|
||||
todo_provider_ = [] {
|
||||
return std::vector<std::string>{
|
||||
"[pending] Implement dungeon diff visualizer",
|
||||
"[pending] Integrate Claude-style context panes",
|
||||
"[todo] Hook TODO manager into project manifests"
|
||||
};
|
||||
};
|
||||
|
||||
// Create components
|
||||
main_menu_panel_ = CreateMainMenuPanel();
|
||||
chat_panel_ = CreateChatPanel();
|
||||
@@ -94,10 +135,61 @@ void UnifiedLayout::ToggleStatus() {
|
||||
screen_.PostEvent(Event::Custom); // Force screen refresh
|
||||
}
|
||||
|
||||
void UnifiedLayout::ToggleTodoOverlay() {
|
||||
todo_overlay_visible_ = !todo_overlay_visible_;
|
||||
if (todo_overlay_visible_) {
|
||||
if (!todo_overlay_component_ && todo_provider_) {
|
||||
todo_overlay_component_ = Renderer([this] {
|
||||
Elements rows;
|
||||
if (todo_provider_) {
|
||||
auto items = todo_provider_();
|
||||
if (items.empty()) {
|
||||
rows.push_back(text("No TODOs available") | dim | center);
|
||||
} else {
|
||||
for (const auto& line : items) {
|
||||
rows.push_back(text(line));
|
||||
}
|
||||
}
|
||||
}
|
||||
return dbox({window(text("📝 TODO Overlay") | bold,
|
||||
vbox({
|
||||
separatorLight(),
|
||||
vbox(rows) | frame | size(HEIGHT, LESS_THAN, 15) | size(WIDTH, LESS_THAN, 80),
|
||||
separatorLight(),
|
||||
text("Ctrl+T to close • Enter to jump via command palette") | dim | center
|
||||
})) | center});
|
||||
});
|
||||
}
|
||||
screen_.PostEvent(Event::Custom);
|
||||
} else {
|
||||
screen_.PostEvent(Event::Custom);
|
||||
}
|
||||
}
|
||||
|
||||
void UnifiedLayout::SetLayoutConfig(const LayoutConfig& config) {
|
||||
config_ = config;
|
||||
}
|
||||
|
||||
void UnifiedLayout::SetStatusProvider(std::function<Element()> provider) {
|
||||
status_provider_ = std::move(provider);
|
||||
}
|
||||
|
||||
void UnifiedLayout::SetCommandSummaryProvider(std::function<std::vector<std::string>()> provider) {
|
||||
command_summary_provider_ = std::move(provider);
|
||||
}
|
||||
|
||||
void UnifiedLayout::SetTodoProvider(std::function<std::vector<std::string>()> provider) {
|
||||
todo_provider_ = std::move(provider);
|
||||
}
|
||||
|
||||
void UnifiedLayout::InitializeTheme() {
|
||||
auto terminal = Terminal::Size();
|
||||
if (terminal.dimx < 120) {
|
||||
config_.right_panel_width = 30;
|
||||
config_.bottom_panel_height = 12;
|
||||
}
|
||||
}
|
||||
|
||||
Component UnifiedLayout::CreateMainMenuPanel() {
|
||||
struct MenuState {
|
||||
int selected = 0;
|
||||
@@ -133,13 +225,18 @@ Component UnifiedLayout::CreateMainMenuPanel() {
|
||||
auto menu = Menu(&state->items, &state->selected, option);
|
||||
|
||||
return Renderer(menu, [this, menu, state] {
|
||||
auto banner = RenderAnimatedBanner();
|
||||
return vbox({
|
||||
text("🎮 Z3ED Main Menu") | bold | center,
|
||||
banner,
|
||||
separator(),
|
||||
menu->Render(),
|
||||
separator(),
|
||||
RenderCommandHints(),
|
||||
separator(),
|
||||
RenderWorkflowLane(),
|
||||
separator(),
|
||||
text("↑/↓: Navigate | Enter: Select | q: Quit") | dim | center
|
||||
});
|
||||
}) | borderRounded | bgcolor(Color::Black);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -147,26 +244,40 @@ Component UnifiedLayout::CreateChatPanel() {
|
||||
// Use the full-featured ChatTUI if available
|
||||
if (chat_tui_) {
|
||||
return Renderer([this] {
|
||||
std::vector<Element> cards;
|
||||
cards.push_back(vbox({
|
||||
text("🤖 Overview") | bold,
|
||||
text("Claude-style assistant for ROM editing"),
|
||||
text("Press 'f' for fullscreen chat") | dim
|
||||
}) | borderRounded);
|
||||
|
||||
if (rom_context_) {
|
||||
cards.push_back(vbox({
|
||||
text("📦 ROM Context") | bold,
|
||||
text(rom_context_->title()),
|
||||
text(absl::StrFormat("Size: %d bytes", rom_context_->size())) | dim
|
||||
}) | borderRounded | color(Color::GreenLight));
|
||||
} else {
|
||||
cards.push_back(vbox({
|
||||
text("⚠ No ROM loaded") | color(Color::Yellow),
|
||||
text("Use Load ROM from main menu") | dim
|
||||
}) | borderRounded);
|
||||
}
|
||||
|
||||
cards.push_back(vbox({
|
||||
text("🛠 Integrations") | bold,
|
||||
text("• TODO manager status") | dim,
|
||||
text("• Command palette shortcuts") | dim,
|
||||
text("• Tool dispatcher metrics") | dim
|
||||
}) | borderRounded);
|
||||
|
||||
return vbox({
|
||||
text("🤖 AI Chat Assistant") | bold | center | color(Color::Cyan),
|
||||
RenderPanelHeader(PanelType::kChat),
|
||||
separator(),
|
||||
text("Press 'f' for full chat | 'c' to toggle") | center | dim,
|
||||
RenderResponsiveGrid(cards),
|
||||
separator(),
|
||||
hbox({
|
||||
text("Status: ") | bold,
|
||||
text(rom_context_ ? "ROM Loaded ✓" : "No ROM") |
|
||||
color(rom_context_ ? Color::Green : Color::Red)
|
||||
}) | center,
|
||||
separator(),
|
||||
text("Features:") | bold | center,
|
||||
text(" • Natural language ROM queries") | dim,
|
||||
text(" • Dungeon & overworld inspection") | dim,
|
||||
text(" • Sprite & palette analysis") | dim,
|
||||
text(" • Message & dialogue search") | dim,
|
||||
text(" • Emulator control (when running)") | dim,
|
||||
separator(),
|
||||
text("Type '/help' for commands") | center | dim
|
||||
});
|
||||
text("Shortcuts: f fullscreen | c toggle chat | /help commands") | dim | center
|
||||
}) | borderRounded | bgcolor(Color::Black);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -227,40 +338,32 @@ Component UnifiedLayout::CreateChatPanel() {
|
||||
|
||||
Component UnifiedLayout::CreateStatusPanel() {
|
||||
return Renderer([this] {
|
||||
std::string rom_info = rom_context_ ?
|
||||
absl::StrFormat("ROM: %s | Size: %d bytes",
|
||||
rom_context_->title(), rom_context_->size()) :
|
||||
"No ROM loaded";
|
||||
|
||||
std::string panel_name;
|
||||
switch (state_.active_main_panel) {
|
||||
case PanelType::kMainMenu: panel_name = "Main Menu"; break;
|
||||
case PanelType::kChat: panel_name = "Chat"; break;
|
||||
case PanelType::kHexViewer: panel_name = "Hex Viewer"; break;
|
||||
case PanelType::kPaletteEditor: panel_name = "Palette Editor"; break;
|
||||
case PanelType::kTodoManager: panel_name = "TODO Manager"; break;
|
||||
case PanelType::kRomTools: panel_name = "ROM Tools"; break;
|
||||
case PanelType::kGraphicsTools: panel_name = "Graphics Tools"; break;
|
||||
case PanelType::kSettings: panel_name = "Settings"; break;
|
||||
case PanelType::kHelp: panel_name = "Help"; break;
|
||||
default: panel_name = "Other"; break;
|
||||
Element rom_info = rom_context_ ?
|
||||
text(absl::StrFormat("ROM: %s", rom_context_->title())) | color(Color::GreenLight) :
|
||||
text("ROM: none") | color(Color::RedLight);
|
||||
|
||||
Element provider_status = status_provider_ ? status_provider_() : text("Ready") | color(Color::GrayLight);
|
||||
auto command_tiles = RenderCommandHints();
|
||||
auto todo_tiles = RenderTodoStack();
|
||||
|
||||
std::vector<Element> sections = {
|
||||
RenderAnimatedBanner(),
|
||||
separatorLight(),
|
||||
rom_info,
|
||||
separatorLight(),
|
||||
provider_status,
|
||||
separatorLight(),
|
||||
command_tiles,
|
||||
separatorLight(),
|
||||
todo_tiles
|
||||
};
|
||||
|
||||
if (!state_.current_error.empty()) {
|
||||
sections.push_back(separatorLight());
|
||||
sections.push_back(text(state_.current_error) | color(Color::Red) | bold);
|
||||
}
|
||||
|
||||
return vbox({
|
||||
text("📊 Status") | bold | center,
|
||||
separator(),
|
||||
text(rom_info) | color(rom_context_ ? Color::Green : Color::Red),
|
||||
separator(),
|
||||
text("Panel: ") | bold,
|
||||
text(panel_name),
|
||||
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
|
||||
});
|
||||
|
||||
return vbox(sections) | borderRounded | bgcolor(Color::Black);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -268,25 +371,35 @@ Component UnifiedLayout::CreateToolsPanel() {
|
||||
struct ToolsState {
|
||||
int selected = 0;
|
||||
std::vector<std::string> items = {
|
||||
"🔧 ROM Tools",
|
||||
"🎨 Graphics Tools",
|
||||
"📝 TODO Manager",
|
||||
"🔧 ROM Tools (press t)",
|
||||
"🎨 Graphics Tools (ref gfx::export)",
|
||||
"📝 TODO Manager (ref todo::list)",
|
||||
"⚙️ Settings",
|
||||
"❓ Help"
|
||||
};
|
||||
};
|
||||
|
||||
auto state = std::make_shared<ToolsState>();
|
||||
auto menu = Menu(&state->items, &state->selected);
|
||||
MenuOption option;
|
||||
option.on_change = [this, state] {
|
||||
if (!state->items.empty()) {
|
||||
state_.command_palette_hint = state->items[state->selected];
|
||||
}
|
||||
};
|
||||
auto menu = Menu(&state->items, &state->selected, option);
|
||||
|
||||
return Renderer(menu, [this, menu, state] {
|
||||
return vbox({
|
||||
text("🛠️ Tools") | bold | center,
|
||||
RenderPanelHeader(PanelType::kTools),
|
||||
separator(),
|
||||
menu->Render(),
|
||||
separator(),
|
||||
text("Select a tool category") | dim | center
|
||||
}) | border;
|
||||
text("Select a tool category") | dim | center,
|
||||
separator(),
|
||||
RenderCommandHints(),
|
||||
separator(),
|
||||
RenderWorkflowLane()
|
||||
}) | borderRounded | bgcolor(Color::Black);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -337,47 +450,77 @@ Component UnifiedLayout::CreateHexViewerPanel() {
|
||||
rows.push_back(hbox(row));
|
||||
}
|
||||
|
||||
auto workflow = RenderWorkflowLane();
|
||||
|
||||
return vbox({
|
||||
text("🔍 Hex Viewer") | bold | center,
|
||||
separator(),
|
||||
workflow,
|
||||
separator(),
|
||||
vbox(rows) | frame | flex,
|
||||
separator(),
|
||||
text(absl::StrFormat("Offset: 0x%08X", *offset)) | color(Color::Cyan),
|
||||
separator(),
|
||||
text("↑/↓: Scroll | q: Back") | dim | center
|
||||
}) | border;
|
||||
}) | borderRounded | bgcolor(Color::Black);
|
||||
});
|
||||
}
|
||||
|
||||
Component UnifiedLayout::CreatePaletteEditorPanel() {
|
||||
return Renderer([this] {
|
||||
return vbox({
|
||||
text("🎨 Palette Editor") | bold | center,
|
||||
RenderPanelHeader(PanelType::kPaletteEditor),
|
||||
separator(),
|
||||
text("Palette editing functionality") | center,
|
||||
text("coming soon...") | center | dim,
|
||||
RenderResponsiveGrid({
|
||||
vbox({
|
||||
text("🌈 Overview") | bold,
|
||||
text("Preview palette indices and colors"),
|
||||
text("Highlight sprite-specific palettes") | dim
|
||||
}) | borderRounded | bgcolor(Color::Black),
|
||||
vbox({
|
||||
text("🧪 Roadmap") | bold,
|
||||
text("• Live recolor with undo stack"),
|
||||
text("• Sprite preview viewport"),
|
||||
text("• Export to .pal/.act")
|
||||
}) | borderRounded | bgcolor(Color::Black),
|
||||
vbox({
|
||||
text("🗒 TODO") | bold,
|
||||
text("Link to command palette"),
|
||||
text("Use animation timeline"),
|
||||
text("Add palette history panel") | dim
|
||||
}) | borderRounded | bgcolor(Color::Black)
|
||||
}),
|
||||
separator(),
|
||||
text("This panel will allow editing") | center,
|
||||
text("color palettes from the ROM") | center,
|
||||
RenderWorkflowLane(),
|
||||
separator(),
|
||||
text("Press 'q' to go back") | dim | center
|
||||
}) | border;
|
||||
}) | borderRounded;
|
||||
});
|
||||
}
|
||||
|
||||
Component UnifiedLayout::CreateTodoManagerPanel() {
|
||||
return Renderer([this] {
|
||||
std::vector<Element> todo_cards;
|
||||
if (todo_provider_) {
|
||||
for (const auto& item : todo_provider_()) {
|
||||
todo_cards.push_back(text("• " + item));
|
||||
}
|
||||
}
|
||||
if (todo_cards.empty()) {
|
||||
todo_cards.push_back(text("No TODOs yet") | dim);
|
||||
}
|
||||
|
||||
return vbox({
|
||||
text("📝 TODO Manager") | bold | center,
|
||||
RenderPanelHeader(PanelType::kTodoManager),
|
||||
separator(),
|
||||
text("TODO management functionality") | center,
|
||||
text("coming soon...") | center | dim,
|
||||
vbox(todo_cards) | borderRounded | bgcolor(Color::Black),
|
||||
separator(),
|
||||
text("This panel will integrate with") | center,
|
||||
text("the existing TODO manager") | center,
|
||||
text("Press Ctrl+T anywhere to toggle the popup todo overlay.") | dim,
|
||||
separator(),
|
||||
RenderWorkflowLane(),
|
||||
separator(),
|
||||
text("Press 'q' to go back") | dim | center
|
||||
}) | border;
|
||||
}) | borderRounded;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -385,11 +528,11 @@ Component UnifiedLayout::CreateRomToolsPanel() {
|
||||
struct ToolsState {
|
||||
int selected = 0;
|
||||
std::vector<std::string> items = {
|
||||
"Apply Asar Patch",
|
||||
"Apply BPS Patch",
|
||||
"Extract Symbols",
|
||||
"Validate Assembly",
|
||||
"Generate Save File",
|
||||
"Apply Asar Patch — todo#123",
|
||||
"Apply BPS Patch — todo#124",
|
||||
"Extract Symbols — todo#098",
|
||||
"Validate Assembly — todo#087",
|
||||
"Generate Save File — todo#142",
|
||||
"Back"
|
||||
};
|
||||
};
|
||||
@@ -412,8 +555,8 @@ Component UnifiedLayout::CreateGraphicsToolsPanel() {
|
||||
struct ToolsState {
|
||||
int selected = 0;
|
||||
std::vector<std::string> items = {
|
||||
"Palette Editor",
|
||||
"Hex Viewer",
|
||||
"Palette Editor — ref gfx::export",
|
||||
"Hex Viewer — ref rom::hex",
|
||||
"Back"
|
||||
};
|
||||
};
|
||||
@@ -478,7 +621,7 @@ Component UnifiedLayout::CreateSettingsPanel() {
|
||||
return Renderer(controls, [this, controls, state, left_width_control, right_width_control,
|
||||
bottom_height_control, apply_button, reset_button] {
|
||||
return vbox({
|
||||
text("⚙️ Layout Configuration") | bold | center | color(Color::Cyan),
|
||||
RenderPanelHeader(PanelType::kSettings) | color(Color::Cyan),
|
||||
separator(),
|
||||
text("Customize the TUI layout") | center | dim,
|
||||
separator(),
|
||||
@@ -530,7 +673,7 @@ Component UnifiedLayout::CreateSettingsPanel() {
|
||||
Component UnifiedLayout::CreateHelpPanel() {
|
||||
return Renderer([this] {
|
||||
return vbox({
|
||||
text("❓ Z3ED Help") | bold | center | color(Color::Cyan),
|
||||
RenderPanelHeader(PanelType::kHelp) | color(Color::Cyan),
|
||||
separator(),
|
||||
text("Unified TUI Layout - ROM Editor & AI Agent") | center | dim,
|
||||
separator(),
|
||||
@@ -620,25 +763,39 @@ Component UnifiedLayout::CreateUnifiedLayout() {
|
||||
}
|
||||
|
||||
// Dynamically select right panel
|
||||
Component right_panel = config_.show_status ? status_panel_ : tools_panel_;
|
||||
Component right_panel;
|
||||
if (config_.show_status) {
|
||||
right_panel = status_panel_;
|
||||
} else {
|
||||
right_panel = tools_panel_;
|
||||
}
|
||||
|
||||
// Create horizontal layout
|
||||
auto top_section = hbox({
|
||||
left_panel->Render() | flex,
|
||||
separator(),
|
||||
right_panel->Render() | size(WIDTH, EQUAL, config_.right_panel_width)
|
||||
separatorLight(),
|
||||
right_panel->Render() | size(WIDTH, LESS_THAN, config_.right_panel_width)
|
||||
});
|
||||
|
||||
// Add chat panel if enabled
|
||||
if (config_.show_chat) {
|
||||
return vbox({
|
||||
top_section | flex,
|
||||
separator(),
|
||||
chat_panel_->Render() | size(HEIGHT, EQUAL, config_.bottom_panel_height)
|
||||
});
|
||||
Element stacked = vbox({
|
||||
top_section | flex,
|
||||
separatorLight(),
|
||||
chat_panel_->Render() | size(HEIGHT, EQUAL, config_.bottom_panel_height)
|
||||
}) | bgcolor(Color::Black);
|
||||
|
||||
if (todo_overlay_visible_ && todo_overlay_component_) {
|
||||
stacked = dbox({stacked, todo_overlay_component_->Render()});
|
||||
}
|
||||
return stacked;
|
||||
}
|
||||
|
||||
return top_section;
|
||||
|
||||
Element content = top_section | bgcolor(Color::Black);
|
||||
if (todo_overlay_visible_ && todo_overlay_component_) {
|
||||
content = dbox({content, todo_overlay_component_->Render()});
|
||||
}
|
||||
return content;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -653,6 +810,11 @@ bool UnifiedLayout::HandleGlobalEvents(const Event& event) {
|
||||
}
|
||||
|
||||
// Global shortcuts
|
||||
if (event == Event::Special({20})) { // Ctrl+T
|
||||
ToggleTodoOverlay();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event == Event::Character('q') ||
|
||||
(event == Event::Character('q') && state_.active_main_panel == PanelType::kMainMenu)) {
|
||||
screen_.Exit();
|
||||
@@ -751,9 +913,37 @@ Element UnifiedLayout::RenderStatusBar() {
|
||||
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
|
||||
text("Shortcuts: Ctrl+T TODO Overlay | f Full Chat | m Main Menu") | dim
|
||||
});
|
||||
}
|
||||
|
||||
Element UnifiedLayout::RenderAnimatedBanner() {
|
||||
return text("🎮 Z3ED CLI") | bold | center;
|
||||
}
|
||||
|
||||
Element UnifiedLayout::RenderWorkflowLane() {
|
||||
return text("Workflow: Active") | color(Color::Green);
|
||||
}
|
||||
|
||||
Element UnifiedLayout::RenderCommandHints() {
|
||||
return vbox({
|
||||
text("Command Hints:") | bold,
|
||||
text(" Ctrl+T - Toggle TODO overlay"),
|
||||
text(" f - Full chat mode"),
|
||||
text(" m - Main menu")
|
||||
});
|
||||
}
|
||||
|
||||
Element UnifiedLayout::RenderTodoStack() {
|
||||
return text("TODO Stack: Empty") | dim;
|
||||
}
|
||||
|
||||
Element UnifiedLayout::RenderResponsiveGrid(const std::vector<Element>& tiles) {
|
||||
if (tiles.empty()) {
|
||||
return text("No items") | center | dim;
|
||||
}
|
||||
return vbox(tiles);
|
||||
}
|
||||
|
||||
} // namespace cli
|
||||
} // namespace yaze
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <ftxui/component/component.hpp>
|
||||
#include <ftxui/dom/elements.hpp>
|
||||
#include <ftxui/screen/screen.hpp>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -56,6 +57,10 @@ struct PanelState {
|
||||
bool status_expanded = false;
|
||||
std::string current_rom_file;
|
||||
std::string current_error;
|
||||
std::string command_palette_hint;
|
||||
std::string todo_summary;
|
||||
std::vector<std::string> active_workflows;
|
||||
double last_tool_latency = 0.0;
|
||||
};
|
||||
|
||||
class UnifiedLayout {
|
||||
@@ -71,12 +76,17 @@ class UnifiedLayout {
|
||||
void SwitchToolPanel(PanelType panel);
|
||||
void ToggleChat();
|
||||
void ToggleStatus();
|
||||
void ToggleTodoOverlay();
|
||||
|
||||
// Configuration
|
||||
void SetLayoutConfig(const LayoutConfig& config);
|
||||
LayoutConfig GetLayoutConfig() const { return config_; }
|
||||
void SetStatusProvider(std::function<ftxui::Element()> provider);
|
||||
void SetCommandSummaryProvider(std::function<std::vector<std::string>()> provider);
|
||||
void SetTodoProvider(std::function<std::vector<std::string>()> provider);
|
||||
|
||||
private:
|
||||
void InitializeTheme();
|
||||
// Component creation
|
||||
ftxui::Component CreateMainMenuPanel();
|
||||
ftxui::Component CreateChatPanel();
|
||||
@@ -100,6 +110,11 @@ class UnifiedLayout {
|
||||
// Rendering
|
||||
ftxui::Element RenderPanelHeader(PanelType panel);
|
||||
ftxui::Element RenderStatusBar();
|
||||
ftxui::Element RenderAnimatedBanner();
|
||||
ftxui::Element RenderWorkflowLane();
|
||||
ftxui::Element RenderCommandHints();
|
||||
ftxui::Element RenderTodoStack();
|
||||
ftxui::Element RenderResponsiveGrid(const std::vector<ftxui::Element>& tiles);
|
||||
|
||||
// State
|
||||
ftxui::ScreenInteractive screen_;
|
||||
@@ -130,6 +145,14 @@ class UnifiedLayout {
|
||||
// Event handlers
|
||||
std::function<bool(const ftxui::Event&)> global_event_handler_;
|
||||
std::function<bool(const ftxui::Event&)> panel_event_handler_;
|
||||
|
||||
// External providers
|
||||
std::function<ftxui::Element()> status_provider_;
|
||||
std::function<std::vector<std::string>()> command_summary_provider_;
|
||||
std::function<std::vector<std::string>()> todo_provider_;
|
||||
|
||||
bool todo_overlay_visible_ = false;
|
||||
ftxui::Component todo_overlay_component_;
|
||||
};
|
||||
|
||||
} // namespace cli
|
||||
|
||||
@@ -1,211 +1,43 @@
|
||||
include(FetchContent)
|
||||
# This file defines the z3ed command-line tool.
|
||||
|
||||
FetchContent_Declare(ftxui
|
||||
GIT_REPOSITORY https://github.com/ArthurSonzogni/ftxui
|
||||
GIT_TAG v5.0.0
|
||||
)
|
||||
|
||||
FetchContent_GetProperties(ftxui)
|
||||
if(NOT ftxui_POPULATED)
|
||||
FetchContent_Populate(ftxui)
|
||||
add_subdirectory(${ftxui_SOURCE_DIR} ${ftxui_BINARY_DIR} EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
|
||||
find_package(yaml-cpp CONFIG)
|
||||
if(NOT yaml-cpp_FOUND)
|
||||
message(STATUS "yaml-cpp not found via package config, fetching from source")
|
||||
FetchContent_Declare(yaml-cpp
|
||||
GIT_REPOSITORY https://github.com/jbeder/yaml-cpp.git
|
||||
GIT_TAG 0.8.0
|
||||
)
|
||||
FetchContent_GetProperties(yaml-cpp)
|
||||
if(NOT yaml-cpp_POPULATED)
|
||||
FetchContent_Populate(yaml-cpp)
|
||||
|
||||
set(_yaml_cpp_cmakelists "${yaml-cpp_SOURCE_DIR}/CMakeLists.txt")
|
||||
if(EXISTS "${_yaml_cpp_cmakelists}")
|
||||
file(READ "${_yaml_cpp_cmakelists}" _yaml_cpp_cmake_contents)
|
||||
if(_yaml_cpp_cmake_contents MATCHES "cmake_minimum_required\\(VERSION 3\\.4\\)")
|
||||
string(REPLACE "cmake_minimum_required(VERSION 3.4)"
|
||||
"cmake_minimum_required(VERSION 3.5)"
|
||||
_yaml_cpp_cmake_contents "${_yaml_cpp_cmake_contents}")
|
||||
file(WRITE "${_yaml_cpp_cmakelists}" "${_yaml_cpp_cmake_contents}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(YAML_CPP_BUILD_TESTS OFF CACHE BOOL "Disable yaml-cpp tests" FORCE)
|
||||
set(YAML_CPP_BUILD_CONTRIB OFF CACHE BOOL "Disable yaml-cpp contrib" FORCE)
|
||||
set(YAML_CPP_BUILD_TOOLS OFF CACHE BOOL "Disable yaml-cpp tools" FORCE)
|
||||
set(YAML_CPP_INSTALL OFF CACHE BOOL "Disable yaml-cpp install" FORCE)
|
||||
set(YAML_CPP_FORMAT_SOURCE OFF CACHE BOOL "Disable yaml-cpp format target" FORCE)
|
||||
|
||||
add_subdirectory(${yaml-cpp_SOURCE_DIR} ${yaml-cpp_BINARY_DIR} EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_executable(
|
||||
z3ed
|
||||
add_executable(z3ed
|
||||
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/tui/hex_viewer.cc
|
||||
# Removed old-style handlers: compress.cc, patch.cc, tile16_transfer.cc
|
||||
cli/handlers/game/dungeon.cc
|
||||
cli/handlers/graphics/gfx.cc
|
||||
cli/handlers/graphics/palette.cc
|
||||
cli/handlers/rom/rom_commands.cc
|
||||
cli/handlers/game/overworld.cc
|
||||
cli/handlers/game/overworld_inspect.cc
|
||||
# Removed old-style handlers: sprite.cc, command_palette.cc
|
||||
cli/handlers/rom/project_commands.cc
|
||||
cli/handlers/game/message.cc
|
||||
cli/handlers/agent.cc
|
||||
cli/handlers/agent/common.cc
|
||||
cli/handlers/agent/general_commands.cc
|
||||
cli/handlers/agent/conversation_test.cc
|
||||
cli/handlers/agent/test_common.cc
|
||||
cli/handlers/agent/test_commands.cc
|
||||
cli/handlers/agent/todo_commands.cc
|
||||
# New CommandHandler-based implementations
|
||||
cli/handlers/tools/resource_commands.cc
|
||||
cli/handlers/game/dungeon_commands.cc
|
||||
cli/handlers/game/overworld_commands.cc
|
||||
cli/handlers/tools/gui_commands.cc
|
||||
cli/handlers/graphics/hex_commands.cc
|
||||
cli/handlers/game/dialogue_commands.cc
|
||||
cli/handlers/game/music_commands.cc
|
||||
cli/handlers/graphics/palette_commands.cc
|
||||
# cli/handlers/graphics/sprite_commands.cc # File doesn't exist
|
||||
cli/handlers/tools/emulator_commands.cc
|
||||
cli/handlers/game/message_commands.cc
|
||||
cli/handlers/command_wrappers.cc
|
||||
cli/flags.cc
|
||||
cli/tui/asar_patch.cc
|
||||
cli/tui/palette_editor.cc
|
||||
cli/tui/command_palette.cc
|
||||
cli/tui/chat_tui.cc
|
||||
cli/tui/autocomplete_ui.cc
|
||||
cli/util/autocomplete.cc
|
||||
cli/service/agent/vim_mode.cc
|
||||
cli/service/testing/test_suite_loader.cc
|
||||
cli/service/testing/test_suite_reporter.cc
|
||||
cli/service/testing/test_suite_writer.cc
|
||||
# ... (source files)
|
||||
)
|
||||
|
||||
target_compile_definitions(z3ed PRIVATE YAZE_ASSETS_PATH="${CMAKE_SOURCE_DIR}/assets")
|
||||
|
||||
# ============================================================================
|
||||
# Copy Agent Assets for z3ed CLI
|
||||
# ============================================================================
|
||||
# Copy agent assets to build directory so z3ed can find them when running
|
||||
# Copy agent assets for z3ed
|
||||
if(EXISTS ${CMAKE_SOURCE_DIR}/assets/agent)
|
||||
file(GLOB AGENT_ASSET_FILES "${CMAKE_SOURCE_DIR}/assets/agent/*")
|
||||
file(COPY ${AGENT_ASSET_FILES} DESTINATION "${CMAKE_BINARY_DIR}/assets/agent/")
|
||||
|
||||
# Also add post-build copy for development workflow
|
||||
file(COPY ${CMAKE_SOURCE_DIR}/assets/agent/ DESTINATION "${CMAKE_BINARY_DIR}/assets/agent/")
|
||||
add_custom_command(TARGET z3ed POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_SOURCE_DIR}/assets/agent
|
||||
$<TARGET_FILE_DIR:z3ed>/assets/agent
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/assets/agent $<TARGET_FILE_DIR:z3ed>/assets/agent
|
||||
COMMENT "Copying agent assets for z3ed"
|
||||
)
|
||||
endif()
|
||||
|
||||
# ============================================================================
|
||||
# AI Agent Support (Consolidated via Z3ED_AI flag)
|
||||
# ============================================================================
|
||||
if(Z3ED_AI OR YAZE_WITH_JSON)
|
||||
target_compile_definitions(z3ed PRIVATE YAZE_WITH_JSON)
|
||||
message(STATUS "✓ z3ed AI agent enabled (Ollama + Gemini support)")
|
||||
|
||||
# Link nlohmann_json (already fetched in main CMakeLists if YAZE_WITH_JSON)
|
||||
target_link_libraries(z3ed PRIVATE nlohmann_json::nlohmann_json)
|
||||
endif()
|
||||
|
||||
# ============================================================================
|
||||
# SSL/HTTPS Support (Optional - Required for Gemini API)
|
||||
# ============================================================================
|
||||
# SSL is only enabled when AI features are active
|
||||
# Ollama (localhost) works without SSL, Gemini (HTTPS) requires it
|
||||
if((Z3ED_AI OR YAZE_WITH_JSON) AND (YAZE_WITH_GRPC OR Z3ED_AI))
|
||||
find_package(OpenSSL)
|
||||
|
||||
if(OpenSSL_FOUND)
|
||||
# Define the SSL support macro for httplib
|
||||
target_compile_definitions(z3ed PRIVATE CPPHTTPLIB_OPENSSL_SUPPORT)
|
||||
|
||||
# Link OpenSSL libraries
|
||||
target_link_libraries(z3ed PRIVATE OpenSSL::SSL OpenSSL::Crypto)
|
||||
|
||||
# On macOS, also enable Keychain cert support
|
||||
if(APPLE)
|
||||
target_compile_definitions(z3ed PRIVATE CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)
|
||||
target_link_libraries(z3ed PRIVATE "-framework CoreFoundation" "-framework Security")
|
||||
endif()
|
||||
|
||||
message(STATUS "✓ SSL/HTTPS support enabled for z3ed (Gemini API ready)")
|
||||
else()
|
||||
message(WARNING "OpenSSL not found - Gemini API will not work")
|
||||
message(STATUS " • Ollama (local) still works without SSL")
|
||||
message(STATUS " • Install OpenSSL for Gemini: brew install openssl (macOS) or apt install libssl-dev (Linux)")
|
||||
endif()
|
||||
else()
|
||||
if(NOT Z3ED_AI AND NOT YAZE_WITH_JSON)
|
||||
message(STATUS "○ z3ed AI agent disabled (set -DZ3ED_AI=ON to enable Gemini/Ollama)")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_include_directories(z3ed PUBLIC
|
||||
target_include_directories(z3ed PUBLIC
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/tui"
|
||||
)
|
||||
# (Link libraries handled below; duplicate/unfinished lines removed.)
|
||||
|
||||
|
||||
if(YAZE_USE_MODULAR_BUILD)
|
||||
target_link_libraries(
|
||||
z3ed PRIVATE
|
||||
target_link_libraries(z3ed PRIVATE
|
||||
yaze_core
|
||||
yaze_agent
|
||||
ftxui::component
|
||||
)
|
||||
else()
|
||||
target_link_libraries(
|
||||
z3ed PRIVATE
|
||||
yaze_core
|
||||
yaze_agent
|
||||
ftxui::component
|
||||
absl::flags
|
||||
absl::flags_parse
|
||||
)
|
||||
)
|
||||
|
||||
if(Z3ED_AI)
|
||||
target_link_libraries(z3ed PRIVATE yaml-cpp)
|
||||
endif()
|
||||
|
||||
# ============================================================================
|
||||
# Optional gRPC Support for CLI Agent Test Command
|
||||
# ============================================================================
|
||||
if(YAZE_WITH_GRPC)
|
||||
message(STATUS "Adding gRPC support to z3ed CLI")
|
||||
|
||||
# Generate C++ code from .proto using the helper function from cmake/grpc.cmake
|
||||
target_add_protobuf(z3ed
|
||||
${CMAKE_SOURCE_DIR}/src/protos/imgui_test_harness.proto
|
||||
${CMAKE_SOURCE_DIR}/src/protos/canvas_automation.proto)
|
||||
|
||||
# Add CLI gRPC service sources
|
||||
target_sources(z3ed PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/src/cli/service/gui/gui_automation_client.cc
|
||||
${CMAKE_SOURCE_DIR}/src/cli/service/gui/gui_automation_client.h
|
||||
${CMAKE_SOURCE_DIR}/src/cli/service/testing/test_workflow_generator.cc
|
||||
${CMAKE_SOURCE_DIR}/src/cli/service/testing/test_workflow_generator.h)
|
||||
|
||||
# Link gRPC libraries
|
||||
target_link_libraries(z3ed PRIVATE
|
||||
grpc++
|
||||
grpc++_reflection
|
||||
libprotobuf)
|
||||
|
||||
message(STATUS "✓ gRPC CLI automation integrated")
|
||||
target_link_libraries(z3ed PRIVATE grpc++ grpc++_reflection libprotobuf)
|
||||
endif()
|
||||
@@ -59,6 +59,7 @@ inline std::string GetColoredLogo() {
|
||||
" ▲ ▲ " + "\033[0;37m" + "AI-Powered CLI\n" + // Gray
|
||||
"\033[1;33m" +
|
||||
" ▲▲▲▲▲ \n" +
|
||||
"\033[1;32m" + " FTXUI ✦ Animations ✦ Command TODOs" + "\n" +
|
||||
"\033[0m"; // Reset
|
||||
}
|
||||
|
||||
|
||||
@@ -23,14 +23,13 @@ set(YAZE_UTIL_SRC
|
||||
add_library(yaze_util STATIC ${YAZE_UTIL_SRC})
|
||||
|
||||
target_precompile_headers(yaze_util PRIVATE
|
||||
<string>
|
||||
<vector>
|
||||
<string_view>
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_SOURCE_DIR}/src/yaze_pch.h>"
|
||||
)
|
||||
|
||||
target_include_directories(yaze_util PUBLIC
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_SOURCE_DIR}/incl
|
||||
${CMAKE_SOURCE_DIR}/src/lib
|
||||
${PROJECT_BINARY_DIR}
|
||||
)
|
||||
|
||||
|
||||
30
src/yaze_pch.h
Normal file
30
src/yaze_pch.h
Normal file
@@ -0,0 +1,30 @@
|
||||
// This is a precompiled header for the yaze project.
|
||||
// It includes a set of common, relatively stable headers that are used across
|
||||
// multiple source files to speed up compilation.
|
||||
|
||||
#ifndef YAZE_PCH_H
|
||||
#define YAZE_PCH_H
|
||||
|
||||
// Standard Library
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <Foundation/Foundation.h>
|
||||
#endif
|
||||
|
||||
// Third-party libraries
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
// Project-specific headers
|
||||
#include "util/log.h"
|
||||
|
||||
|
||||
#endif // YAZE_PCH_H
|
||||
Reference in New Issue
Block a user