47 std::string status_msg;
55 std::function<absl::Status()> exec;
59 auto state = std::make_shared<PaletteState>();
61 static std::vector<Cmd> cmds = {
62 {
"hex-read",
"🔢 Hex",
"Read ROM bytes",
63 "--address=0x1C800 --length=16 --format=both",
64 []() {
return absl::OkStatus(); }},
66 {
"hex-write",
"🔢 Hex",
"Write ROM bytes",
67 "--address=0x1C800 --data=\"FF 00\"",
68 []() {
return absl::OkStatus(); }},
70 {
"hex-search",
"🔢 Hex",
"Search byte pattern",
71 "--pattern=\"FF 00 ?? 12\"",
72 []() {
return absl::OkStatus(); }},
74 {
"palette-get",
"🎨 Palette",
"Get palette colors",
75 "--group=0 --palette=0 --format=hex",
76 []() {
return absl::OkStatus(); }},
78 {
"palette-set",
"🎨 Palette",
"Set palette color",
79 "--group=0 --palette=0 --index=5 --color=FF0000",
80 []() {
return absl::OkStatus(); }},
82 {
"palette-analyze",
"🎨 Palette",
"Analyze palette",
83 "--type=palette --id=0/0",
84 []() {
return absl::OkStatus(); }},
87 auto search_input = Input(&state->query,
"Search commands...");
89 auto menu = Renderer([state] {
90 std::vector<int> filtered_idx;
91 if (state->query.empty()) {
92 for (size_t i = 0; i < cmds.size(); ++i) {
93 filtered_idx.push_back(i);
96 for (
size_t i = 0; i < cmds.size(); ++i) {
97 cmds[i].score = fuzzy_match(state->query, cmds[i].name);
98 if (cmds[i].score > 0) {
99 filtered_idx.push_back(i);
102 std::sort(filtered_idx.begin(), filtered_idx.end(), [](
int a,
int b) {
103 return cmds[a].score > cmds[b].score;
107 for (
size_t i = 0; i < filtered_idx.size(); ++i) {
108 int idx = filtered_idx[i];
110 text(cmds[idx].cat) | color(Color::GrayLight),
112 text(cmds[idx].name) | bold,
114 if (
static_cast<int>(i) == state->selected) {
115 item = item | inverted | focus;
117 items.push_back(item);
119 return vbox(items) | vscroll_indicator | frame;
122 auto execute_command = [state] {
123 std::vector<int> filtered_idx;
124 if (state->query.empty()) {
125 for (
size_t i = 0; i < cmds.size(); ++i) {
126 filtered_idx.push_back(i);
129 for (
size_t i = 0; i < cmds.size(); ++i) {
130 cmds[i].score = fuzzy_match(state->query, cmds[i].name);
131 if (cmds[i].score > 0) {
132 filtered_idx.push_back(i);
135 std::sort(filtered_idx.begin(), filtered_idx.end(), [](
int a,
int b) {
136 return cmds[a].score > cmds[b].score;
140 if (state->selected <
static_cast<int>(filtered_idx.size())) {
141 int cmd_idx = filtered_idx[state->selected];
142 auto status = cmds[cmd_idx].exec();
143 state->status_msg = status.ok() ?
"✓ Success: Command executed." :
"✗ Error: " + std::string(status.message());
147 auto back_btn = Button(
"Back", [] {
149 ScreenInteractive::Active()->ExitLoopClosure()();
152 auto container = Container::Vertical({search_input, menu, back_btn});
154 return Renderer(container, [container, search_input, menu, back_btn, state] {
155 std::vector<int> filtered_idx;
156 if (state->query.empty()) {
157 for (size_t i = 0; i < cmds.size(); ++i) {
158 filtered_idx.push_back(i);
161 for (
size_t i = 0; i < cmds.size(); ++i) {
162 cmds[i].score =
fuzzy_match(state->query, cmds[i].name);
163 if (cmds[i].score > 0) {
164 filtered_idx.push_back(i);
167 std::sort(filtered_idx.begin(), filtered_idx.end(), [](
int a,
int b) {
168 return cmds[a].score > cmds[b].score;
172 Element details = text(
"Select a command to see details.") | dim;
173 if (state->selected <
static_cast<int>(filtered_idx.size())) {
174 int idx = filtered_idx[state->selected];
176 text(cmds[idx].desc) | bold,
178 text(
"Usage: " + cmds[idx].name +
" " + cmds[idx].usage) | color(Color::Cyan),
183 text(
"⚡ Command Palette") | bold | center | color(Color::Cyan),
184 text(app_context.rom.is_loaded() ?
"ROM: " + app_context.rom.title() :
"No ROM loaded") | center | dim,
186 hbox({text(
"🔍 "), search_input->Render() | flex}),
189 menu->Render() | flex,
194 hbox({ back_btn->Render() }) | center,
196 text(state->status_msg) | center | (state->status_msg.find(
"✓") != 0 ? color(Color::Green) : color(Color::Red)),
197 text(
"↑↓: Navigate | Enter: Execute | Esc: Back") | center | dim,
199 }) | CatchEvent([state, execute_command](
const Event& e) {
200 if (e == Event::Return) {
204 if (e == Event::ArrowUp) {
205 if (state->selected > 0) state->selected--;
208 if (e == Event::ArrowDown) {
210 std::vector<int> filtered_idx;
211 if (state->query.empty()) {
212 for (
size_t i = 0; i < cmds.size(); ++i) {
213 filtered_idx.push_back(i);
216 for (
size_t i = 0; i < cmds.size(); ++i) {
217 cmds[i].score =
fuzzy_match(state->query, cmds[i].name);
218 if (cmds[i].score > 0) {
219 filtered_idx.push_back(i);
223 if (state->selected <
static_cast<int>(filtered_idx.size()) - 1) state->selected++;