9#include "absl/strings/str_format.h"
45std::string ToLower(std::string_view text) {
46 std::string out(text);
47 std::transform(out.begin(), out.end(), out.begin(), [](
unsigned char c) {
48 return static_cast<char>(std::tolower(c));
54 return std::isalnum(
static_cast<unsigned char>(c)) != 0;
58 return index == 0 || !
IsAlnum(text[index - 1]);
62 std::vector<std::string> tokens;
65 if (std::isspace(
static_cast<unsigned char>(c))) {
66 if (!current.empty()) {
67 tokens.push_back(current);
75 tokens.push_back(current);
82 const size_t n = text.size();
83 const size_t m = query.size();
86 constexpr int kNone = -1;
88 std::vector<int> prev(n,
kNone);
89 std::vector<int> cur(n,
kNone);
90 for (
size_t j = 0; j < m; ++j) {
91 int best_before =
kNone;
92 for (
size_t i = 0; i < n; ++i) {
94 if (i >= 2 && prev[i - 2] > best_before)
95 best_before = prev[i - 2];
96 if (text[i] != query[j])
106 if (best_before !=
kNone)
107 best = best_before + char_score;
108 if (i >= 1 && prev[i - 1] !=
kNone) {
113 std::swap(prev, cur);
117 result = std::max(result, v);
121int ScoreSingle(
const std::string& text,
const std::string& query) {
122 if (query.empty() || text.empty())
126 size_t pos = text.find(query);
129 if (pos != std::string::npos) {
130 while (pos != std::string::npos) {
133 pos = text.find(query, pos + 1);
145 const std::string lower = ToLower(name);
146 for (
size_t pos = lower.find(
"panel"); pos != std::string::npos;
147 pos = lower.find(
"panel", pos + 1)) {
148 const size_t end = pos + 5;
149 const bool starts = pos == 0 || !
IsAlnum(lower[pos - 1]);
150 const bool ends = end == lower.size() || !
IsAlnum(lower[end]) ||
151 (lower[end] ==
's' &&
152 (end + 1 == lower.size() || !
IsAlnum(lower[end + 1])));
160 int boost = std::min(usage_count, 20) * 3;
161 if (last_used_ms > 0) {
162 const int64_t age_ms = now_ms - last_used_ms;
163 if (age_ms < 60LL * 1000) {
165 }
else if (age_ms < 60LL * 60 * 1000) {
167 }
else if (age_ms < 24LL * 60 * 60 * 1000) {
177 return std::chrono::duration_cast<std::chrono::milliseconds>(
178 std::chrono::system_clock::now().time_since_epoch())
183 const std::string& category,
184 const std::string& description,
185 const std::string& shortcut,
186 std::function<
void()> callback) {
192 entry.
callback = std::move(callback);
210 std::unique_ptr<CommandProvider> provider) {
213 const std::string
id = provider->ProviderId();
216 "Provider with empty id refused; skipping registration");
222 if ((*it)->ProviderId() == id) {
223 LOG_WARN(
"CommandPalette",
"Replacing existing CommandProvider '%s'",
241 [&](
const std::unique_ptr<CommandProvider>& p) {
242 return p && p->ProviderId() == provider_id;
249 if (provider && provider->ProviderId() == provider_id) {
252 provider->Provide(
this);
261 std::vector<std::string> ids;
265 ids.push_back(provider->ProviderId());
267 for (
const auto&
id : ids) {
273 if (provider_id.empty())
277 if (it->second.provider_id == provider_id) {
287 if (canonical.empty())
289 const int64_t now =
NowMs();
290 auto& stats =
usage_[canonical];
293 for (
const auto& [other_name, entry] :
commands_) {
294 if (other_name != canonical &&
296 stats.count = std::max(stats.count, entry.usage_count);
300 stats.last_used_ms = now;
303 it->second.usage_count = stats.count;
304 it->second.last_used_ms = stats.last_used_ms;
310 const std::string& query) {
315 std::string_view query) {
316 const std::string query_lower = ToLower(query);
317 const std::string text_lower = ToLower(text);
318 const int whole = ScoreSingle(text_lower, query_lower);
324 const auto tokens = SplitWhitespace(query_lower);
325 if (tokens.size() < 2)
328 for (
const auto& token : tokens) {
329 const int token_score = ScoreSingle(text_lower, token);
330 if (token_score <= 0)
332 total += token_score;
334 const int average = total /
static_cast<int>(tokens.size());
335 return std::clamp(average / 2, 1, kScoreSubsequenceMax);
339 std::string_view query,
350 std::string_view name) {
351 std::string lower = ToLower(name);
353 for (
size_t pos = lower.find(
"(alt)"); pos != std::string::npos;
354 pos = lower.find(
"(alt)")) {
357 std::vector<std::string> words;
359 for (
char c : lower) {
361 current.push_back(c);
362 }
else if (!current.empty()) {
363 words.push_back(std::move(current));
367 if (!current.empty())
368 words.push_back(std::move(current));
370 for (
auto& word : words) {
374 else if (word ==
"panels")
386 for (
char c : name) {
387 if (std::isspace(
static_cast<unsigned char>(c)))
390 return name.find(
'.') != std::string_view::npos ||
391 name.find(
'_') != std::string_view::npos ||
392 std::islower(
static_cast<unsigned char>(name.front())) != 0;
396 std::vector<const CommandEntry*> sorted;
398 for (
const auto& [name, entry] :
commands_) {
401 sorted.push_back(&entry);
403 std::sort(sorted.begin(), sorted.end(),
405 return a->name < b->name;
413 if (entry.provider_id == kShortcutsProviderId)
415 if (HasPanelWord(entry.name))
423 std::string live_shortcut;
425 std::string other_shortcut;
426 std::function<void()> fallback_callback;
428 int64_t last_used_ms = 0;
430 std::vector<std::string> order;
431 std::unordered_map<std::string, Group> groups;
434 auto [it, inserted] = groups.try_emplace(key);
436 order.push_back(key);
437 Group& group = it->second;
438 const bool is_shortcut = entry->provider_id == kShortcutsProviderId;
439 const int rank = rank_of(*entry);
440 if (!group.rep || rank < group.rep_rank) {
442 group.rep_rank = rank;
444 if (!entry->shortcut.empty()) {
446 if (group.live_shortcut.empty() || rank < group.live_rank) {
447 group.live_shortcut = entry->shortcut;
448 group.live_rank = rank;
450 }
else if (group.other_shortcut.empty()) {
451 group.other_shortcut = entry->shortcut;
454 if (!group.fallback_callback && entry->callback)
455 group.fallback_callback = entry->callback;
456 group.usage_count = std::max(group.usage_count, entry->usage_count);
457 group.last_used_ms = std::max(group.last_used_ms, entry->last_used_ms);
460 std::vector<CommandEntry> result;
461 result.reserve(order.size());
462 for (
const auto& key : order) {
463 const Group& group = groups[key];
465 if (!group.live_shortcut.empty()) {
466 entry.
shortcut = group.live_shortcut;
467 }
else if (entry.
shortcut.empty()) {
468 entry.
shortcut = group.other_shortcut;
471 entry.
callback = group.fallback_callback;
474 result.push_back(std::move(entry));
480 const std::string& name)
const {
492 int64_t now_ms)
const {
493 std::vector<CommandMatch> matches;
494 const bool empty_query = SplitWhitespace(query).empty();
498 score = 1 + FrecencyBoost(entry.usage_count, entry.last_used_ms, now_ms);
503 matches.push_back({std::move(entry), score});
507 std::stable_sort(matches.begin(), matches.end(),
509 return a.score > b.score;
515 const std::string& query)
const {
520 const std::string& query) {
521 std::vector<CommandEntry> results;
522 for (
auto& match :
Search(query)) {
523 results.push_back(std::move(match.entry));
529 std::vector<CommandEntry> recent;
531 if (entry.usage_count > 0)
532 recent.push_back(std::move(entry));
534 std::stable_sort(recent.begin(), recent.end(),
536 return a.last_used_ms > b.last_used_ms;
538 if (recent.size() >
static_cast<size_t>(limit))
539 recent.resize(limit);
544 std::vector<CommandEntry> frequent;
546 if (entry.usage_count > 0)
547 frequent.push_back(std::move(entry));
549 std::stable_sort(frequent.begin(), frequent.end(),
551 return a.usage_count > b.usage_count;
553 if (frequent.size() >
static_cast<size_t>(limit))
554 frequent.resize(limit);
564 for (
const auto& [name, stats] :
usage_) {
565 if (stats.count > 0) {
567 cmd[
"usage_count"] = stats.count;
568 cmd[
"last_used_ms"] = stats.last_used_ms;
569 j[
"commands"][name] = cmd;
573 std::ofstream file(filepath);
574 if (file.is_open()) {
576 LOG_DEBUG(
"CommandPalette",
"Saved command history to %s",
579 }
catch (
const std::exception& e) {
580 LOG_ERROR(
"CommandPalette",
"Failed to save command history: %s", e.what());
585 if (!std::filesystem::exists(filepath)) {
590 std::ifstream file(filepath);
591 if (!file.is_open()) {
595 std::string content((std::istreambuf_iterator<char>(file)),
596 std::istreambuf_iterator<char>());
604 for (
auto& [name, cmd_json] : j[
"commands"].
items()) {
606 stats.
count = cmd_json.value(
"usage_count", 0);
607 stats.
last_used_ms = cmd_json.value(
"last_used_ms", int64_t{0});
608 if (stats.
count <= 0)
613 it->second.usage_count = stats.
count;
619 LOG_INFO(
"CommandPalette",
"Loaded %d command history entries from %s",
620 loaded, filepath.c_str());
621 }
catch (
const std::exception& e) {
622 LOG_ERROR(
"CommandPalette",
"Failed to load command history: %s", e.what());
627 std::vector<CommandEntry> result;
629 for (
const auto& [name, entry] :
commands_) {
630 result.push_back(entry);
641 const auto* descriptor =
648 std::string show_name =
649 absl::StrFormat(
"Show: %s", descriptor->display_name);
650 std::string show_desc =
651 absl::StrFormat(
"Open the %s window", descriptor->display_name);
654 descriptor->shortcut_hint,
655 [window_manager, base_id, session_id]() {
656 window_manager->OpenWindow(session_id, base_id);
660 std::string hide_name =
661 absl::StrFormat(
"Hide: %s", descriptor->display_name);
662 std::string hide_desc =
663 absl::StrFormat(
"Close the %s window", descriptor->display_name);
666 [window_manager, base_id, session_id]() {
671 std::string toggle_name =
672 absl::StrFormat(
"Toggle: %s", descriptor->display_name);
673 std::string toggle_desc = absl::StrFormat(
"Toggle the %s window visibility",
674 descriptor->display_name);
676 auto toggle_fn = [window_manager, base_id, session_id]() {
685 std::string window_name =
686 absl::StrFormat(
"window: %s", descriptor->display_name);
689 descriptor->shortcut_hint, [window_manager, base_id, session_id]() {
690 WindowHost host(window_manager);
691 const bool opened = ImGui::GetCurrentContext() != nullptr
692 ? host.OpenAndFocusWindow(session_id, base_id)
693 : host.OpenWindow(session_id, base_id);
695 window_manager->MarkWindowRecentlyUsed(base_id);
702 std::string pin_toggle_name =
703 absl::StrFormat(
"Toggle Pin: %s", descriptor->display_name);
704 std::string pin_toggle_desc =
705 absl::StrFormat(
"Keep the %s window visible across editor switches",
706 descriptor->display_name);
709 [window_manager, base_id, session_id]() {
717void CommandPalette::RegisterDrawerCommands(
718 std::function<
void(
int drawer_type)> toggle_callback,
719 std::function<
void()> cycle_next, std::function<
void()> cycle_prev) {
720 if (!toggle_callback)
727 const int type_as_int =
static_cast<int>(entry.type);
728 std::string name = absl::StrFormat(
"drawer: %s", entry.name);
730 absl::StrFormat(
"Toggle the %s right drawer", entry.name);
733 name, CommandCategory::kDrawer, desc,
"",
734 [toggle_callback, type_as_int]() { toggle_callback(type_as_int); });
738 AddCommand(
"drawer: Next", CommandCategory::kDrawer,
739 "Cycle to the next right drawer",
"", std::move(cycle_next));
742 AddCommand(
"drawer: Previous", CommandCategory::kDrawer,
743 "Cycle to the previous right drawer",
"", std::move(cycle_prev));
747void CommandPalette::RegisterEditorCommands(
748 std::function<
void(
const std::string&)> switch_callback) {
750 auto categories = EditorRegistry::GetAllEditorCategories();
752 for (
const auto& category : categories) {
753 std::string name = absl::StrFormat(
"Switch to: %s Editor", category);
755 absl::StrFormat(
"Switch to the %s editor category", category);
757 AddCommand(name, CommandCategory::kEditor, desc,
"",
758 [switch_callback, category]() { switch_callback(category); });
762void CommandPalette::RegisterLayoutCommands(
763 std::function<
void(
const std::string&)> apply_callback) {
767 const char* description;
770 static const ProfileInfo profiles[] = {
771 {
"code",
"Code",
"Focused editing workspace with minimal panel noise"},
773 "Debugger-first workspace for tracing and memory tools"},
774 {
"mapping",
"Mapping",
775 "Map-centric workspace for overworld/dungeon flows"},
776 {
"chat",
"Chat + Agent",
777 "Agent collaboration workspace with chat-centric layout"},
780 for (
const auto& profile : profiles) {
781 std::string name = absl::StrFormat(
"Apply Profile: %s", profile.name);
782 auto apply_fn = [apply_callback, profile_id = std::string(profile.id)]() {
783 apply_callback(
"profile:" + profile_id);
785 AddCommand(name, CommandCategory::kLayout, profile.description,
"",
787 AddCommand(absl::StrFormat(
"layout: profile %s", profile.name),
788 CommandCategory::kLayout, profile.description,
"", apply_fn);
791 AddCommand(
"Capture Layout Snapshot", CommandCategory::kLayout,
792 "Capture current layout as temporary session snapshot",
"",
793 [apply_callback]() { apply_callback(
"session:capture"); });
794 AddCommand(
"layout: capture snapshot", CommandCategory::kLayout,
795 "Capture current layout as temporary session snapshot",
"",
796 [apply_callback]() { apply_callback(
"session:capture"); });
797 AddCommand(
"Restore Layout Snapshot", CommandCategory::kLayout,
798 "Restore temporary session snapshot",
"",
799 [apply_callback]() { apply_callback(
"session:restore"); });
800 AddCommand(
"layout: restore snapshot", CommandCategory::kLayout,
801 "Restore temporary session snapshot",
"",
802 [apply_callback]() { apply_callback(
"session:restore"); });
803 AddCommand(
"Clear Layout Snapshot", CommandCategory::kLayout,
804 "Clear temporary session snapshot",
"",
805 [apply_callback]() { apply_callback(
"session:clear"); });
806 AddCommand(
"layout: clear snapshot", CommandCategory::kLayout,
807 "Clear temporary session snapshot",
"",
808 [apply_callback]() { apply_callback(
"session:clear"); });
813 const char* description;
816 static const PresetInfo presets[] = {
817 {
"Minimal",
"Minimal workspace with essential panels only"},
818 {
"Developer",
"Debug-focused layout with emulator and memory tools"},
819 {
"Designer",
"Visual-focused layout for graphics and palette editing"},
820 {
"Modder",
"Full-featured layout with all panels available"},
821 {
"Overworld Expert",
"Optimized layout for overworld editing"},
822 {
"Dungeon Expert",
"Optimized layout for dungeon editing"},
823 {
"Testing",
"QA-focused layout with testing tools"},
824 {
"Audio",
"Music and sound editing focused layout"},
825 {
"Logic Debugger",
"Debug and development focused layout"},
826 {
"Overworld Artist",
"Visual and overworld focused layout"},
827 {
"Dungeon Master",
"Comprehensive dungeon editing layout"},
828 {
"Audio Engineer",
"Music and sound editing layout"},
831 for (
const auto& preset : presets) {
832 std::string name = absl::StrFormat(
"Apply Layout: %s", preset.name);
833 auto apply_fn = [apply_callback, preset_name = std::string(preset.name)]() {
834 apply_callback(preset_name);
837 AddCommand(name, CommandCategory::kLayout, preset.description,
"",
839 AddCommand(absl::StrFormat(
"layout: %s", preset.name),
840 CommandCategory::kLayout, preset.description,
"", apply_fn);
844 AddCommand(
"Reset Layout: Default", CommandCategory::kLayout,
845 "Reset to the default layout for current editor",
"",
846 [apply_callback]() { apply_callback(
"Default"); });
847 AddCommand(
"layout: Default", CommandCategory::kLayout,
848 "Reset to the default layout for current editor",
"",
849 [apply_callback]() { apply_callback(
"Default"); });
852void CommandPalette::RegisterRecentFilesCommands(
853 std::function<
void(
const std::string&)> open_callback) {
854 const auto& recent_files =
857 for (
const auto& filepath : recent_files) {
859 if (!std::filesystem::exists(filepath)) {
864 std::filesystem::path path(filepath);
865 std::string filename = path.filename().string();
867 std::string name = absl::StrFormat(
"Open Recent: %s", filename);
868 std::string desc = absl::StrFormat(
"Open file %s", filepath);
870 AddCommand(name, CommandCategory::kFile, desc,
"",
871 [open_callback, filepath]() { open_callback(filepath); });
875void CommandPalette::RegisterOverworldMapCommands(
size_t session_id) {
878 const std::string map_name =
879 label.empty() ? absl::StrFormat(
"Map %02X", map_id) : label;
880 const std::string name =
881 absl::StrFormat(
"Overworld: Open Map [%02X] %s", map_id, map_name);
882 const std::string desc =
883 absl::StrFormat(
"Jump to overworld map %02X", map_id);
885 name, CommandCategory::kNavigation, desc,
"", [map_id, session_id]() {
886 if (
auto* bus = ContentRegistry::Context::event_bus()) {
887 bus->Publish(JumpToMapRequestEvent::Create(map_id, session_id));
893void CommandPalette::RegisterDungeonRoomCommands(
size_t session_id) {
894 constexpr int kTotalRooms = 0x128;
895 for (
int room_id = 0; room_id < kTotalRooms; ++room_id) {
897 const std::string room_name =
898 label.empty() ? absl::StrFormat(
"Room %03X", room_id) : label;
900 const std::string name =
901 absl::StrFormat(
"Dungeon: Open Room [%03X] %s", room_id, room_name);
902 const std::string desc =
903 absl::StrFormat(
"Jump to dungeon room %03X", room_id);
906 name, CommandCategory::kNavigation, desc,
"", [room_id, session_id]() {
907 if (
auto* bus = ContentRegistry::Context::event_bus()) {
908 bus->Publish(JumpToRoomRequestEvent::Create(room_id, session_id));
914void CommandPalette::RegisterWorkflowCommands(
916 if (window_manager) {
918 for (
const auto& category : categories) {
919 for (
const auto& descriptor :
921 if (descriptor.workflow_group.empty()) {
924 if (descriptor.enabled_condition && !descriptor.enabled_condition()) {
927 const std::string label = descriptor.workflow_label.empty()
928 ? descriptor.display_name
929 : descriptor.workflow_label;
930 const std::string group = descriptor.workflow_group.empty()
931 ? std::string(
"General")
932 : descriptor.workflow_group;
933 const std::string description =
934 descriptor.workflow_description.empty()
935 ? absl::StrFormat(
"Open %s", descriptor.display_name)
936 : descriptor.workflow_description;
938 absl::StrFormat(
"%s: %s", group, label), CommandCategory::kWorkflow,
939 description, descriptor.shortcut_hint,
940 [window_manager, session_id, panel_id = descriptor.card_id]() {
941 window_manager->OpenWindow(session_id, panel_id);
947 for (
const auto& action : ContentRegistry::WorkflowActions::GetAll()) {
948 if (action.enabled && !action.enabled()) {
951 const std::string group =
952 action.group.empty() ? std::string(
"General") : action.group;
953 AddCommand(absl::StrFormat(
"%s: %s", group, action.label),
954 CommandCategory::kWorkflow, action.description, action.shortcut,
959void CommandPalette::RegisterWelcomeCommands(
961 const std::vector<std::string>& template_names,
962 std::function<
void(
const std::string&)> remove_callback,
963 std::function<
void(
const std::string&)> toggle_pin_callback,
964 std::function<
void()> undo_remove_callback,
965 std::function<
void()> clear_recents_callback,
966 std::function<
void(
const std::string&)> create_from_template_callback,
967 std::function<
void()> dismiss_welcome_callback,
968 std::function<
void()> show_welcome_callback) {
973 for (
const auto& entry : model->
entries()) {
974 if (entry.unavailable)
976 const std::string label = entry.display_name_override.empty()
978 : entry.display_name_override;
979 const std::string path = entry.filepath;
981 if (remove_callback) {
982 const std::string name =
983 absl::StrFormat(
"Welcome: Remove Recent \"%s\"", label);
984 const std::string desc =
985 absl::StrFormat(
"Remove %s from the welcome screen's recents list.",
987 AddCommand(name, CommandCategory::kFile, desc,
"",
988 [remove_callback, path]() { remove_callback(path); });
990 if (toggle_pin_callback) {
991 const std::string name = absl::StrFormat(
992 "Welcome: %s Recent \"%s\"", entry.pinned ?
"Unpin" :
"Pin", label);
993 const std::string desc =
994 absl::StrFormat(
"%s %s on the welcome screen.",
995 entry.pinned ?
"Unpin" :
"Pin", entry.filepath);
997 name, CommandCategory::kFile, desc,
"",
998 [toggle_pin_callback, path]() { toggle_pin_callback(path); });
1003 if (clear_recents_callback) {
1004 AddCommand(
"Welcome: Clear Recent Files", CommandCategory::kFile,
1005 "Forget every entry in the welcome screen's recent list.",
"",
1006 clear_recents_callback);
1009 if (undo_remove_callback) {
1010 AddCommand(
"Welcome: Undo Last Recent Removal", CommandCategory::kFile,
1011 "Restore the last recent-project entry removed via Forget.",
"",
1012 undo_remove_callback);
1015 if (create_from_template_callback) {
1016 for (
const auto& template_name : template_names) {
1017 if (template_name.empty())
1019 const std::string name = absl::StrFormat(
1020 "Welcome: Create Project from Template: %s", template_name);
1021 const std::string desc = absl::StrFormat(
1022 "Start a new project using the \"%s\" template.", template_name);
1023 AddCommand(name, CommandCategory::kFile, desc,
"",
1024 [create_from_template_callback, template_name]() {
1025 create_from_template_callback(template_name);
1030 if (show_welcome_callback) {
1031 AddCommand(
"Welcome: Show Welcome Screen", CommandCategory::kView,
1032 "Bring back the welcome screen if it's been dismissed.",
"",
1033 show_welcome_callback);
1035 if (dismiss_welcome_callback) {
1036 AddCommand(
"Welcome: Dismiss Welcome Screen", CommandCategory::kView,
1037 "Hide the welcome screen for the rest of this session.",
"",
1038 dismiss_welcome_callback);
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 SaveHistory(const std::string &filepath)
Save command usage history to disk.
std::string CanonicalCommandName(const std::string &name) const
void Clear()
Clear all commands (and forget every registered provider). Usage history is kept so refreshing provid...
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 (raw, not deduplicated)
static std::string NormalizeCommandName(std::string_view name)
std::unordered_map< std::string, CommandEntry > commands_
void RegisterProvider(std::unique_ptr< CommandProvider > provider)
static int ScoreEntry(const CommandEntry &entry, std::string_view query, int64_t now_ms)
static int ScoreText(std::string_view text, std::string_view query)
std::vector< CommandEntry > GetRecentCommands(int limit=10)
std::vector< CommandEntry > GetVisibleCommands() const
bool RecordUsage(const std::string &name)
void RefreshProviders()
Remove-and-re-run every registered provider.
static bool IsInternalCommandId(std::string_view name)
std::string current_provider_id_
std::vector< std::unique_ptr< CommandProvider > > providers_
std::vector< CommandMatch > Search(const std::string &query, int64_t now_ms) const
std::unordered_map< std::string, UsageStats > usage_
Usage keyed by command name; survives Clear() and provider refreshes.
std::vector< CommandEntry > GetFrequentCommands(int limit=10)
static int FuzzyScore(const std::string &text, const std::string &query)
Legacy name for ScoreText (kept for the Window Finder).
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()
const std::vector< std::string > & GetRecentFiles() const
#define LOG_DEBUG(category, format,...)
#define LOG_ERROR(category, format,...)
#define LOG_WARN(category, format,...)
#define LOG_INFO(category, format,...)
constexpr int kScoreSubstring
bool IsWordStartAt(const std::string &text, size_t index)
bool HasPanelWord(std::string_view name)
constexpr int kScoreWordStartSubstring
constexpr int kCharConsecutiveBonus
constexpr int kScorePrefix
constexpr int kCharWordStartBonus
int BestSubsequenceScore(const std::string &text, const std::string &query)
constexpr int kScoreSubsequenceMax
std::vector< std::string > SplitWhitespace(const std::string &text)
constexpr const char * kShortcutsProviderId
constexpr int kScoreExact
int ScoreSingle(const std::string &text, const std::string &query)
int FrecencyBoost(int usage_count, int64_t last_used_ms, int64_t now_ms)
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.
constexpr int kNumOverworldMaps
std::string GetOverworldMapLabel(int id)
Convenience function to get an overworld map label.
static constexpr const char * kPanel
std::function< void()> callback
A search hit: the (deduplicated) entry plus the score it matched with.