- Updated include paths for various GUI-related headers to improve organization and clarity. - Introduced new components for better modularity, including PaletteEditorWidget and EditorCardManager. - Refactored existing code to utilize the new components, ensuring consistency across the GUI subsystem. Benefits: - Enhances maintainability and readability of the GUI code. - Facilitates future enhancements and optimizations within the GUI subsystem.
610 lines
19 KiB
C++
610 lines
19 KiB
C++
#include "app/editor/system/proposal_drawer.h"
|
|
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
|
|
#include "absl/strings/str_format.h"
|
|
#include "absl/time/time.h"
|
|
#include "imgui/imgui.h"
|
|
#include "app/gui/core/icons.h"
|
|
#include "cli/service/rom/rom_sandbox_manager.h"
|
|
|
|
// Policy evaluation support (optional, only in main yaze build)
|
|
#ifdef YAZE_ENABLE_POLICY_FRAMEWORK
|
|
#include "cli/service/planning/policy_evaluator.h"
|
|
#endif
|
|
|
|
namespace yaze {
|
|
namespace editor {
|
|
|
|
ProposalDrawer::ProposalDrawer() {
|
|
RefreshProposals();
|
|
}
|
|
|
|
void ProposalDrawer::Draw() {
|
|
if (!visible_) return;
|
|
|
|
// Set drawer position on the right side
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
ImGui::SetNextWindowPos(ImVec2(io.DisplaySize.x - drawer_width_, 0),
|
|
ImGuiCond_Always);
|
|
ImGui::SetNextWindowSize(ImVec2(drawer_width_, io.DisplaySize.y),
|
|
ImGuiCond_Always);
|
|
|
|
ImGuiWindowFlags flags = ImGuiWindowFlags_NoMove |
|
|
ImGuiWindowFlags_NoResize |
|
|
ImGuiWindowFlags_NoCollapse;
|
|
|
|
if (ImGui::Begin("Agent Proposals", &visible_, flags)) {
|
|
if (needs_refresh_) {
|
|
RefreshProposals();
|
|
needs_refresh_ = false;
|
|
if (!selected_proposal_id_.empty() && !selected_proposal_) {
|
|
SelectProposal(selected_proposal_id_);
|
|
}
|
|
}
|
|
|
|
// Header with refresh button
|
|
if (ImGui::Button(ICON_MD_REFRESH " Refresh")) {
|
|
RefreshProposals();
|
|
}
|
|
ImGui::SameLine();
|
|
DrawStatusFilter();
|
|
|
|
ImGui::Separator();
|
|
|
|
// Split view: proposal list on top, details on bottom
|
|
float list_height = ImGui::GetContentRegionAvail().y * 0.4f;
|
|
|
|
ImGui::BeginChild("ProposalList", ImVec2(0, list_height), true);
|
|
DrawProposalList();
|
|
ImGui::EndChild();
|
|
|
|
if (selected_proposal_) {
|
|
ImGui::Separator();
|
|
ImGui::BeginChild("ProposalDetail", ImVec2(0, 0), true);
|
|
DrawProposalDetail();
|
|
ImGui::EndChild();
|
|
}
|
|
}
|
|
ImGui::End();
|
|
|
|
// Confirmation dialog
|
|
if (show_confirm_dialog_) {
|
|
ImGui::OpenPopup("Confirm Action");
|
|
show_confirm_dialog_ = false;
|
|
}
|
|
|
|
if (ImGui::BeginPopupModal("Confirm Action", nullptr,
|
|
ImGuiWindowFlags_AlwaysAutoResize)) {
|
|
ImGui::Text("Are you sure you want to %s this proposal?",
|
|
confirm_action_.c_str());
|
|
ImGui::Separator();
|
|
|
|
if (ImGui::Button("Yes", ImVec2(120, 0))) {
|
|
if (confirm_action_ == "accept") {
|
|
AcceptProposal(confirm_proposal_id_);
|
|
} else if (confirm_action_ == "reject") {
|
|
RejectProposal(confirm_proposal_id_);
|
|
} else if (confirm_action_ == "delete") {
|
|
DeleteProposal(confirm_proposal_id_);
|
|
}
|
|
ImGui::CloseCurrentPopup();
|
|
RefreshProposals();
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("No", ImVec2(120, 0))) {
|
|
ImGui::CloseCurrentPopup();
|
|
}
|
|
ImGui::EndPopup();
|
|
}
|
|
|
|
#ifdef YAZE_ENABLE_POLICY_FRAMEWORK
|
|
// Policy override dialog (NEW)
|
|
if (show_override_dialog_) {
|
|
ImGui::OpenPopup("Override Policy");
|
|
show_override_dialog_ = false;
|
|
}
|
|
|
|
if (ImGui::BeginPopupModal("Override Policy", nullptr,
|
|
ImGuiWindowFlags_AlwaysAutoResize)) {
|
|
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f),
|
|
ICON_MD_WARNING " Policy Override Required");
|
|
ImGui::Separator();
|
|
ImGui::TextWrapped("This proposal has policy warnings.");
|
|
ImGui::TextWrapped("Do you want to override and accept anyway?");
|
|
ImGui::Spacing();
|
|
ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.0f, 1.0f),
|
|
"Note: This action will be logged.");
|
|
ImGui::Separator();
|
|
|
|
if (ImGui::Button("Override and Accept", ImVec2(150, 0))) {
|
|
confirm_action_ = "accept";
|
|
show_confirm_dialog_ = true;
|
|
ImGui::CloseCurrentPopup();
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Cancel", ImVec2(150, 0))) {
|
|
ImGui::CloseCurrentPopup();
|
|
}
|
|
ImGui::EndPopup();
|
|
}
|
|
#endif // YAZE_ENABLE_POLICY_FRAMEWORK
|
|
|
|
}
|
|
|
|
void ProposalDrawer::DrawProposalList() {
|
|
if (proposals_.empty()) {
|
|
ImGui::TextWrapped("No proposals found.");
|
|
ImGui::TextWrapped("Run CLI command: z3ed agent run --prompt \"...\"");
|
|
return;
|
|
}
|
|
|
|
ImGuiTableFlags flags = ImGuiTableFlags_Borders |
|
|
ImGuiTableFlags_RowBg |
|
|
ImGuiTableFlags_ScrollY;
|
|
|
|
if (ImGui::BeginTable("ProposalsTable", 3, flags)) {
|
|
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 60.0f);
|
|
ImGui::TableSetupColumn("Status", ImGuiTableColumnFlags_WidthFixed, 80.0f);
|
|
ImGui::TableSetupColumn("Prompt", ImGuiTableColumnFlags_WidthStretch);
|
|
ImGui::TableSetupScrollFreeze(0, 1);
|
|
ImGui::TableHeadersRow();
|
|
|
|
for (const auto& proposal : proposals_) {
|
|
ImGui::TableNextRow();
|
|
|
|
// ID column
|
|
ImGui::TableSetColumnIndex(0);
|
|
bool is_selected = (proposal.id == selected_proposal_id_);
|
|
if (ImGui::Selectable(proposal.id.c_str(), is_selected,
|
|
ImGuiSelectableFlags_SpanAllColumns)) {
|
|
SelectProposal(proposal.id);
|
|
}
|
|
|
|
// Status column
|
|
ImGui::TableSetColumnIndex(1);
|
|
switch (proposal.status) {
|
|
case cli::ProposalRegistry::ProposalStatus::kPending:
|
|
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.0f, 1.0f), "Pending");
|
|
break;
|
|
case cli::ProposalRegistry::ProposalStatus::kAccepted:
|
|
ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "Accepted");
|
|
break;
|
|
case cli::ProposalRegistry::ProposalStatus::kRejected:
|
|
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Rejected");
|
|
break;
|
|
}
|
|
|
|
// Prompt column (truncated)
|
|
ImGui::TableSetColumnIndex(2);
|
|
std::string truncated = proposal.prompt;
|
|
if (truncated.length() > 30) {
|
|
truncated = truncated.substr(0, 27) + "...";
|
|
}
|
|
ImGui::TextWrapped("%s", truncated.c_str());
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
}
|
|
|
|
void ProposalDrawer::DrawProposalDetail() {
|
|
if (!selected_proposal_) return;
|
|
|
|
const auto& p = *selected_proposal_;
|
|
|
|
// Metadata section
|
|
if (ImGui::CollapsingHeader("Metadata", ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
ImGui::Text("ID: %s", p.id.c_str());
|
|
ImGui::Text("Sandbox: %s", p.sandbox_id.c_str());
|
|
ImGui::Text("Created: %s", absl::FormatTime(p.created_at).c_str());
|
|
if (p.reviewed_at.has_value()) {
|
|
ImGui::Text("Reviewed: %s", absl::FormatTime(*p.reviewed_at).c_str());
|
|
}
|
|
ImGui::Text("Commands: %d", p.commands_executed);
|
|
ImGui::Text("Bytes Changed: %d", p.bytes_changed);
|
|
ImGui::Separator();
|
|
ImGui::TextWrapped("Prompt: %s", p.prompt.c_str());
|
|
ImGui::TextWrapped("Description: %s", p.description.c_str());
|
|
}
|
|
|
|
// Diff section
|
|
if (ImGui::CollapsingHeader("Diff", ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
if (diff_content_.empty() && std::filesystem::exists(p.diff_path)) {
|
|
std::ifstream diff_file(p.diff_path);
|
|
if (diff_file.is_open()) {
|
|
std::stringstream buffer;
|
|
buffer << diff_file.rdbuf();
|
|
diff_content_ = buffer.str();
|
|
}
|
|
}
|
|
|
|
if (!diff_content_.empty()) {
|
|
ImGui::BeginChild("DiffContent", ImVec2(0, 150), true,
|
|
ImGuiWindowFlags_HorizontalScrollbar);
|
|
ImGui::TextUnformatted(diff_content_.c_str());
|
|
ImGui::EndChild();
|
|
} else {
|
|
ImGui::TextWrapped("No diff available");
|
|
}
|
|
}
|
|
|
|
// Log section
|
|
if (ImGui::CollapsingHeader("Execution Log")) {
|
|
if (log_content_.empty() && std::filesystem::exists(p.log_path)) {
|
|
std::ifstream log_file(p.log_path);
|
|
if (log_file.is_open()) {
|
|
std::stringstream buffer;
|
|
std::string line;
|
|
int line_count = 0;
|
|
while (std::getline(log_file, line) && line_count < log_display_lines_) {
|
|
buffer << line << "\n";
|
|
line_count++;
|
|
}
|
|
if (line_count >= log_display_lines_) {
|
|
buffer << "... (truncated, see " << p.log_path.string() << ")\n";
|
|
}
|
|
log_content_ = buffer.str();
|
|
}
|
|
}
|
|
|
|
if (!log_content_.empty()) {
|
|
ImGui::BeginChild("LogContent", ImVec2(0, 150), true,
|
|
ImGuiWindowFlags_HorizontalScrollbar);
|
|
ImGui::TextUnformatted(log_content_.c_str());
|
|
ImGui::EndChild();
|
|
} else {
|
|
ImGui::TextWrapped("No log available");
|
|
}
|
|
}
|
|
|
|
// Policy Status section (NEW)
|
|
#ifdef YAZE_ENABLE_POLICY_FRAMEWORK
|
|
DrawPolicyStatus();
|
|
#endif
|
|
|
|
// Action buttons
|
|
ImGui::Separator();
|
|
DrawActionButtons();
|
|
}
|
|
|
|
void ProposalDrawer::DrawStatusFilter() {
|
|
const char* filter_labels[] = {"All", "Pending", "Accepted", "Rejected"};
|
|
int current_filter = static_cast<int>(status_filter_);
|
|
|
|
ImGui::SetNextItemWidth(120.0f);
|
|
if (ImGui::Combo("Filter", ¤t_filter, filter_labels, 4)) {
|
|
status_filter_ = static_cast<StatusFilter>(current_filter);
|
|
RefreshProposals();
|
|
}
|
|
}
|
|
|
|
void ProposalDrawer::DrawPolicyStatus() {
|
|
#ifdef YAZE_ENABLE_POLICY_FRAMEWORK
|
|
if (!selected_proposal_) return;
|
|
|
|
const auto& p = *selected_proposal_;
|
|
|
|
// Only evaluate policies for pending proposals
|
|
if (p.status != cli::ProposalRegistry::ProposalStatus::kPending) {
|
|
return;
|
|
}
|
|
|
|
if (ImGui::CollapsingHeader("Policy Status", ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
auto& policy_eval = cli::PolicyEvaluator::GetInstance();
|
|
|
|
if (!policy_eval.IsEnabled()) {
|
|
ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f),
|
|
ICON_MD_INFO " No policies configured");
|
|
ImGui::TextWrapped("Create .yaze/policies/agent.yaml to enable policy evaluation");
|
|
return;
|
|
}
|
|
|
|
// Evaluate proposal against policies
|
|
auto policy_result = policy_eval.EvaluateProposal(p.id);
|
|
|
|
if (!policy_result.ok()) {
|
|
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f),
|
|
ICON_MD_ERROR " Policy evaluation failed");
|
|
ImGui::TextWrapped("%s", policy_result.status().message().data());
|
|
return;
|
|
}
|
|
|
|
const auto& result = policy_result.value();
|
|
|
|
// Overall status
|
|
if (result.is_clean()) {
|
|
ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f),
|
|
ICON_MD_CHECK_CIRCLE " All policies passed");
|
|
} else if (result.passed) {
|
|
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f),
|
|
ICON_MD_WARNING " Passed with warnings");
|
|
} else {
|
|
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f),
|
|
ICON_MD_CANCEL " Critical violations found");
|
|
}
|
|
|
|
ImGui::Separator();
|
|
|
|
// Show critical violations
|
|
if (!result.critical_violations.empty()) {
|
|
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f),
|
|
ICON_MD_BLOCK " Critical Violations:");
|
|
for (const auto& violation : result.critical_violations) {
|
|
ImGui::Bullet();
|
|
ImGui::TextWrapped("%s: %s", violation.policy_name.c_str(),
|
|
violation.message.c_str());
|
|
if (!violation.details.empty()) {
|
|
ImGui::Indent();
|
|
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "%s",
|
|
violation.details.c_str());
|
|
ImGui::Unindent();
|
|
}
|
|
}
|
|
ImGui::Separator();
|
|
}
|
|
|
|
// Show warnings
|
|
if (!result.warnings.empty()) {
|
|
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f),
|
|
ICON_MD_WARNING " Warnings:");
|
|
for (const auto& violation : result.warnings) {
|
|
ImGui::Bullet();
|
|
ImGui::TextWrapped("%s: %s", violation.policy_name.c_str(),
|
|
violation.message.c_str());
|
|
if (!violation.details.empty()) {
|
|
ImGui::Indent();
|
|
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "%s",
|
|
violation.details.c_str());
|
|
ImGui::Unindent();
|
|
}
|
|
}
|
|
ImGui::Separator();
|
|
}
|
|
|
|
// Show info messages
|
|
if (!result.info.empty()) {
|
|
ImGui::TextColored(ImVec4(0.5f, 0.5f, 1.0f, 1.0f),
|
|
ICON_MD_INFO " Information:");
|
|
for (const auto& violation : result.info) {
|
|
ImGui::Bullet();
|
|
ImGui::TextWrapped("%s: %s", violation.policy_name.c_str(),
|
|
violation.message.c_str());
|
|
}
|
|
}
|
|
}
|
|
#endif // YAZE_ENABLE_POLICY_FRAMEWORK
|
|
}
|
|
|
|
void ProposalDrawer::DrawActionButtons() {
|
|
if (!selected_proposal_) return;
|
|
|
|
const auto& p = *selected_proposal_;
|
|
bool is_pending = p.status == cli::ProposalRegistry::ProposalStatus::kPending;
|
|
|
|
// Evaluate policies to determine if Accept button should be enabled
|
|
bool can_accept = true;
|
|
bool needs_override = false;
|
|
|
|
#ifdef YAZE_ENABLE_POLICY_FRAMEWORK
|
|
if (is_pending) {
|
|
auto& policy_eval = cli::PolicyEvaluator::GetInstance();
|
|
if (policy_eval.IsEnabled()) {
|
|
auto policy_result = policy_eval.EvaluateProposal(p.id);
|
|
if (policy_result.ok()) {
|
|
const auto& result = policy_result.value();
|
|
can_accept = !result.has_critical_violations();
|
|
needs_override = result.can_accept_with_override();
|
|
}
|
|
}
|
|
}
|
|
#endif // YAZE_ENABLE_POLICY_FRAMEWORK
|
|
|
|
// Accept button (only for pending proposals, gated by policy)
|
|
if (is_pending) {
|
|
if (!can_accept) {
|
|
ImGui::BeginDisabled();
|
|
}
|
|
|
|
if (ImGui::Button(ICON_MD_CHECK " Accept", ImVec2(-1, 0))) {
|
|
if (needs_override) {
|
|
// Show override confirmation dialog
|
|
show_override_dialog_ = true;
|
|
confirm_proposal_id_ = p.id;
|
|
} else {
|
|
// Proceed directly to accept confirmation
|
|
confirm_action_ = "accept";
|
|
confirm_proposal_id_ = p.id;
|
|
show_confirm_dialog_ = true;
|
|
}
|
|
}
|
|
|
|
if (!can_accept) {
|
|
ImGui::EndDisabled();
|
|
ImGui::SameLine();
|
|
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f),
|
|
"(Blocked by policy)");
|
|
}
|
|
|
|
// Reject button (only for pending proposals)
|
|
if (ImGui::Button(ICON_MD_CLOSE " Reject", ImVec2(-1, 0))) {
|
|
confirm_action_ = "reject";
|
|
confirm_proposal_id_ = p.id;
|
|
show_confirm_dialog_ = true;
|
|
}
|
|
}
|
|
|
|
// Delete button (for all proposals)
|
|
if (ImGui::Button(ICON_MD_DELETE " Delete", ImVec2(-1, 0))) {
|
|
confirm_action_ = "delete";
|
|
confirm_proposal_id_ = p.id;
|
|
show_confirm_dialog_ = true;
|
|
}
|
|
}
|
|
|
|
void ProposalDrawer::FocusProposal(const std::string& proposal_id) {
|
|
visible_ = true;
|
|
selected_proposal_id_ = proposal_id;
|
|
selected_proposal_ = nullptr;
|
|
needs_refresh_ = true;
|
|
}
|
|
|
|
void ProposalDrawer::RefreshProposals() {
|
|
auto& registry = cli::ProposalRegistry::Instance();
|
|
|
|
std::optional<cli::ProposalRegistry::ProposalStatus> filter;
|
|
switch (status_filter_) {
|
|
case StatusFilter::kPending:
|
|
filter = cli::ProposalRegistry::ProposalStatus::kPending;
|
|
break;
|
|
case StatusFilter::kAccepted:
|
|
filter = cli::ProposalRegistry::ProposalStatus::kAccepted;
|
|
break;
|
|
case StatusFilter::kRejected:
|
|
filter = cli::ProposalRegistry::ProposalStatus::kRejected;
|
|
break;
|
|
case StatusFilter::kAll:
|
|
filter = std::nullopt;
|
|
break;
|
|
}
|
|
|
|
proposals_ = registry.ListProposals(filter);
|
|
|
|
// Clear selection if proposal no longer exists
|
|
if (!selected_proposal_id_.empty()) {
|
|
bool found = false;
|
|
for (const auto& p : proposals_) {
|
|
if (p.id == selected_proposal_id_) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
selected_proposal_id_.clear();
|
|
selected_proposal_ = nullptr;
|
|
diff_content_.clear();
|
|
log_content_.clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ProposalDrawer::SelectProposal(const std::string& proposal_id) {
|
|
selected_proposal_id_ = proposal_id;
|
|
selected_proposal_ = nullptr;
|
|
diff_content_.clear();
|
|
log_content_.clear();
|
|
|
|
// Find the proposal in our list
|
|
for (auto& p : proposals_) {
|
|
if (p.id == proposal_id) {
|
|
selected_proposal_ = &p;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
absl::Status ProposalDrawer::AcceptProposal(const std::string& proposal_id) {
|
|
auto& registry = cli::ProposalRegistry::Instance();
|
|
|
|
// Get proposal metadata to find sandbox
|
|
auto proposal_or = registry.GetProposal(proposal_id);
|
|
if (!proposal_or.ok()) {
|
|
return proposal_or.status();
|
|
}
|
|
|
|
const auto& proposal = *proposal_or;
|
|
|
|
// Check if ROM is available
|
|
if (!rom_) {
|
|
return absl::FailedPreconditionError(
|
|
"No ROM loaded. Cannot merge proposal changes.");
|
|
}
|
|
|
|
// Find sandbox ROM path using the sandbox_id from the proposal
|
|
auto& sandbox_mgr = cli::RomSandboxManager::Instance();
|
|
auto sandboxes = sandbox_mgr.ListSandboxes();
|
|
|
|
std::filesystem::path sandbox_rom_path;
|
|
for (const auto& sandbox : sandboxes) {
|
|
if (sandbox.id == proposal.sandbox_id) {
|
|
sandbox_rom_path = sandbox.rom_path;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (sandbox_rom_path.empty()) {
|
|
return absl::NotFoundError(
|
|
absl::StrFormat("Sandbox ROM not found for proposal %s (sandbox: %s)",
|
|
proposal_id, proposal.sandbox_id));
|
|
}
|
|
|
|
// Verify sandbox ROM exists
|
|
std::error_code ec;
|
|
if (!std::filesystem::exists(sandbox_rom_path, ec)) {
|
|
return absl::NotFoundError(
|
|
absl::StrFormat("Sandbox ROM file does not exist: %s",
|
|
sandbox_rom_path.string()));
|
|
}
|
|
|
|
// Load sandbox ROM data
|
|
Rom sandbox_rom;
|
|
auto load_status = sandbox_rom.LoadFromFile(sandbox_rom_path.string());
|
|
if (!load_status.ok()) {
|
|
return absl::InternalError(
|
|
absl::StrFormat("Failed to load sandbox ROM: %s",
|
|
load_status.message()));
|
|
}
|
|
|
|
// Merge sandbox ROM data into main ROM
|
|
// Copy the entire ROM data vector from sandbox to main ROM
|
|
const auto& sandbox_data = sandbox_rom.vector();
|
|
auto merge_status = rom_->WriteVector(0, sandbox_data);
|
|
if (!merge_status.ok()) {
|
|
return absl::InternalError(
|
|
absl::StrFormat("Failed to merge sandbox ROM data: %s",
|
|
merge_status.message()));
|
|
}
|
|
|
|
// Update proposal status
|
|
auto status = registry.UpdateStatus(
|
|
proposal_id, cli::ProposalRegistry::ProposalStatus::kAccepted);
|
|
|
|
if (status.ok()) {
|
|
// Mark ROM as dirty so save prompts appear
|
|
// Note: Rom tracks dirty state internally via Write operations
|
|
// The WriteVector call above already marked it as dirty
|
|
}
|
|
|
|
needs_refresh_ = true;
|
|
return status;
|
|
}
|
|
|
|
absl::Status ProposalDrawer::RejectProposal(const std::string& proposal_id) {
|
|
auto& registry = cli::ProposalRegistry::Instance();
|
|
auto status = registry.UpdateStatus(
|
|
proposal_id, cli::ProposalRegistry::ProposalStatus::kRejected);
|
|
|
|
needs_refresh_ = true;
|
|
return status;
|
|
}
|
|
|
|
absl::Status ProposalDrawer::DeleteProposal(const std::string& proposal_id) {
|
|
auto& registry = cli::ProposalRegistry::Instance();
|
|
auto status = registry.RemoveProposal(proposal_id);
|
|
|
|
if (proposal_id == selected_proposal_id_) {
|
|
selected_proposal_id_.clear();
|
|
selected_proposal_ = nullptr;
|
|
diff_content_.clear();
|
|
log_content_.clear();
|
|
}
|
|
|
|
needs_refresh_ = true;
|
|
return status;
|
|
}
|
|
|
|
} // namespace editor
|
|
} // namespace yaze
|