yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
wasm_session_bridge.cc
Go to the documentation of this file.
1// clang-format off
2#ifdef __EMSCRIPTEN__
3
5
6#include <emscripten.h>
7#include <emscripten/bind.h>
8
9#include "absl/strings/str_format.h"
10#include "app/editor/editor.h"
12#include "rom/rom.h"
13#include "nlohmann/json.hpp"
14#include "util/log.h"
15
16namespace yaze {
17namespace app {
18namespace platform {
19
20// Static member initialization
21editor::EditorManager* WasmSessionBridge::editor_manager_ = nullptr;
22bool WasmSessionBridge::initialized_ = false;
23SharedSessionState WasmSessionBridge::current_state_;
24std::mutex WasmSessionBridge::state_mutex_;
25std::vector<WasmSessionBridge::StateChangeCallback> WasmSessionBridge::state_callbacks_;
26WasmSessionBridge::CommandCallback WasmSessionBridge::command_handler_;
27std::string WasmSessionBridge::pending_command_;
28std::string WasmSessionBridge::pending_result_;
29bool WasmSessionBridge::command_pending_ = false;
30
31// ============================================================================
32// SharedSessionState Implementation
33// ============================================================================
34
35std::string SharedSessionState::ToJson() const {
36 nlohmann::json j;
37
38 // ROM state
39 j["rom"]["loaded"] = rom_loaded;
40 j["rom"]["filename"] = rom_filename;
41 j["rom"]["title"] = rom_title;
42 j["rom"]["size"] = rom_size;
43 j["rom"]["dirty"] = rom_dirty;
44
45 // Editor state
46 j["editor"]["current"] = current_editor;
47 j["editor"]["type"] = current_editor_type;
48 j["editor"]["visible_cards"] = visible_cards;
49
50 // Session info
51 j["session"]["id"] = session_id;
52 j["session"]["count"] = session_count;
53 j["session"]["name"] = session_name;
54
55 // Feature flags
56 j["flags"]["save_all_palettes"] = flag_save_all_palettes;
57 j["flags"]["save_gfx_groups"] = flag_save_gfx_groups;
58 j["flags"]["save_overworld_maps"] = flag_save_overworld_maps;
59 j["flags"]["load_custom_overworld"] = flag_load_custom_overworld;
60 j["flags"]["apply_zscustom_asm"] = flag_apply_zscustom_asm;
61
62 // Project info
63 j["project"]["name"] = project_name;
64 j["project"]["path"] = project_path;
65 j["project"]["has_project"] = has_project;
66
67 // Z3ed state
68 j["z3ed"]["last_command"] = last_z3ed_command;
69 j["z3ed"]["last_result"] = last_z3ed_result;
70 j["z3ed"]["command_pending"] = z3ed_command_pending;
71
72 return j.dump();
73}
74
75SharedSessionState SharedSessionState::FromJson(const std::string& json) {
76 SharedSessionState state;
77
78 try {
79 auto j = nlohmann::json::parse(json);
80
81 // ROM state
82 if (j.contains("rom")) {
83 state.rom_loaded = j["rom"].value("loaded", false);
84 state.rom_filename = j["rom"].value("filename", "");
85 state.rom_title = j["rom"].value("title", "");
86 state.rom_size = j["rom"].value("size", 0);
87 state.rom_dirty = j["rom"].value("dirty", false);
88 }
89
90 // Editor state
91 if (j.contains("editor")) {
92 state.current_editor = j["editor"].value("current", "");
93 state.current_editor_type = j["editor"].value("type", 0);
94 if (j["editor"].contains("visible_cards")) {
95 state.visible_cards = j["editor"]["visible_cards"].get<std::vector<std::string>>();
96 }
97 }
98
99 // Session info
100 if (j.contains("session")) {
101 state.session_id = j["session"].value("id", 0);
102 state.session_count = j["session"].value("count", 1);
103 state.session_name = j["session"].value("name", "");
104 }
105
106 // Feature flags
107 if (j.contains("flags")) {
108 state.flag_save_all_palettes = j["flags"].value("save_all_palettes", false);
109 state.flag_save_gfx_groups = j["flags"].value("save_gfx_groups", false);
110 state.flag_save_overworld_maps = j["flags"].value("save_overworld_maps", true);
111 state.flag_load_custom_overworld = j["flags"].value("load_custom_overworld", false);
112 state.flag_apply_zscustom_asm = j["flags"].value("apply_zscustom_asm", false);
113 }
114
115 // Project info
116 if (j.contains("project")) {
117 state.project_name = j["project"].value("name", "");
118 state.project_path = j["project"].value("path", "");
119 state.has_project = j["project"].value("has_project", false);
120 }
121
122 } catch (const std::exception& e) {
123 LOG_ERROR("SharedSessionState", "Failed to parse JSON: %s", e.what());
124 }
125
126 return state;
127}
128
129void SharedSessionState::UpdateFromEditor(editor::EditorManager* manager) {
130 if (!manager) return;
131
132 // ROM state
133 auto* rom = manager->GetCurrentRom();
134 if (rom && rom->is_loaded()) {
135 rom_loaded = true;
136 rom_filename = rom->filename();
137 rom_title = rom->title();
138 rom_size = rom->size();
139 rom_dirty = rom->dirty();
140 } else {
141 rom_loaded = false;
142 rom_filename = "";
143 rom_title = "";
144 rom_size = 0;
145 rom_dirty = false;
146 }
147
148 // Editor state
149 auto* current = manager->GetCurrentEditor();
150 if (current) {
151 current_editor_type = static_cast<int>(current->type());
152 if (current_editor_type >= 0 &&
153 current_editor_type < static_cast<int>(editor::kEditorNames.size())) {
154 current_editor = editor::kEditorNames[current_editor_type];
155 }
156 }
157
158 // Session info
159 session_id = manager->GetCurrentSessionIndex();
160 session_count = manager->GetActiveSessionCount();
161
162 // Feature flags from global
163 auto& flags = core::FeatureFlags::get();
164 flag_save_all_palettes = flags.kSaveAllPalettes;
165 flag_save_gfx_groups = flags.kSaveGfxGroups;
166 flag_save_overworld_maps = flags.overworld.kSaveOverworldMaps;
167 flag_load_custom_overworld = flags.overworld.kLoadCustomOverworld;
168 flag_apply_zscustom_asm = flags.overworld.kApplyZSCustomOverworldASM;
169}
170
171absl::Status SharedSessionState::ApplyToEditor(editor::EditorManager* manager) {
172 if (!manager) {
173 return absl::InvalidArgumentError("EditorManager is null");
174 }
175
176 // Apply feature flags to global
177 auto& flags = core::FeatureFlags::get();
178 flags.kSaveAllPalettes = flag_save_all_palettes;
179 flags.kSaveGfxGroups = flag_save_gfx_groups;
180 flags.overworld.kSaveOverworldMaps = flag_save_overworld_maps;
181 flags.overworld.kLoadCustomOverworld = flag_load_custom_overworld;
182 flags.overworld.kApplyZSCustomOverworldASM = flag_apply_zscustom_asm;
183
184 // Switch editor if changed
185 if (!current_editor.empty()) {
186 for (size_t i = 0; i < editor::kEditorNames.size(); ++i) {
187 if (editor::kEditorNames[i] == current_editor) {
188 manager->SwitchToEditor(static_cast<editor::EditorType>(i));
189 break;
190 }
191 }
192 }
193
194 return absl::OkStatus();
195}
196
197// ============================================================================
198// JavaScript Bindings Setup
199// ============================================================================
200
201EM_JS(void, SetupYazeSessionApi, (), {
202 if (typeof Module === 'undefined') return;
203
204 // Create unified window.yaze namespace if not exists
205 if (!window.yaze) {
206 window.yaze = {};
207 }
208
209 // Session API namespace
210 window.yaze.session = {
211 // State management
212 getState: function() {
213 if (Module.sessionGetState) {
214 try { return JSON.parse(Module.sessionGetState()); }
215 catch(e) { return {error: e.message}; }
216 }
217 return {error: "API not ready"};
218 },
219
220 setState: function(state) {
221 if (Module.sessionSetState) {
222 try { return JSON.parse(Module.sessionSetState(JSON.stringify(state))); }
223 catch(e) { return {error: e.message}; }
224 }
225 return {error: "API not ready"};
226 },
227
228 getProperty: function(name) {
229 if (Module.sessionGetProperty) {
230 try { return JSON.parse(Module.sessionGetProperty(name)); }
231 catch(e) { return {error: e.message}; }
232 }
233 return {error: "API not ready"};
234 },
235
236 setProperty: function(name, value) {
237 if (Module.sessionSetProperty) {
238 try { return JSON.parse(Module.sessionSetProperty(name, JSON.stringify(value))); }
239 catch(e) { return {error: e.message}; }
240 }
241 return {error: "API not ready"};
242 },
243
244 refresh: function() {
245 if (Module.sessionRefreshState) {
246 try { return JSON.parse(Module.sessionRefreshState()); }
247 catch(e) { return {error: e.message}; }
248 }
249 return {error: "API not ready"};
250 },
251
252 // Feature flags
253 getFlags: function() {
254 if (Module.sessionGetFeatureFlags) {
255 try { return JSON.parse(Module.sessionGetFeatureFlags()); }
256 catch(e) { return {error: e.message}; }
257 }
258 return {error: "API not ready"};
259 },
260
261 setFlag: function(name, value) {
262 if (Module.sessionSetFeatureFlag) {
263 try { return JSON.parse(Module.sessionSetFeatureFlag(name, value)); }
264 catch(e) { return {error: e.message}; }
265 }
266 return {error: "API not ready"};
267 },
268
269 getAvailableFlags: function() {
270 if (Module.sessionGetAvailableFlags) {
271 try { return JSON.parse(Module.sessionGetAvailableFlags()); }
272 catch(e) { return {error: e.message}; }
273 }
274 return {error: "API not ready"};
275 },
276
277 // Z3ed integration
278 executeCommand: function(command) {
279 if (Module.sessionExecuteZ3edCommand) {
280 try { return JSON.parse(Module.sessionExecuteZ3edCommand(command)); }
281 catch(e) { return {error: e.message}; }
282 }
283 return {error: "API not ready"};
284 },
285
286 getPendingCommand: function() {
287 if (Module.sessionGetPendingCommand) {
288 try { return JSON.parse(Module.sessionGetPendingCommand()); }
289 catch(e) { return {error: e.message}; }
290 }
291 return {error: "API not ready"};
292 },
293
294 setCommandResult: function(result) {
295 if (Module.sessionSetCommandResult) {
296 try { return JSON.parse(Module.sessionSetCommandResult(JSON.stringify(result))); }
297 catch(e) { return {error: e.message}; }
298 }
299 return {error: "API not ready"};
300 },
301
302 isReady: function() {
303 return Module.sessionIsReady ? Module.sessionIsReady() : false;
304 }
305 };
306
307 console.log("[yaze] window.yaze.session API initialized");
308});
309
310// ============================================================================
311// WasmSessionBridge Implementation
312// ============================================================================
313
314void WasmSessionBridge::Initialize(editor::EditorManager* editor_manager) {
315 editor_manager_ = editor_manager;
316 initialized_ = (editor_manager_ != nullptr);
317
318 if (initialized_) {
319 SetupJavaScriptBindings();
320
321 // Initialize state from editor
322 std::lock_guard<std::mutex> lock(state_mutex_);
323 current_state_.UpdateFromEditor(editor_manager_);
324
325 LOG_INFO("WasmSessionBridge", "Session bridge initialized");
326 }
327}
328
329bool WasmSessionBridge::IsReady() {
330 return initialized_ && editor_manager_ != nullptr;
331}
332
333void WasmSessionBridge::SetupJavaScriptBindings() {
334 SetupYazeSessionApi();
335}
336
337std::string WasmSessionBridge::GetState() {
338 if (!IsReady()) {
339 return R"({"error": "Session bridge not initialized"})";
340 }
341
342 std::lock_guard<std::mutex> lock(state_mutex_);
343 current_state_.UpdateFromEditor(editor_manager_);
344 return current_state_.ToJson();
345}
346
347std::string WasmSessionBridge::SetState(const std::string& state_json) {
348 nlohmann::json result;
349
350 if (!IsReady()) {
351 result["success"] = false;
352 result["error"] = "Session bridge not initialized";
353 return result.dump();
354 }
355
356 try {
357 std::lock_guard<std::mutex> lock(state_mutex_);
358 auto new_state = SharedSessionState::FromJson(state_json);
359 auto status = new_state.ApplyToEditor(editor_manager_);
360
361 if (status.ok()) {
362 current_state_ = new_state;
363 result["success"] = true;
364 } else {
365 result["success"] = false;
366 result["error"] = status.ToString();
367 }
368 } catch (const std::exception& e) {
369 result["success"] = false;
370 result["error"] = e.what();
371 }
372
373 return result.dump();
374}
375
376std::string WasmSessionBridge::GetProperty(const std::string& property_name) {
377 nlohmann::json result;
378
379 if (!IsReady()) {
380 result["error"] = "Session bridge not initialized";
381 return result.dump();
382 }
383
384 std::lock_guard<std::mutex> lock(state_mutex_);
385 current_state_.UpdateFromEditor(editor_manager_);
386
387 if (property_name == "rom.loaded") {
388 result["value"] = current_state_.rom_loaded;
389 } else if (property_name == "rom.filename") {
390 result["value"] = current_state_.rom_filename;
391 } else if (property_name == "rom.title") {
392 result["value"] = current_state_.rom_title;
393 } else if (property_name == "rom.size") {
394 result["value"] = current_state_.rom_size;
395 } else if (property_name == "rom.dirty") {
396 result["value"] = current_state_.rom_dirty;
397 } else if (property_name == "editor.current") {
398 result["value"] = current_state_.current_editor;
399 } else if (property_name == "session.id") {
400 result["value"] = current_state_.session_id;
401 } else if (property_name == "session.count") {
402 result["value"] = current_state_.session_count;
403 } else {
404 result["error"] = "Unknown property: " + property_name;
405 }
406
407 return result.dump();
408}
409
410std::string WasmSessionBridge::SetProperty(const std::string& property_name,
411 const std::string& value) {
412 nlohmann::json result;
413
414 if (!IsReady()) {
415 result["success"] = false;
416 result["error"] = "Session bridge not initialized";
417 return result.dump();
418 }
419
420 // Most properties are read-only from external sources
421 // Only feature flags can be set
422 if (property_name.find("flags.") == 0) {
423 std::string flag_name = property_name.substr(6);
424 try {
425 bool flag_value = nlohmann::json::parse(value).get<bool>();
426 return SetFeatureFlag(flag_name, flag_value);
427 } catch (const std::exception& e) {
428 result["success"] = false;
429 result["error"] = "Invalid boolean value";
430 }
431 } else {
432 result["success"] = false;
433 result["error"] = "Property is read-only: " + property_name;
434 }
435
436 return result.dump();
437}
438
439// ============================================================================
440// Feature Flags
441// ============================================================================
442
443std::string WasmSessionBridge::GetFeatureFlags() {
444 nlohmann::json result;
445
446 auto& flags = core::FeatureFlags::get();
447
448 result["save_all_palettes"] = flags.kSaveAllPalettes;
449 result["save_gfx_groups"] = flags.kSaveGfxGroups;
450 result["save_with_change_queue"] = flags.kSaveWithChangeQueue;
451 result["save_dungeon_maps"] = flags.kSaveDungeonMaps;
452 result["save_graphics_sheet"] = flags.kSaveGraphicsSheet;
453 result["log_to_console"] = flags.kLogToConsole;
454 result["enable_performance_monitoring"] = flags.kEnablePerformanceMonitoring;
455 result["enable_tiered_gfx_architecture"] = flags.kEnableTieredGfxArchitecture;
456 result["use_native_file_dialog"] = flags.kUseNativeFileDialog;
457
458 // Overworld flags
459 result["overworld"]["draw_sprites"] = flags.overworld.kDrawOverworldSprites;
460 result["overworld"]["save_maps"] = flags.overworld.kSaveOverworldMaps;
461 result["overworld"]["save_entrances"] = flags.overworld.kSaveOverworldEntrances;
462 result["overworld"]["save_exits"] = flags.overworld.kSaveOverworldExits;
463 result["overworld"]["save_items"] = flags.overworld.kSaveOverworldItems;
464 result["overworld"]["save_properties"] = flags.overworld.kSaveOverworldProperties;
465 result["overworld"]["load_custom"] = flags.overworld.kLoadCustomOverworld;
466 result["overworld"]["apply_zscustom_asm"] = flags.overworld.kApplyZSCustomOverworldASM;
467
468 return result.dump();
469}
470
471std::string WasmSessionBridge::SetFeatureFlag(const std::string& flag_name, bool value) {
472 nlohmann::json result;
473
474 auto& flags = core::FeatureFlags::get();
475 bool found = true;
476
477 if (flag_name == "save_all_palettes") {
478 flags.kSaveAllPalettes = value;
479 } else if (flag_name == "save_gfx_groups") {
480 flags.kSaveGfxGroups = value;
481 } else if (flag_name == "save_with_change_queue") {
482 flags.kSaveWithChangeQueue = value;
483 } else if (flag_name == "save_dungeon_maps") {
484 flags.kSaveDungeonMaps = value;
485 } else if (flag_name == "save_graphics_sheet") {
486 flags.kSaveGraphicsSheet = value;
487 } else if (flag_name == "log_to_console") {
488 flags.kLogToConsole = value;
489 } else if (flag_name == "enable_performance_monitoring") {
490 flags.kEnablePerformanceMonitoring = value;
491 } else if (flag_name == "overworld.draw_sprites") {
492 flags.overworld.kDrawOverworldSprites = value;
493 } else if (flag_name == "overworld.save_maps") {
494 flags.overworld.kSaveOverworldMaps = value;
495 } else if (flag_name == "overworld.save_entrances") {
496 flags.overworld.kSaveOverworldEntrances = value;
497 } else if (flag_name == "overworld.save_exits") {
498 flags.overworld.kSaveOverworldExits = value;
499 } else if (flag_name == "overworld.save_items") {
500 flags.overworld.kSaveOverworldItems = value;
501 } else if (flag_name == "overworld.save_properties") {
502 flags.overworld.kSaveOverworldProperties = value;
503 } else if (flag_name == "overworld.load_custom") {
504 flags.overworld.kLoadCustomOverworld = value;
505 } else if (flag_name == "overworld.apply_zscustom_asm") {
506 flags.overworld.kApplyZSCustomOverworldASM = value;
507 } else {
508 found = false;
509 }
510
511 if (found) {
512 result["success"] = true;
513 result["flag"] = flag_name;
514 result["value"] = value;
515 LOG_INFO("WasmSessionBridge", "Set flag %s = %s", flag_name.c_str(), value ? "true" : "false");
516 } else {
517 result["success"] = false;
518 result["error"] = "Unknown flag: " + flag_name;
519 }
520
521 return result.dump();
522}
523
524std::string WasmSessionBridge::GetAvailableFlags() {
525 nlohmann::json result = nlohmann::json::array();
526
527 result.push_back("save_all_palettes");
528 result.push_back("save_gfx_groups");
529 result.push_back("save_with_change_queue");
530 result.push_back("save_dungeon_maps");
531 result.push_back("save_graphics_sheet");
532 result.push_back("log_to_console");
533 result.push_back("enable_performance_monitoring");
534 result.push_back("enable_tiered_gfx_architecture");
535 result.push_back("overworld.draw_sprites");
536 result.push_back("overworld.save_maps");
537 result.push_back("overworld.save_entrances");
538 result.push_back("overworld.save_exits");
539 result.push_back("overworld.save_items");
540 result.push_back("overworld.save_properties");
541 result.push_back("overworld.load_custom");
542 result.push_back("overworld.apply_zscustom_asm");
543
544 return result.dump();
545}
546
547// ============================================================================
548// Z3ed Command Integration
549// ============================================================================
550
551std::string WasmSessionBridge::ExecuteZ3edCommand(const std::string& command) {
552 nlohmann::json result;
553
554 if (!IsReady()) {
555 result["success"] = false;
556 result["error"] = "Session bridge not initialized";
557 return result.dump();
558 }
559
560 // If we have a command handler, use it directly
561 if (command_handler_) {
562 std::string output = command_handler_(command);
563 result["success"] = true;
564 result["output"] = output;
565
566 std::lock_guard<std::mutex> lock(state_mutex_);
567 current_state_.last_z3ed_command = command;
568 current_state_.last_z3ed_result = output;
569 return result.dump();
570 }
571
572 // Otherwise, queue for external CLI
573 {
574 std::lock_guard<std::mutex> lock(state_mutex_);
575 pending_command_ = command;
576 command_pending_ = true;
577 current_state_.z3ed_command_pending = true;
578 current_state_.last_z3ed_command = command;
579 }
580
581 result["success"] = true;
582 result["queued"] = true;
583 result["command"] = command;
584
585 LOG_INFO("WasmSessionBridge", "Queued z3ed command: %s", command.c_str());
586 return result.dump();
587}
588
589std::string WasmSessionBridge::GetPendingCommand() {
590 nlohmann::json result;
591
592 std::lock_guard<std::mutex> lock(state_mutex_);
593
594 if (command_pending_) {
595 result["pending"] = true;
596 result["command"] = pending_command_;
597 } else {
598 result["pending"] = false;
599 }
600
601 return result.dump();
602}
603
604std::string WasmSessionBridge::SetCommandResult(const std::string& result_str) {
605 nlohmann::json result;
606
607 std::lock_guard<std::mutex> lock(state_mutex_);
608
609 pending_result_ = result_str;
610 command_pending_ = false;
611 current_state_.z3ed_command_pending = false;
612 current_state_.last_z3ed_result = result_str;
613
614 result["success"] = true;
615
616 return result.dump();
617}
618
619void WasmSessionBridge::SetCommandHandler(CommandCallback handler) {
620 command_handler_ = handler;
621}
622
623// ============================================================================
624// Event System
625// ============================================================================
626
627void WasmSessionBridge::OnStateChange(StateChangeCallback callback) {
628 state_callbacks_.push_back(callback);
629}
630
631void WasmSessionBridge::NotifyStateChange() {
632 std::lock_guard<std::mutex> lock(state_mutex_);
633 current_state_.UpdateFromEditor(editor_manager_);
634
635 for (const auto& callback : state_callbacks_) {
636 if (callback) {
637 callback(current_state_);
638 }
639 }
640}
641
642std::string WasmSessionBridge::RefreshState() {
643 if (!IsReady()) {
644 return R"({"error": "Session bridge not initialized"})";
645 }
646
647 std::lock_guard<std::mutex> lock(state_mutex_);
648 current_state_.UpdateFromEditor(editor_manager_);
649
650 nlohmann::json result;
651 result["success"] = true;
652 result["state"] = nlohmann::json::parse(current_state_.ToJson());
653
654 return result.dump();
655}
656
657// ============================================================================
658// Emscripten Bindings
659// ============================================================================
660
661EMSCRIPTEN_BINDINGS(wasm_session_bridge) {
662 emscripten::function("sessionIsReady", &WasmSessionBridge::IsReady);
663 emscripten::function("sessionGetState", &WasmSessionBridge::GetState);
664 emscripten::function("sessionSetState", &WasmSessionBridge::SetState);
665 emscripten::function("sessionGetProperty", &WasmSessionBridge::GetProperty);
666 emscripten::function("sessionSetProperty", &WasmSessionBridge::SetProperty);
667 emscripten::function("sessionGetFeatureFlags", &WasmSessionBridge::GetFeatureFlags);
668 emscripten::function("sessionSetFeatureFlag", &WasmSessionBridge::SetFeatureFlag);
669 emscripten::function("sessionGetAvailableFlags", &WasmSessionBridge::GetAvailableFlags);
670 emscripten::function("sessionExecuteZ3edCommand", &WasmSessionBridge::ExecuteZ3edCommand);
671 emscripten::function("sessionGetPendingCommand", &WasmSessionBridge::GetPendingCommand);
672 emscripten::function("sessionSetCommandResult", &WasmSessionBridge::SetCommandResult);
673 emscripten::function("sessionRefreshState", &WasmSessionBridge::RefreshState);
674}
675
676} // namespace platform
677} // namespace app
678} // namespace yaze
679
680#endif // __EMSCRIPTEN__
681
std::function< std::string(const std::string &)> CommandCallback
static Flags & get()
Definition features.h:92
#define LOG_ERROR(category, format,...)
Definition log.h:109
#define LOG_INFO(category, format,...)
Definition log.h:105
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");} })
constexpr std::array< const char *, 14 > kEditorNames
Definition editor.h:167
void UpdateFromEditor(editor::EditorManager *)
static SharedSessionState FromJson(const std::string &)
std::string executeCommand(std::string command)
EMSCRIPTEN_BINDINGS(yaze_debug_inspector)