21#include <emscripten.h>
22#include <emscripten/bind.h>
24#include "absl/status/status.h"
25#include "absl/strings/str_join.h"
26#include "absl/strings/str_split.h"
43 std::unique_ptr<yaze::cli::BrowserAIService> ai_service;
44 std::string last_output;
46 std::string model =
"gemini-2.5-flash";
49 bool initialized =
false;
59 void SetupAIService() {
60 yaze::cli::BrowserAIConfig config;
62 config.api_key = api_key;
64 config.api_base = api_base;
65 config.verbose =
false;
66 auto http_client = std::make_unique<yaze::net::EmscriptenHttpClient>();
67 ai_service = std::make_unique<yaze::cli::BrowserAIService>(
68 config, std::move(http_client));
70 ai_service->SetRomContext(rom);
74 void ConfigureAI(
const char* provider_value,
const char* model_value,
75 const char* api_base_value,
const char* api_key_value) {
77 provider = provider_value;
83 api_base = api_base_value;
86 api_key = api_key_value;
95 return manager->GetCurrentRom();
100static BridgeState g_bridge;
108 return g_bridge.ai_service.get();
112 return g_bridge.GetActiveRom();
121EM_JS(
void, z3ed_print_to_terminal, (
const char* text), {
122 if (window.z3edTerminal) {
123 window.z3edTerminal.print(UTF8ToString(text));
125 console.log(UTF8ToString(text));
130EM_JS(
void, z3ed_error_to_terminal, (
const char* text), {
131 if (window.z3edTerminal) {
132 window.z3edTerminal.printError(UTF8ToString(text));
134 console.error(UTF8ToString(text));
139std::vector<std::string> ParseCommand(
const std::string& command) {
141 return absl::StrSplit(command,
' ', absl::SkipEmpty());
145std::string ProcessCommandInternal(
const std::string& command_str) {
146 auto args = ParseCommand(command_str);
148 return "No command provided";
151 std::ostringstream output;
154 if (args[0] ==
"help") {
155 if (args.size() > 1) {
157 return registry.GenerateCategoryHelp(args[1]);
164 if (args[0] ==
"rom" && args.size() > 1) {
165 if (args[1] ==
"load" && args.size() > 2) {
167 yaze::app::wasm::TriggerRomLoad(args[2]);
168 return "Requesting load for: " + args[2] +
". Check application log.";
171 if (args[1] ==
"info") {
173 output <<
"ROM Info:\n";
174 output <<
" Size: " <<
rom->
size() <<
" bytes\n";
175 output <<
" Title: " <<
rom->
title() <<
"\n";
178 return "No ROM loaded.";
184 if (args[0] ==
"ai") {
185 if (!g_bridge.ai_service) {
186 g_bridge.SetupAIService();
188 if (!g_bridge.ai_service) {
189 return "AI service unavailable";
191 if (args.size() < 2) {
192 return "AI command requires a prompt. Usage: ai <prompt>";
196 std::vector<std::string> prompt_parts(args.begin() + 1, args.end());
197 std::string prompt = absl::StrJoin(prompt_parts,
" ");
200 auto response = g_bridge.ai_service->GenerateResponse(prompt);
202 return response->text_response;
204 return "AI error: " + std::string(response.status().message());
209 if (args[0] ==
"editor") {
212 return "Error: Editor manager not available";
214 if (args.size() > 2 && args[1] ==
"switch") {
215 std::string target = args[2];
218 std::transform(
name.begin(),
name.end(),
name.begin(), ::tolower);
219 std::transform(target.begin(), target.end(), target.begin(), ::tolower);
220 if (name == target) {
221 editor_manager->SwitchToEditor(
226 return "Unknown editor: " + args[2];
229 if (args.size() > 3 && args[1] ==
"card") {
233 std::string card_key = args[2];
234 std::string state = args[3];
235 bool visible = (state ==
"show" || state ==
"on" || state ==
"true");
238 if (current_editor &&
242 if (card_key ==
"object" || card_key ==
"objects" ||
243 card_key ==
"room" || card_key ==
"selector" ||
244 card_key ==
"graphics" || card_key ==
"debug") {
245 return "Panel visibility is now controlled via Layout Designer. Use "
246 "'editor layout' commands.";
248 return "Unknown card key for Dungeon Editor: " + card_key;
250 return "Panel control not supported for current editor";
253 if (args.size() > 2 && args[1] ==
"debug" && args[2] ==
"toggle") {
255 return "Debug controls are now accessed via the Layout Designer. "
256 "Use the canvas debug panel or layout commands.";
261 if (args[0] ==
"dungeon") {
264 return "Error: Editor manager not available";
267 if (!current_editor ||
270 return "Error: Dungeon editor is not active. Use 'editor switch dungeon' "
273 auto* dungeon_editor =
276 if (args.size() > 2 && args[1] ==
"room") {
278 int room_id = std::stoi(args[2],
nullptr, 16);
279 dungeon_editor->FocusRoom(room_id);
280 return "Focused room " + args[2];
282 return "Invalid room ID (hex required)";
286 if (args.size() > 2 && args[1] ==
"select_object") {
288 int obj_id = std::stoi(args[2],
nullptr, 16);
289 dungeon_editor->SelectObject(obj_id);
290 return "Selected object " + args[2];
292 return "Invalid object ID (hex required)";
296 if (args.size() > 2 && args[1] ==
"agent_mode") {
297 bool enabled = (args[2] ==
"on" || args[2] ==
"true");
298 dungeon_editor->SetAgentMode(enabled);
299 return "Agent mode " + std::string(enabled ?
"enabled" :
"disabled");
305 if (registry.HasCommand(args[0])) {
306 std::vector<std::string> cmd_args(args.begin() + 1, args.end());
308 std::string cmd_output;
309 auto status = registry.Execute(args[0], cmd_args, g_bridge.GetActiveRom(),
312 return cmd_output.empty() ?
"Command executed successfully" : cmd_output;
314 return "Command failed: " + std::string(status.message());
318 return "Unknown command: " + args[0] +
"\nType 'help' for available commands";
322std::vector<std::string> GetCompletionsInternal(
const std::string& partial) {
323 std::vector<std::string> completions;
326 auto parts = absl::StrSplit(partial,
' ', absl::SkipEmpty());
327 std::vector<std::string> cmd_parts(parts.begin(), parts.end());
330 if (cmd_parts.empty() ||
331 (cmd_parts.size() == 1 && !partial.empty() && partial.back() !=
' ')) {
332 std::string prefix = cmd_parts.empty() ?
"" : cmd_parts[0];
336 auto categories = registry.GetCategories();
338 std::set<std::string> unique_commands;
340 for (
const auto& category : categories) {
341 auto commands = registry.GetCommandsInCategory(category);
342 for (
const auto& cmd : commands) {
343 if (prefix.empty() || cmd.find(prefix) == 0) {
344 unique_commands.insert(cmd);
350 std::vector<std::string> special = {
351 "help",
"rom",
"ai",
"clear",
"version",
352 "hex",
"palette",
"sprite",
"music",
"dialogue",
353 "message",
"resource",
"dungeon",
"overworld",
"gui",
354 "emulator",
"query",
"analyze",
"catalog"};
356 for (
const auto& cmd : special) {
357 if (prefix.empty() || cmd.find(prefix) == 0) {
358 unique_commands.insert(cmd);
363 completions.assign(unique_commands.begin(), unique_commands.end());
365 }
else if (cmd_parts.size() >= 1) {
367 const std::string& command = cmd_parts[0];
369 if (command ==
"rom") {
371 std::vector<std::string> rom_cmds = {
"load",
"info",
"save",
"stats",
373 std::string prefix = cmd_parts.size() > 1 ? cmd_parts[1] :
"";
374 for (
const auto& subcmd : rom_cmds) {
375 if (prefix.empty() || subcmd.find(prefix) == 0) {
376 completions.push_back(
"rom " + subcmd);
384 std::sort(completions.begin(), completions.end());
400 g_bridge.Initialize();
403 g_bridge.last_output =
"Invalid command";
404 return g_bridge.last_output.c_str();
407 std::string command_str(command);
408 g_bridge.last_output = ProcessCommandInternal(command_str);
411 z3ed_print_to_terminal(g_bridge.last_output.c_str());
413 return g_bridge.last_output.c_str();
422const char* Z3edGetCompletions(
const char* partial) {
423 g_bridge.Initialize();
426 g_bridge.last_output =
"[]";
427 return g_bridge.last_output.c_str();
430 auto completions = GetCompletionsInternal(std::string(partial));
433 std::ostringstream
json;
435 for (
size_t i = 0; i < completions.size(); ++i) {
438 json <<
"\"" << completions[i] <<
"\"";
442 g_bridge.last_output =
json.str();
443 return g_bridge.last_output.c_str();
451void Z3edSetApiKey(
const char* api_key) {
452 g_bridge.ConfigureAI(
nullptr,
nullptr,
nullptr, api_key ? api_key :
"");
463void Z3edConfigureAI(
const char* provider,
const char* model,
464 const char* api_base,
const char* api_key) {
465 g_bridge.Initialize();
467 model ? model :
"", api_base ? api_base :
"",
468 api_key ? api_key :
"");
477 if (!g_bridge.initialized) {
478 g_bridge.Initialize();
480 return g_bridge.initialized ? 1 : 0;
490int Z3edLoadRomData(
const uint8_t* data,
size_t size) {
491 if (!data || size == 0) {
492 z3ed_error_to_terminal(
"Invalid ROM data");
497 std::string temp_path =
"/.yaze/roms/terminal_upload.sfc";
498 std::ofstream file(temp_path, std::ios::binary);
500 z3ed_error_to_terminal(
"Failed to write to VFS");
503 file.write(
reinterpret_cast<const char*
>(data), size);
507 yaze::app::wasm::TriggerRomLoad(temp_path);
509 z3ed_print_to_terminal(
"ROM uploaded to VFS. Loading...");
518const char* Z3edGetRomInfo() {
522 g_bridge.last_output =
"{\"error\": \"No ROM loaded\"}";
523 return g_bridge.last_output.c_str();
526 std::ostringstream
json;
528 <<
"\"loaded\": true,"
529 <<
"\"size\": " <<
rom->
size() <<
","
530 <<
"\"title\": \"" <<
rom->
title() <<
"\""
533 g_bridge.last_output =
json.str();
534 return g_bridge.last_output.c_str();
543const char* Z3edQueryResource(
const char* query) {
544 g_bridge.Initialize();
547 g_bridge.last_output =
"{\"error\": \"Invalid query\"}";
548 return g_bridge.last_output.c_str();
554 g_bridge.last_output =
"{\"error\": \"No ROM loaded\"}";
555 return g_bridge.last_output.c_str();
560 std::string cmd_name =
"resource-search";
563 if (registry.HasCommand(cmd_name)) {
565 std::vector<std::string> cmd_args = {
"--query", query,
"--format",
"json"};
567 std::string cmd_output;
568 auto status = registry.Execute(cmd_name, cmd_args, rom, &cmd_output);
571 if (!cmd_output.empty()) {
573 g_bridge.last_output = cmd_output;
574 return g_bridge.last_output.c_str();
576 return "{\"status\":\"success\", \"message\":\"Query executed but no "
577 "output returned.\"}";
581 g_bridge.last_output =
"{\"error\": \"Resource query failed\"}";
582 return g_bridge.last_output.c_str();
590 emscripten::allow_raw_pointers());
591 emscripten::function(
"getCompletions", &Z3edGetCompletions,
592 emscripten::allow_raw_pointers());
593 emscripten::function(
"setApiKey", &Z3edSetApiKey,
594 emscripten::allow_raw_pointers());
595 emscripten::function(
"configureAI", &Z3edConfigureAI,
596 emscripten::allow_raw_pointers());
597 emscripten::function(
"isReady", &Z3edIsReady);
598 emscripten::function(
"getRomInfo", &Z3edGetRomInfo,
599 emscripten::allow_raw_pointers());
600 emscripten::function(
"queryResource", &Z3edQueryResource,
601 emscripten::allow_raw_pointers());
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
static CommandRegistry & Instance()
std::string GenerateCompleteHelp() const
Generate complete help text (all commands)
DungeonEditorV2 - Simplified dungeon editor using component delegation.
EM_JS(void, CallJsAiDriver,(const char *history_json), { if(window.yaze &&window.yaze.ai &&window.yaze.ai.processAgentRequest) { window.yaze.ai.processAgentRequest(UTF8ToString(history_json));} else { console.error("AI Driver not found in window.yaze.ai.processAgentRequest");} })
yaze::editor::EditorManager * GetGlobalEditorManager()
constexpr char kProviderGemini[]
BrowserAIService * GetGlobalBrowserAIService()
Rom * rom()
Get the current ROM instance.
Editor * current_editor()
Get the currently active editor.
constexpr std::array< const char *, 14 > kEditorNames
const char * Z3edProcessCommand(const char *command)
EMSCRIPTEN_BINDINGS(yaze_debug_inspector)