9#include "absl/strings/str_format.h"
23 const std::string& category,
24 const std::string& description,
25 const std::string& shortcut,
26 std::function<
void()> callback) {
40 it->second.last_used_ms =
41 std::chrono::duration_cast<std::chrono::milliseconds>(
42 std::chrono::system_clock::now().time_since_epoch())
48 const std::string& query) {
56 std::string text_lower = text;
57 std::string query_lower = query;
58 std::transform(text_lower.begin(), text_lower.end(), text_lower.begin(),
60 std::transform(query_lower.begin(), query_lower.end(), query_lower.begin(),
64 if (text_lower == query_lower)
68 if (text_lower.find(query_lower) == 0)
72 if (text_lower.find(query_lower) != std::string::npos)
76 while (text_idx < text_lower.length() && query_idx < query_lower.length()) {
77 if (text_lower[text_idx] == query_lower[query_idx]) {
85 if (query_idx != query_lower.length())
92 const std::string& query) {
93 std::vector<std::pair<int, CommandEntry>> scored;
95 for (
const auto& [name, entry] :
commands_) {
99 score +=
FuzzyScore(entry.category, query) / 2;
100 score +=
FuzzyScore(entry.description, query) / 4;
103 score += entry.usage_count * 2;
105 auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
106 std::chrono::system_clock::now().time_since_epoch())
108 int64_t age_ms = now_ms - entry.last_used_ms;
109 if (age_ms < 60000) {
111 }
else if (age_ms < 3600000) {
116 scored.push_back({score, entry});
121 std::sort(scored.begin(), scored.end(),
122 [](
const auto& a,
const auto& b) { return a.first > b.first; });
124 std::vector<CommandEntry> results;
125 for (
const auto& [score, entry] : scored) {
126 results.push_back(entry);
133 std::vector<CommandEntry> recent;
135 for (
const auto& [name, entry] :
commands_) {
136 if (entry.usage_count > 0) {
137 recent.push_back(entry);
141 std::sort(recent.begin(), recent.end(),
143 return a.last_used_ms > b.last_used_ms;
146 if (recent.size() >
static_cast<size_t>(limit)) {
147 recent.resize(limit);
154 std::vector<CommandEntry> frequent;
156 for (
const auto& [name, entry] :
commands_) {
157 if (entry.usage_count > 0) {
158 frequent.push_back(entry);
162 std::sort(frequent.begin(), frequent.end(),
164 return a.usage_count > b.usage_count;
167 if (frequent.size() >
static_cast<size_t>(limit)) {
168 frequent.resize(limit);
180 for (
const auto& [name, entry] :
commands_) {
181 if (entry.usage_count > 0) {
183 cmd[
"usage_count"] = entry.usage_count;
184 cmd[
"last_used_ms"] = entry.last_used_ms;
185 j[
"commands"][name] = cmd;
189 std::ofstream file(filepath);
190 if (file.is_open()) {
192 LOG_INFO(
"CommandPalette",
"Saved command history to %s",
195 }
catch (
const std::exception& e) {
196 LOG_ERROR(
"CommandPalette",
"Failed to save command history: %s", e.what());
201 if (!std::filesystem::exists(filepath)) {
206 std::ifstream file(filepath);
207 if (!file.is_open()) {
211 std::string content((std::istreambuf_iterator<char>(file)),
212 std::istreambuf_iterator<char>());
220 for (
auto& [name, cmd_json] : j[
"commands"].
items()) {
223 it->second.usage_count = cmd_json.value(
"usage_count", 0);
224 it->second.last_used_ms = cmd_json.value(
"last_used_ms", int64_t{0});
229 LOG_INFO(
"CommandPalette",
"Loaded %d command history entries from %s",
230 loaded, filepath.c_str());
231 }
catch (
const std::exception& e) {
232 LOG_ERROR(
"CommandPalette",
"Failed to load command history: %s", e.what());
237 std::vector<CommandEntry> result;
239 for (
const auto& [name, entry] :
commands_) {
240 result.push_back(entry);
247 if (!panel_manager)
return;
252 for (
const auto& [prefixed_id, descriptor] : descriptors) {
254 const std::string& base_id = descriptor.card_id;
257 std::string show_name =
258 absl::StrFormat(
"Show: %s", descriptor.display_name);
259 std::string show_desc =
260 absl::StrFormat(
"Show the %s panel", descriptor.display_name);
263 descriptor.shortcut_hint,
264 [panel_manager, base_id, session_id]() {
265 panel_manager->ShowPanel(session_id, base_id);
269 std::string hide_name =
270 absl::StrFormat(
"Hide: %s", descriptor.display_name);
271 std::string hide_desc =
272 absl::StrFormat(
"Hide the %s panel", descriptor.display_name);
275 [panel_manager, base_id, session_id]() {
276 panel_manager->
HidePanel(session_id, base_id);
280 std::string toggle_name =
281 absl::StrFormat(
"Toggle: %s", descriptor.display_name);
282 std::string toggle_desc =
283 absl::StrFormat(
"Toggle the %s panel visibility", descriptor.display_name);
286 [panel_manager, base_id, session_id]() {
293 std::function<
void(
const std::string&)> switch_callback) {
297 for (
const auto& category : categories) {
298 std::string name = absl::StrFormat(
"Switch to: %s Editor", category);
300 absl::StrFormat(
"Switch to the %s editor category", category);
303 [switch_callback, category]() { switch_callback(category); });
308 std::function<
void(
const std::string&)> apply_callback) {
312 const char* description;
315 static const ProfileInfo profiles[] = {
317 "Focused editing workspace with minimal panel noise"},
319 "Debugger-first workspace for tracing and memory tools"},
320 {
"mapping",
"Mapping",
321 "Map-centric workspace for overworld/dungeon flows"},
322 {
"chat",
"Chat + Agent",
323 "Agent collaboration workspace with chat-centric layout"},
326 for (
const auto& profile : profiles) {
327 std::string name = absl::StrFormat(
"Apply Profile: %s", profile.name);
329 [apply_callback, profile_id = std::string(profile.id)]() {
330 apply_callback(
"profile:" + profile_id);
335 "Capture current layout as temporary session snapshot",
"",
336 [apply_callback]() { apply_callback(
"session:capture"); });
338 "Restore temporary session snapshot",
"",
339 [apply_callback]() { apply_callback(
"session:restore"); });
341 "Clear temporary session snapshot",
"",
342 [apply_callback]() { apply_callback(
"session:clear"); });
347 const char* description;
350 static const PresetInfo presets[] = {
351 {
"Minimal",
"Minimal workspace with essential panels only"},
352 {
"Developer",
"Debug-focused layout with emulator and memory tools"},
353 {
"Designer",
"Visual-focused layout for graphics and palette editing"},
354 {
"Modder",
"Full-featured layout with all panels available"},
355 {
"Overworld Expert",
"Optimized layout for overworld editing"},
356 {
"Dungeon Expert",
"Optimized layout for dungeon editing"},
357 {
"Testing",
"QA-focused layout with testing tools"},
358 {
"Audio",
"Music and sound editing focused layout"},
361 for (
const auto& preset : presets) {
362 std::string name = absl::StrFormat(
"Apply Layout: %s", preset.name);
365 [apply_callback, preset_name = std::string(preset.name)]() {
366 apply_callback(preset_name);
372 "Reset to the default layout for current editor",
"",
373 [apply_callback]() { apply_callback(
"Default"); });
377 std::function<
void(
const std::string&)> open_callback) {
378 const auto& recent_files =
381 for (
const auto& filepath : recent_files) {
383 if (!std::filesystem::exists(filepath)) {
388 std::filesystem::path path(filepath);
389 std::string filename = path.filename().string();
391 std::string name = absl::StrFormat(
"Open Recent: %s", filename);
392 std::string desc = absl::StrFormat(
"Open file %s", filepath);
395 [open_callback, filepath]() { open_callback(filepath); });
400 constexpr int kTotalRooms = 0x128;
401 for (
int room_id = 0; room_id < kTotalRooms; ++room_id) {
403 const std::string room_name =
404 label.empty() ? absl::StrFormat(
"Room %03X", room_id) : label;
406 const std::string name =
407 absl::StrFormat(
"Dungeon: Open Room [%03X] %s", room_id, room_name);
408 const std::string desc =
409 absl::StrFormat(
"Jump to dungeon room %03X", room_id);
412 [room_id, session_id]() {
static Json parse(const std::string &)
std::string dump(int=-1, char=' ', bool=false, int=0) const
bool contains(const std::string &) const
std::vector< CommandEntry > SearchCommands(const std::string &query)
void RegisterDungeonRoomCommands(size_t session_id)
Register dungeon room navigation commands.
void RegisterPanelCommands(PanelManager *panel_manager, size_t session_id)
Register all panel toggle commands from PanelManager.
void SaveHistory(const std::string &filepath)
Save command usage history to disk.
void RegisterLayoutCommands(std::function< void(const std::string &)> apply_callback)
Register layout preset commands.
void AddCommand(const std::string &name, const std::string &category, const std::string &description, const std::string &shortcut, std::function< void()> callback)
void LoadHistory(const std::string &filepath)
Load command usage history from disk.
std::vector< CommandEntry > GetAllCommands() const
Get all registered commands.
void RecordUsage(const std::string &name)
std::unordered_map< std::string, CommandEntry > commands_
void RegisterEditorCommands(std::function< void(const std::string &)> switch_callback)
Register all editor switch commands.
std::vector< CommandEntry > GetRecentCommands(int limit=10)
void RegisterRecentFilesCommands(std::function< void(const std::string &)> open_callback)
Register commands to open recent files.
std::vector< CommandEntry > GetFrequentCommands(int limit=10)
static int FuzzyScore(const std::string &text, const std::string &query)
static std::vector< std::string > GetAllEditorCategories()
Get all editor categories in display order for sidebar.
Central registry for all editor cards with session awareness and dependency injection.
bool TogglePanel(size_t session_id, const std::string &base_card_id)
const std::unordered_map< std::string, PanelDescriptor > & GetAllPanelDescriptors() const
Get all panel descriptors (for layout designer, panel browser, etc.)
bool HidePanel(size_t session_id, const std::string &base_card_id)
static RecentFilesManager & GetInstance()
const std::vector< std::string > & GetRecentFiles() const
#define LOG_ERROR(category, format,...)
#define LOG_INFO(category, format,...)
::yaze::EventBus * event_bus()
Get the current EventBus instance.
std::string GetRoomLabel(int id)
Convenience function to get a room label.
static constexpr const char * kLayout
static constexpr const char * kFile
static constexpr const char * kEditor
static constexpr const char * kPanel
static constexpr const char * kNavigation
std::function< void()> callback
static JumpToRoomRequestEvent Create(int room, size_t session=0)