yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
command_palette.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cctype>
5#include <chrono>
6#include <filesystem>
7#include <fstream>
8
9#include "absl/strings/str_format.h"
17#include "core/project.h"
18#include "util/json.h"
19#include "util/log.h"
21
22namespace yaze {
23namespace editor {
24
25void CommandPalette::AddCommand(const std::string& name,
26 const std::string& category,
27 const std::string& description,
28 const std::string& shortcut,
29 std::function<void()> callback) {
30 CommandEntry entry;
31 entry.name = name;
32 entry.category = category;
33 entry.description = description;
34 entry.shortcut = shortcut;
35 entry.callback = callback;
37 commands_[name] = entry;
38}
39
41 commands_.clear();
42 providers_.clear();
44}
45
47 std::unique_ptr<CommandProvider> provider) {
48 if (!provider)
49 return;
50 const std::string id = provider->ProviderId();
51 if (id.empty()) {
52 LOG_WARN("CommandPalette",
53 "Provider with empty id refused; skipping registration");
54 return;
55 }
56 // Replace any prior provider with the same id (warn so duplicate-id bugs
57 // don't hide silently).
58 for (auto it = providers_.begin(); it != providers_.end(); ++it) {
59 if ((*it)->ProviderId() == id) {
60 LOG_WARN("CommandPalette", "Replacing existing CommandProvider '%s'",
61 id.c_str());
63 providers_.erase(it);
64 break;
65 }
66 }
67 CommandProvider* raw = provider.get();
68 providers_.push_back(std::move(provider));
70 raw->Provide(this);
72}
73
74void CommandPalette::UnregisterProvider(const std::string& provider_id) {
75 RemoveProviderCommands(provider_id);
76 providers_.erase(
77 std::remove_if(providers_.begin(), providers_.end(),
78 [&](const std::unique_ptr<CommandProvider>& p) {
79 return p && p->ProviderId() == provider_id;
80 }),
81 providers_.end());
82}
83
84void CommandPalette::RefreshProvider(const std::string& provider_id) {
85 for (auto& provider : providers_) {
86 if (provider && provider->ProviderId() == provider_id) {
87 RemoveProviderCommands(provider_id);
88 current_provider_id_ = provider_id;
89 provider->Provide(this);
91 return;
92 }
93 }
94}
95
97 // Snapshot ids so we don't iterate while mutating.
98 std::vector<std::string> ids;
99 ids.reserve(providers_.size());
100 for (const auto& provider : providers_) {
101 if (provider)
102 ids.push_back(provider->ProviderId());
103 }
104 for (const auto& id : ids) {
105 RefreshProvider(id);
106 }
107}
108
109void CommandPalette::RemoveProviderCommands(const std::string& provider_id) {
110 if (provider_id.empty())
111 return;
112 for (auto it = commands_.begin(); it != commands_.end();) {
113 if (it->second.provider_id == provider_id) {
114 it = commands_.erase(it);
115 } else {
116 ++it;
117 }
118 }
119}
120
121void CommandPalette::RecordUsage(const std::string& name) {
122 auto it = commands_.find(name);
123 if (it != commands_.end()) {
124 it->second.usage_count++;
125 it->second.last_used_ms =
126 std::chrono::duration_cast<std::chrono::milliseconds>(
127 std::chrono::system_clock::now().time_since_epoch())
128 .count();
129 }
130}
131
132/*static*/ int CommandPalette::FuzzyScore(const std::string& text,
133 const std::string& query) {
134 if (query.empty())
135 return 0;
136
137 int score = 0;
138 size_t text_idx = 0;
139 size_t query_idx = 0;
140
141 std::string text_lower = text;
142 std::string query_lower = query;
143 std::transform(text_lower.begin(), text_lower.end(), text_lower.begin(),
144 ::tolower);
145 std::transform(query_lower.begin(), query_lower.end(), query_lower.begin(),
146 ::tolower);
147
148 // Exact match bonus
149 if (text_lower == query_lower)
150 return 1000;
151
152 // Starts with bonus
153 if (text_lower.find(query_lower) == 0)
154 return 500;
155
156 // Contains bonus
157 if (text_lower.find(query_lower) != std::string::npos)
158 return 250;
159
160 // Fuzzy match - characters in order
161 while (text_idx < text_lower.length() && query_idx < query_lower.length()) {
162 if (text_lower[text_idx] == query_lower[query_idx]) {
163 score += 10;
164 query_idx++;
165 }
166 text_idx++;
167 }
168
169 // Penalty if not all characters matched
170 if (query_idx != query_lower.length())
171 return 0;
172
173 return score;
174}
175
176std::vector<CommandEntry> CommandPalette::SearchCommands(
177 const std::string& query) {
178 std::vector<std::pair<int, CommandEntry>> scored;
179
180 for (const auto& [name, entry] : commands_) {
181 int score = FuzzyScore(entry.name, query);
182
183 // Also check category and description
184 score += FuzzyScore(entry.category, query) / 2;
185 score += FuzzyScore(entry.description, query) / 4;
186
187 // Frecency bonus (frequency + recency)
188 score += entry.usage_count * 2;
189
190 auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
191 std::chrono::system_clock::now().time_since_epoch())
192 .count();
193 int64_t age_ms = now_ms - entry.last_used_ms;
194 if (age_ms < 60000) { // Used in last minute
195 score += 50;
196 } else if (age_ms < 3600000) { // Last hour
197 score += 25;
198 }
199
200 if (score > 0) {
201 scored.push_back({score, entry});
202 }
203 }
204
205 // Sort by score descending
206 std::sort(scored.begin(), scored.end(),
207 [](const auto& a, const auto& b) { return a.first > b.first; });
208
209 std::vector<CommandEntry> results;
210 for (const auto& [score, entry] : scored) {
211 results.push_back(entry);
212 }
213
214 return results;
215}
216
217std::vector<CommandEntry> CommandPalette::GetRecentCommands(int limit) {
218 std::vector<CommandEntry> recent;
219
220 for (const auto& [name, entry] : commands_) {
221 if (entry.usage_count > 0) {
222 recent.push_back(entry);
223 }
224 }
225
226 std::sort(recent.begin(), recent.end(),
227 [](const CommandEntry& a, const CommandEntry& b) {
228 return a.last_used_ms > b.last_used_ms;
229 });
230
231 if (recent.size() > static_cast<size_t>(limit)) {
232 recent.resize(limit);
233 }
234
235 return recent;
236}
237
238std::vector<CommandEntry> CommandPalette::GetFrequentCommands(int limit) {
239 std::vector<CommandEntry> frequent;
240
241 for (const auto& [name, entry] : commands_) {
242 if (entry.usage_count > 0) {
243 frequent.push_back(entry);
244 }
245 }
246
247 std::sort(frequent.begin(), frequent.end(),
248 [](const CommandEntry& a, const CommandEntry& b) {
249 return a.usage_count > b.usage_count;
250 });
251
252 if (frequent.size() > static_cast<size_t>(limit)) {
253 frequent.resize(limit);
254 }
255
256 return frequent;
257}
258
259void CommandPalette::SaveHistory(const std::string& filepath) {
260 try {
261 yaze::Json j;
262 j["version"] = 1;
263 j["commands"] = yaze::Json::object();
264
265 for (const auto& [name, entry] : commands_) {
266 if (entry.usage_count > 0) {
267 yaze::Json cmd;
268 cmd["usage_count"] = entry.usage_count;
269 cmd["last_used_ms"] = entry.last_used_ms;
270 j["commands"][name] = cmd;
271 }
272 }
273
274 std::ofstream file(filepath);
275 if (file.is_open()) {
276 file << j.dump(2);
277 LOG_INFO("CommandPalette", "Saved command history to %s",
278 filepath.c_str());
279 }
280 } catch (const std::exception& e) {
281 LOG_ERROR("CommandPalette", "Failed to save command history: %s", e.what());
282 }
283}
284
285void CommandPalette::LoadHistory(const std::string& filepath) {
286 if (!std::filesystem::exists(filepath)) {
287 return;
288 }
289
290 try {
291 std::ifstream file(filepath);
292 if (!file.is_open()) {
293 return;
294 }
295
296 std::string content((std::istreambuf_iterator<char>(file)),
297 std::istreambuf_iterator<char>());
298 yaze::Json j = yaze::Json::parse(content);
299
300 if (!j.contains("commands") || !j["commands"].is_object()) {
301 return;
302 }
303
304 int loaded = 0;
305 for (auto& [name, cmd_json] : j["commands"].items()) {
306 auto it = commands_.find(name);
307 if (it != commands_.end()) {
308 it->second.usage_count = cmd_json.value("usage_count", 0);
309 it->second.last_used_ms = cmd_json.value("last_used_ms", int64_t{0});
310 loaded++;
311 }
312 }
313
314 LOG_INFO("CommandPalette", "Loaded %d command history entries from %s",
315 loaded, filepath.c_str());
316 } catch (const std::exception& e) {
317 LOG_ERROR("CommandPalette", "Failed to load command history: %s", e.what());
318 }
319}
320
321std::vector<CommandEntry> CommandPalette::GetAllCommands() const {
322 std::vector<CommandEntry> result;
323 result.reserve(commands_.size());
324 for (const auto& [name, entry] : commands_) {
325 result.push_back(entry);
326 }
327 return result;
328}
329
331 WorkspaceWindowManager* window_manager, size_t session_id) {
332 if (!window_manager)
333 return;
334
335 for (const auto& base_id : window_manager->GetWindowsInSession(session_id)) {
336 const auto* descriptor =
337 window_manager->GetWindowDescriptor(session_id, base_id);
338 if (!descriptor) {
339 continue;
340 }
341
342 // Create show command
343 std::string show_name =
344 absl::StrFormat("Show: %s", descriptor->display_name);
345 std::string show_desc =
346 absl::StrFormat("Open the %s window", descriptor->display_name);
347
348 AddCommand(show_name, CommandCategory::kPanel, show_desc,
349 descriptor->shortcut_hint,
350 [window_manager, base_id, session_id]() {
351 window_manager->OpenWindow(session_id, base_id);
352 });
353
354 // Create hide command
355 std::string hide_name =
356 absl::StrFormat("Hide: %s", descriptor->display_name);
357 std::string hide_desc =
358 absl::StrFormat("Close the %s window", descriptor->display_name);
359
360 AddCommand(hide_name, CommandCategory::kPanel, hide_desc, "",
361 [window_manager, base_id, session_id]() {
362 window_manager->CloseWindow(session_id, base_id);
363 });
364
365 // Create toggle command (legacy name kept for muscle memory / docs)
366 std::string toggle_name =
367 absl::StrFormat("Toggle: %s", descriptor->display_name);
368 std::string toggle_desc = absl::StrFormat("Toggle the %s window visibility",
369 descriptor->display_name);
370
371 auto toggle_fn = [window_manager, base_id, session_id]() {
372 window_manager->ToggleWindow(session_id, base_id);
373 };
374
375 AddCommand(toggle_name, CommandCategory::kPanel, toggle_desc, "",
376 toggle_fn);
377
378 // Window Finder selects a destination, rather than toggling its visibility.
379 // Keep the explicit Toggle command above for open/close actions.
380 std::string window_name =
381 absl::StrFormat("window: %s", descriptor->display_name);
383 window_name, CommandCategory::kPanel, show_desc,
384 descriptor->shortcut_hint, [window_manager, base_id, session_id]() {
385 WindowHost host(window_manager);
386 const bool opened = ImGui::GetCurrentContext() != nullptr
387 ? host.OpenAndFocusWindow(session_id, base_id)
388 : host.OpenWindow(session_id, base_id);
389 if (opened) {
390 window_manager->MarkWindowRecentlyUsed(base_id);
391 }
392 });
393
394 // Pin-to-global toggle. Mirrors the sidebar right-click pin + the panel
395 // tab pin UI, so users who live in the command palette never need to
396 // reach for the sidebar just to make a panel survive editor switches.
397 std::string pin_toggle_name =
398 absl::StrFormat("Toggle Pin: %s", descriptor->display_name);
399 std::string pin_toggle_desc =
400 absl::StrFormat("Keep the %s window visible across editor switches",
401 descriptor->display_name);
402
403 AddCommand(pin_toggle_name, CommandCategory::kPanel, pin_toggle_desc, "",
404 [window_manager, base_id, session_id]() {
405 const bool pinned =
406 window_manager->IsWindowPinned(session_id, base_id);
407 window_manager->SetWindowPinned(session_id, base_id, !pinned);
408 });
409 }
410}
411
412void CommandPalette::RegisterDrawerCommands(
413 std::function<void(int drawer_type)> toggle_callback,
414 std::function<void()> cycle_next, std::function<void()> cycle_prev) {
415 if (!toggle_callback)
416 return;
417
418 for (const auto& entry : GetDrawerCatalog()) {
419 if (!entry.name)
420 continue;
421
422 const int type_as_int = static_cast<int>(entry.type);
423 std::string name = absl::StrFormat("drawer: %s", entry.name);
424 std::string desc =
425 absl::StrFormat("Toggle the %s right drawer", entry.name);
426
427 AddCommand(
428 name, CommandCategory::kDrawer, desc, /*shortcut=*/"",
429 [toggle_callback, type_as_int]() { toggle_callback(type_as_int); });
430 }
431
432 if (cycle_next) {
433 AddCommand("drawer: Next", CommandCategory::kDrawer,
434 "Cycle to the next right drawer", "", std::move(cycle_next));
435 }
436 if (cycle_prev) {
437 AddCommand("drawer: Previous", CommandCategory::kDrawer,
438 "Cycle to the previous right drawer", "", std::move(cycle_prev));
439 }
440}
441
442void CommandPalette::RegisterEditorCommands(
443 std::function<void(const std::string&)> switch_callback) {
444 // Get all editor categories
445 auto categories = EditorRegistry::GetAllEditorCategories();
446
447 for (const auto& category : categories) {
448 std::string name = absl::StrFormat("Switch to: %s Editor", category);
449 std::string desc =
450 absl::StrFormat("Switch to the %s editor category", category);
451
452 AddCommand(name, CommandCategory::kEditor, desc, "",
453 [switch_callback, category]() { switch_callback(category); });
454 }
455}
456
457void CommandPalette::RegisterLayoutCommands(
458 std::function<void(const std::string&)> apply_callback) {
459 struct ProfileInfo {
460 const char* id;
461 const char* name;
462 const char* description;
463 };
464
465 static const ProfileInfo profiles[] = {
466 {"code", "Code", "Focused editing workspace with minimal panel noise"},
467 {"debug", "Debug",
468 "Debugger-first workspace for tracing and memory tools"},
469 {"mapping", "Mapping",
470 "Map-centric workspace for overworld/dungeon flows"},
471 {"chat", "Chat + Agent",
472 "Agent collaboration workspace with chat-centric layout"},
473 };
474
475 for (const auto& profile : profiles) {
476 std::string name = absl::StrFormat("Apply Profile: %s", profile.name);
477 auto apply_fn = [apply_callback, profile_id = std::string(profile.id)]() {
478 apply_callback("profile:" + profile_id);
479 };
480 AddCommand(name, CommandCategory::kLayout, profile.description, "",
481 apply_fn);
482 AddCommand(absl::StrFormat("layout: profile %s", profile.name),
483 CommandCategory::kLayout, profile.description, "", apply_fn);
484 }
485
486 AddCommand("Capture Layout Snapshot", CommandCategory::kLayout,
487 "Capture current layout as temporary session snapshot", "",
488 [apply_callback]() { apply_callback("session:capture"); });
489 AddCommand("layout: capture snapshot", CommandCategory::kLayout,
490 "Capture current layout as temporary session snapshot", "",
491 [apply_callback]() { apply_callback("session:capture"); });
492 AddCommand("Restore Layout Snapshot", CommandCategory::kLayout,
493 "Restore temporary session snapshot", "",
494 [apply_callback]() { apply_callback("session:restore"); });
495 AddCommand("layout: restore snapshot", CommandCategory::kLayout,
496 "Restore temporary session snapshot", "",
497 [apply_callback]() { apply_callback("session:restore"); });
498 AddCommand("Clear Layout Snapshot", CommandCategory::kLayout,
499 "Clear temporary session snapshot", "",
500 [apply_callback]() { apply_callback("session:clear"); });
501 AddCommand("layout: clear snapshot", CommandCategory::kLayout,
502 "Clear temporary session snapshot", "",
503 [apply_callback]() { apply_callback("session:clear"); });
504
505 // Legacy named workspace presets
506 struct PresetInfo {
507 const char* name;
508 const char* description;
509 };
510
511 static const PresetInfo presets[] = {
512 {"Minimal", "Minimal workspace with essential panels only"},
513 {"Developer", "Debug-focused layout with emulator and memory tools"},
514 {"Designer", "Visual-focused layout for graphics and palette editing"},
515 {"Modder", "Full-featured layout with all panels available"},
516 {"Overworld Expert", "Optimized layout for overworld editing"},
517 {"Dungeon Expert", "Optimized layout for dungeon editing"},
518 {"Testing", "QA-focused layout with testing tools"},
519 {"Audio", "Music and sound editing focused layout"},
520 {"Logic Debugger", "Debug and development focused layout"},
521 {"Overworld Artist", "Visual and overworld focused layout"},
522 {"Dungeon Master", "Comprehensive dungeon editing layout"},
523 {"Audio Engineer", "Music and sound editing layout"},
524 };
525
526 for (const auto& preset : presets) {
527 std::string name = absl::StrFormat("Apply Layout: %s", preset.name);
528 auto apply_fn = [apply_callback, preset_name = std::string(preset.name)]() {
529 apply_callback(preset_name);
530 };
531
532 AddCommand(name, CommandCategory::kLayout, preset.description, "",
533 apply_fn);
534 AddCommand(absl::StrFormat("layout: %s", preset.name),
535 CommandCategory::kLayout, preset.description, "", apply_fn);
536 }
537
538 // Reset to default layout command
539 AddCommand("Reset Layout: Default", CommandCategory::kLayout,
540 "Reset to the default layout for current editor", "",
541 [apply_callback]() { apply_callback("Default"); });
542 AddCommand("layout: Default", CommandCategory::kLayout,
543 "Reset to the default layout for current editor", "",
544 [apply_callback]() { apply_callback("Default"); });
545}
546
547void CommandPalette::RegisterRecentFilesCommands(
548 std::function<void(const std::string&)> open_callback) {
549 const auto& recent_files =
551
552 for (const auto& filepath : recent_files) {
553 // Skip files that no longer exist
554 if (!std::filesystem::exists(filepath)) {
555 continue;
556 }
557
558 // Extract just the filename for display
559 std::filesystem::path path(filepath);
560 std::string filename = path.filename().string();
561
562 std::string name = absl::StrFormat("Open Recent: %s", filename);
563 std::string desc = absl::StrFormat("Open file %s", filepath);
564
565 AddCommand(name, CommandCategory::kFile, desc, "",
566 [open_callback, filepath]() { open_callback(filepath); });
567 }
568}
569
570void CommandPalette::RegisterDungeonRoomCommands(size_t session_id) {
571 constexpr int kTotalRooms = 0x128;
572 for (int room_id = 0; room_id < kTotalRooms; ++room_id) {
573 const std::string label = zelda3::GetRoomLabel(room_id);
574 const std::string room_name =
575 label.empty() ? absl::StrFormat("Room %03X", room_id) : label;
576
577 const std::string name =
578 absl::StrFormat("Dungeon: Open Room [%03X] %s", room_id, room_name);
579 const std::string desc =
580 absl::StrFormat("Jump to dungeon room %03X", room_id);
581
582 AddCommand(
583 name, CommandCategory::kNavigation, desc, "", [room_id, session_id]() {
584 if (auto* bus = ContentRegistry::Context::event_bus()) {
585 bus->Publish(JumpToRoomRequestEvent::Create(room_id, session_id));
586 }
587 });
588 }
589}
590
591void CommandPalette::RegisterWorkflowCommands(
592 WorkspaceWindowManager* window_manager, size_t session_id) {
593 if (window_manager) {
594 const auto categories = window_manager->GetAllCategories(session_id);
595 for (const auto& category : categories) {
596 for (const auto& descriptor :
597 window_manager->GetWindowsInCategory(session_id, category)) {
598 if (descriptor.workflow_group.empty()) {
599 continue;
600 }
601 if (descriptor.enabled_condition && !descriptor.enabled_condition()) {
602 continue;
603 }
604 const std::string label = descriptor.workflow_label.empty()
605 ? descriptor.display_name
606 : descriptor.workflow_label;
607 const std::string group = descriptor.workflow_group.empty()
608 ? std::string("General")
609 : descriptor.workflow_group;
610 const std::string description =
611 descriptor.workflow_description.empty()
612 ? absl::StrFormat("Open %s", descriptor.display_name)
613 : descriptor.workflow_description;
614 AddCommand(
615 absl::StrFormat("%s: %s", group, label), CommandCategory::kWorkflow,
616 description, descriptor.shortcut_hint,
617 [window_manager, session_id, panel_id = descriptor.card_id]() {
618 window_manager->OpenWindow(session_id, panel_id);
619 });
620 }
621 }
622 }
623
624 for (const auto& action : ContentRegistry::WorkflowActions::GetAll()) {
625 if (action.enabled && !action.enabled()) {
626 continue;
627 }
628 const std::string group =
629 action.group.empty() ? std::string("General") : action.group;
630 AddCommand(absl::StrFormat("%s: %s", group, action.label),
631 CommandCategory::kWorkflow, action.description, action.shortcut,
632 action.callback);
633 }
634}
635
636void CommandPalette::RegisterWelcomeCommands(
637 const RecentProjectsModel* model,
638 const std::vector<std::string>& template_names,
639 std::function<void(const std::string&)> remove_callback,
640 std::function<void(const std::string&)> toggle_pin_callback,
641 std::function<void()> undo_remove_callback,
642 std::function<void()> clear_recents_callback,
643 std::function<void(const std::string&)> create_from_template_callback,
644 std::function<void()> dismiss_welcome_callback,
645 std::function<void()> show_welcome_callback) {
646 // Per-entry remove/pin commands. Keeping the palette fully driven by the
647 // same model that powers the welcome screen means this list stays in sync
648 // with pins/renames automatically on the next RefreshCommands().
649 if (model) {
650 for (const auto& entry : model->entries()) {
651 if (entry.unavailable)
652 continue; // Platform-gated; skip silently.
653 const std::string label = entry.display_name_override.empty()
654 ? entry.name
655 : entry.display_name_override;
656 const std::string path = entry.filepath;
657
658 if (remove_callback) {
659 const std::string name =
660 absl::StrFormat("Welcome: Remove Recent \"%s\"", label);
661 const std::string desc =
662 absl::StrFormat("Remove %s from the welcome screen's recents list.",
663 entry.filepath);
664 AddCommand(name, CommandCategory::kFile, desc, "",
665 [remove_callback, path]() { remove_callback(path); });
666 }
667 if (toggle_pin_callback) {
668 const std::string name = absl::StrFormat(
669 "Welcome: %s Recent \"%s\"", entry.pinned ? "Unpin" : "Pin", label);
670 const std::string desc =
671 absl::StrFormat("%s %s on the welcome screen.",
672 entry.pinned ? "Unpin" : "Pin", entry.filepath);
673 AddCommand(
674 name, CommandCategory::kFile, desc, "",
675 [toggle_pin_callback, path]() { toggle_pin_callback(path); });
676 }
677 }
678 }
679
680 if (clear_recents_callback) {
681 AddCommand("Welcome: Clear Recent Files", CommandCategory::kFile,
682 "Forget every entry in the welcome screen's recent list.", "",
683 clear_recents_callback);
684 }
685
686 if (undo_remove_callback) {
687 AddCommand("Welcome: Undo Last Recent Removal", CommandCategory::kFile,
688 "Restore the last recent-project entry removed via Forget.", "",
689 undo_remove_callback);
690 }
691
692 if (create_from_template_callback) {
693 for (const auto& template_name : template_names) {
694 if (template_name.empty())
695 continue;
696 const std::string name = absl::StrFormat(
697 "Welcome: Create Project from Template: %s", template_name);
698 const std::string desc = absl::StrFormat(
699 "Start a new project using the \"%s\" template.", template_name);
700 AddCommand(name, CommandCategory::kFile, desc, "",
701 [create_from_template_callback, template_name]() {
702 create_from_template_callback(template_name);
703 });
704 }
705 }
706
707 if (show_welcome_callback) {
708 AddCommand("Welcome: Show Welcome Screen", CommandCategory::kView,
709 "Bring back the welcome screen if it's been dismissed.", "",
710 show_welcome_callback);
711 }
712 if (dismiss_welcome_callback) {
713 AddCommand("Welcome: Dismiss Welcome Screen", CommandCategory::kView,
714 "Hide the welcome screen for the rest of this session.", "",
715 dismiss_welcome_callback);
716 }
717}
718
719} // namespace editor
720} // namespace yaze
bool is_object() const
Definition json.h:57
static Json parse(const std::string &)
Definition json.h:36
static Json object()
Definition json.h:34
items_view items()
Definition json.h:88
std::string dump(int=-1, char=' ', bool=false, int=0) const
Definition json.h:91
bool contains(const std::string &) const
Definition json.h:53
std::vector< CommandEntry > SearchCommands(const std::string &query)
void SaveHistory(const std::string &filepath)
Save command usage history to disk.
void Clear()
Clear all commands (and forget every registered provider).
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.
void UnregisterProvider(const std::string &provider_id)
void RegisterPanelCommands(WorkspaceWindowManager *window_manager, size_t session_id)
Register all window toggle commands from WorkspaceWindowManager.
void RemoveProviderCommands(const std::string &provider_id)
std::vector< CommandEntry > GetAllCommands() const
Get all registered commands.
void RecordUsage(const std::string &name)
std::unordered_map< std::string, CommandEntry > commands_
void RegisterProvider(std::unique_ptr< CommandProvider > provider)
std::vector< CommandEntry > GetRecentCommands(int limit=10)
void RefreshProviders()
Remove-and-re-run every registered provider.
std::vector< std::unique_ptr< CommandProvider > > providers_
std::vector< CommandEntry > GetFrequentCommands(int limit=10)
static int FuzzyScore(const std::string &text, const std::string &query)
void RefreshProvider(const std::string &provider_id)
Plug-in command source for the palette.
virtual void Provide(CommandPalette *palette)=0
Populate palette with this source's commands.
const std::vector< RecentProject > & entries() const
Central registry for all editor cards with session awareness and dependency injection.
std::vector< WindowDescriptor > GetWindowsInCategory(size_t session_id, const std::string &category) const
const WindowDescriptor * GetWindowDescriptor(size_t session_id, const std::string &base_window_id) const
std::vector< std::string > GetWindowsInSession(size_t session_id) const
void SetWindowPinned(size_t session_id, const std::string &base_window_id, bool pinned)
bool CloseWindow(size_t session_id, const std::string &base_window_id)
std::vector< std::string > GetAllCategories(size_t session_id) const
bool IsWindowPinned(size_t session_id, const std::string &base_window_id) const
bool ToggleWindow(size_t session_id, const std::string &base_window_id)
static RecentFilesManager & GetInstance()
Definition project.h:441
const std::vector< std::string > & GetRecentFiles() const
Definition project.h:478
#define LOG_ERROR(category, format,...)
Definition log.h:110
#define LOG_WARN(category, format,...)
Definition log.h:108
#define LOG_INFO(category, format,...)
Definition log.h:106
absl::Span< const DrawerCatalogEntry > GetDrawerCatalog()
Switchable drawers in header-cycle order (excludes Tool Output).
std::string GetRoomLabel(int id)
Convenience function to get a room label.
static constexpr const char * kPanel
std::function< void()> callback