1#define IMGUI_DEFINE_MATH_OPERATORS
15#include "absl/status/status.h"
16#include "absl/strings/match.h"
17#include "absl/strings/str_format.h"
18#include "absl/strings/str_join.h"
19#include "absl/time/clock.h"
20#include "absl/time/time.h"
29#include "imgui/imgui.h"
36#if defined(YAZE_WITH_GRPC)
42namespace fs = std::filesystem;
46 if (!path.empty() && path.front() ==
'~') {
47 const char* home =
nullptr;
49 home = std::getenv(
"USERPROFILE");
51 home = std::getenv(
"HOME");
53 if (home !=
nullptr) {
54 path.replace(0, 1, home);
57 return std::filesystem::path(path);
62 if (!config_dir.ok()) {
64 return fs::current_path() /
".yaze" /
"agent" /
"history" / (session_id.empty() ?
"default.json" : session_id +
".json");
67 fs::path base = *config_dir;
71 auto directory = base /
"agent";
74 if (!session_id.empty()) {
75 directory = directory /
"sessions";
76 return directory / (session_id +
"_history.json");
79 return directory /
"chat_history.json";
83 const int column_count =
static_cast<int>(table_data.
headers.size());
84 if (column_count <= 0) {
85 ImGui::TextDisabled(
"(empty)");
89 if (ImGui::BeginTable(
"structured_table", column_count,
90 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
91 ImGuiTableFlags_SizingStretchProp)) {
92 for (
const auto& header : table_data.
headers) {
93 ImGui::TableSetupColumn(header.c_str());
95 ImGui::TableHeadersRow();
97 for (
const auto& row : table_data.
rows) {
98 ImGui::TableNextRow();
99 for (
int col = 0; col < column_count; ++col) {
100 ImGui::TableSetColumnIndex(col);
101 if (col <
static_cast<int>(row.size())) {
102 ImGui::TextWrapped(
"%s", row[col].c_str());
104 ImGui::TextUnformatted(
"-");
133 static Rom* last_rom_initialized =
nullptr;
144 if (labels_status.ok()) {
147 last_rom_initialized = rom;
151 total_count += labels.
size();
187 [
this](
const std::string& message) {
197 std::filesystem::path output_path;
208 absl::StrFormat(
"Capture failed: %s", status.message()),
226 if (!directory.empty()) {
227 std::filesystem::create_directories(directory, ec);
239 "Chat history requires gRPC/JSON support and is disabled",
246 absl::StatusOr<AgentChatHistoryCodec::Snapshot> result =
249 if (result.status().code() == absl::StatusCode::kUnimplemented) {
253 "Chat history requires gRPC/JSON support and is disabled",
262 result.status().ToString()),
270 if (!snapshot.
history.empty()) {
305 "Chat history requires gRPC/JSON support and is disabled",
328 if (status.code() == absl::StatusCode::kUnimplemented) {
332 "Chat history requires gRPC/JSON support and is disabled",
355 for (
const auto& message : history) {
356 if (message.metrics.has_value()) {
357 total = std::max(total, message.metrics->total_proposals);
358 }
else if (message.proposal.has_value()) {
366 if (proposal_id.empty()) {
376 int new_total_proposals) {
379 if (msg.proposal.has_value()) {
380 const auto& proposal = *msg.proposal;
383 proposal.id, proposal.change_count,
384 proposal.change_count == 1 ?
"" :
"s"),
389 delta == 1 ?
"" :
"s"),
394 if (msg.proposal.has_value()) {
400 const absl::StatusOr<ChatMessage>& response) {
401 if (!response.ok()) {
404 absl::StrFormat(
"Agent error: %s", response.status().message()),
410 const ChatMessage& message = response.value();
412 if (message.metrics.has_value()) {
413 total = std::max(total, message.metrics->total_proposals);
427 if (msg.is_internal) {
431 ImGui::PushID(index);
434 const bool from_user = (msg.sender == ChatMessage::Sender::kUser);
435 const ImVec4 header_color = from_user ? theme.user_message_color : theme.agent_message_color;
436 const char* header_label = from_user ?
"You" :
"Agent";
438 ImGui::TextColored(header_color,
"%s", header_label);
442 "%s", absl::FormatTime(
"%H:%M:%S", msg.timestamp, absl::LocalTimeZone())
447 ImGui::PushStyleColor(ImGuiCol_Button, theme.button_copy);
448 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, theme.button_copy_hover);
450 std::string copy_text = msg.message;
451 if (copy_text.empty() && msg.json_pretty.has_value()) {
452 copy_text = *msg.json_pretty;
454 ImGui::SetClipboardText(copy_text.c_str());
459 ImGui::PopStyleColor(2);
460 if (ImGui::IsItemHovered()) {
461 ImGui::SetTooltip(
"Copy to clipboard");
466 if (msg.table_data.has_value()) {
467 RenderTable(*msg.table_data);
468 }
else if (msg.json_pretty.has_value()) {
471 ImGui::PushStyleColor(ImGuiCol_Text, theme.json_text_color);
473 ImGui::PopStyleColor();
475 ImGui::TextWrapped(
"%s", msg.message.c_str());
478 if (msg.proposal.has_value()) {
490 if (!msg.proposal.has_value()) {
495 const auto& proposal = *msg.proposal;
496 ImGui::PushStyleColor(ImGuiCol_ChildBg, theme.proposal_panel_bg);
497 ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 4.0f);
498 ImGui::BeginChild(absl::StrFormat(
"proposal_panel_%d", index).c_str(),
499 ImVec2(0, ImGui::GetFrameHeight() * 3.2f),
true,
500 ImGuiWindowFlags_None);
502 ImGui::TextColored(theme.proposal_accent,
"%s Proposal %s",
504 ImGui::Text(
"Changes: %d", proposal.change_count);
505 ImGui::Text(
"Commands: %d", proposal.executed_commands);
507 if (!proposal.sandbox_rom_path.empty()) {
508 ImGui::TextDisabled(
"Sandbox: %s",
509 proposal.sandbox_rom_path.string().c_str());
511 if (!proposal.proposal_json_path.empty()) {
512 ImGui::TextDisabled(
"Manifest: %s",
513 proposal.proposal_json_path.string().c_str());
516 if (ImGui::SmallButton(
521 if (ImGui::SmallButton(
523 ImGui::SetClipboardText(proposal.id.c_str());
530 ImGui::PopStyleVar();
531 ImGui::PopStyleColor();
537 float reserved_height = ImGui::GetFrameHeightWithSpacing() * 4.0f;
538 reserved_height += 100.0f;
541 ImGui::PushStyleColor(ImGuiCol_ChildBg, theme.code_bg_color);
542 if (ImGui::BeginChild(
"History", ImVec2(0, -reserved_height),
true,
543 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
544 if (history.empty()) {
546 ImVec2 text_size = ImGui::CalcTextSize(
"No messages yet");
547 ImVec2 avail = ImGui::GetContentRegionAvail();
548 ImGui::SetCursorPosX((avail.x - text_size.x) / 2);
549 ImGui::SetCursorPosY((avail.y - text_size.y) / 2);
551 ImGui::SetCursorPosX(
552 (avail.x - ImGui::CalcTextSize(
"Start typing below to begin").x) / 2);
553 ImGui::TextDisabled(
"Start typing below to begin");
555 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,
557 for (
size_t index = 0; index < history.size(); ++index) {
560 ImGui::PopStyleVar();
564 ImGui::SetScrollHereY(1.0f);
568 ImGui::PopStyleColor();
576 ImGui::TextColored(theme.command_text_color,
ICON_MD_EDIT " Message:");
578 bool submitted = ImGui::InputTextMultiline(
580 ImGuiInputTextFlags_None);
584 if (ImGui::IsItemFocused()) {
585 if (ImGui::IsKeyPressed(ImGuiKey_Enter) && ImGui::GetIO().KeyCtrl) {
593 ImGui::PushStyleColor(ImGuiCol_Button, theme.provider_gemini);
594 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
595 ImVec4(theme.provider_gemini.x * 1.2f, theme.provider_gemini.y * 1.1f,
596 theme.provider_gemini.z, theme.provider_gemini.w));
597 if (ImGui::Button(absl::StrFormat(
"%s Send",
ICON_MD_SEND).c_str(),
613 ImGui::SetKeyboardFocusHere(-1);
616 ImGui::PopStyleColor(2);
617 if (ImGui::IsItemHovered()) {
618 ImGui::SetTooltip(
"Send message (Ctrl+Enter)");
622 ImGui::TextDisabled(
ICON_MD_INFO " Ctrl+Enter: send • Enter: newline");
625 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.6f, 0.5f, 0.0f, 0.7f));
626 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
627 ImVec4(1.0f, 0.843f, 0.0f, 0.9f));
634 ImGui::PopStyleColor(2);
635 if (ImGui::IsItemHovered()) {
636 ImGui::SetTooltip(
"Clear all messages from conversation");
640 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.4f, 0.35f, 0.6f, 0.7f));
641 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
642 ImVec4(0.502f, 0.0f, 0.502f, 0.9f));
648 ImGui::PopStyleColor(2);
649 if (ImGui::IsItemHovered()) {
650 ImGui::SetTooltip(
"View code proposals");
655 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.15f, 0.5f, 0.7f, 0.7f));
656 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
657 ImVec4(0.196f, 0.6f, 0.8f, 0.9f));
661 std::filesystem::path captured_path;
670 absl::StrFormat(
"Capture failed: %s", status.message()),
675 ImGui::PopStyleColor(2);
676 if (ImGui::IsItemHovered()) {
677 ImGui::SetTooltip(
"Capture screenshot for vision analysis");
681 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.5f, 0.3f, 0.3f, 0.7f));
682 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
683 ImVec4(0.863f, 0.078f, 0.235f, 0.9f));
690 ImGui::PopStyleColor(2);
691 if (ImGui::IsItemHovered()) {
692 ImGui::SetTooltip(
"Stop current generation");
696 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.3f, 0.5f, 0.3f, 0.7f));
697 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
698 ImVec4(0.133f, 0.545f, 0.133f, 0.9f));
705 ImGui::PopStyleColor(2);
706 if (ImGui::IsItemHovered()) {
707 ImGui::SetTooltip(
"Export conversation history");
713 ImGui::TextColored(ImVec4(0.196f, 0.6f, 0.8f, 1.0f),
715 ImGui::SetNextItemWidth(-200);
716 ImGui::InputTextWithHint(
717 "##quick_vision_prompt",
"Ask about the screenshot...",
720 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.15f, 0.5f, 0.7f, 0.8f));
721 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
722 ImVec4(0.196f, 0.6f, 0.8f, 1.0f));
723 if (ImGui::Button(
ICON_MD_SEND " Analyze##vision_send", ImVec2(180, 0))) {
735 ImGui::PopStyleColor(2);
749 ImGui::SetNextWindowSize(ImVec2(1400, 1000), ImGuiCond_FirstUseEver);
750 ImGui::Begin(
title_.c_str(), &
active_, ImGuiWindowFlags_MenuBar);
753 if (ImGui::BeginMenuBar()) {
778 if (ImGui::MenuItem(
ICON_MD_ADD " New Session Tab")) {
800 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.2f, 0.8f, 0.4f, 1.0f));
809 ImGui::PopStyleColor();
819 ? ImVec4(0.133f, 0.545f, 0.133f, 1.0f)
820 : ImVec4(0.6f, 0.6f, 0.6f, 1.0f);
823 ImDrawList* draw_list = ImGui::GetWindowDrawList();
824 ImVec2 bar_start = ImGui::GetCursorScreenPos();
825 ImVec2 bar_size(ImGui::GetContentRegionAvail().x, 60);
828 ImU32 color_top = ImGui::GetColorU32(ImVec4(0.18f, 0.22f, 0.28f, 1.0f));
829 ImU32 color_bottom = ImGui::GetColorU32(ImVec4(0.12f, 0.16f, 0.22f, 1.0f));
830 draw_list->AddRectFilledMultiColor(
831 bar_start, ImVec2(bar_start.x + bar_size.x, bar_start.y + bar_size.y),
832 color_top, color_top, color_bottom, color_bottom);
836 ? ImVec4(0.2f, 0.8f, 0.4f, 1.0f)
838 ? ImVec4(0.196f, 0.6f, 0.8f, 1.0f)
839 : ImVec4(0.6f, 0.6f, 0.6f, 1.0f);
840 draw_list->AddRectFilled(bar_start,
841 ImVec2(bar_start.x + bar_size.x, bar_start.y + 3),
842 ImGui::GetColorU32(accent_color));
844 ImGui::BeginChild(
"AgentChat_ConnectionBar", bar_size,
false,
845 ImGuiWindowFlags_NoScrollbar);
846 ImGui::PushID(
"ConnectionBar");
849 float content_height = ImGui::GetFrameHeight();
850 float vertical_padding = (55.0f - content_height) / 2.0f;
851 ImGui::SetCursorPosY(ImGui::GetCursorPosY() + vertical_padding);
856 ImGui::SetNextItemWidth(95);
857 const char* providers[] = {
"Mock",
"Ollama",
"Gemini"};
861 if (ImGui::Combo(
"##main_provider", ¤t_provider, providers, 3)) {
863 : (current_provider == 1) ?
"ollama"
879 ImGui::SetNextItemWidth(150);
880 ImGui::InputTextWithHint(
"##main_model",
"Model name...",
883 if (ImGui::IsItemHovered()) {
884 ImGui::SetTooltip(
"AI model name");
891 ImGui::SetNextItemWidth(200);
892 if (ImGui::InputTextWithHint(
"##main_api_key",
893 "API Key (or load from env)...",
896 ImGuiInputTextFlags_Password)) {
899 if (ImGui::IsItemHovered()) {
900 ImGui::SetTooltip(
"Gemini API Key (hidden)");
904 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.15f, 0.5f, 0.7f, 0.7f));
905 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
906 ImVec4(0.196f, 0.6f, 0.8f, 1.0f));
908 const char* gemini_key =
nullptr;
910 char* env_key =
nullptr;
912 if (_dupenv_s(&env_key, &len,
"GEMINI_API_KEY") == 0 &&
913 env_key !=
nullptr) {
920 gemini_key = std::getenv(
"GEMINI_API_KEY");
931 ImGui::PopStyleColor(2);
932 if (ImGui::IsItemHovered()) {
933 ImGui::SetTooltip(
"Load from GEMINI_API_KEY");
939 if (ImGui::IsItemHovered()) {
940 ImGui::SetTooltip(
"Show reasoning");
945 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.4f, 0.4f, 0.6f, 0.7f));
946 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
947 ImVec4(0.5f, 0.5f, 0.7f, 0.9f));
948 if (ImGui::SmallButton(
952 ImGui::OpenPopup(
"SessionsPopup");
954 ImGui::PopStyleColor(2);
955 if (ImGui::IsItemHovered()) {
956 ImGui::SetTooltip(
"Manage chat sessions");
960 if (ImGui::BeginPopup(
"SessionsPopup")) {
961 ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f),
965 if (ImGui::Button(
ICON_MD_ADD " New Session", ImVec2(200, 0))) {
966 std::string session_id = absl::StrFormat(
968 std::string session_name = absl::StrFormat(
975 ImGui::CloseCurrentPopup();
980 ImGui::TextDisabled(
"Active Sessions:");
983 ImGui::PushID(
static_cast<int>(i));
985 if (ImGui::Selectable(absl::StrFormat(
"%s %s%s",
ICON_MD_CHAT,
991 ImGui::CloseCurrentPopup();
1001 ImGui::SameLine(ImGui::GetContentRegionAvail().x - 25);
1011 if (ImGui::BeginTable(
"AgentChat_MainLayout", 2,
1012 ImGuiTableFlags_Resizable |
1013 ImGuiTableFlags_Reorderable |
1014 ImGuiTableFlags_BordersInnerV |
1015 ImGuiTableFlags_ContextMenuInBody)) {
1016 ImGui::TableSetupColumn(
"Chat", ImGuiTableColumnFlags_WidthStretch, 0.7f);
1017 ImGui::TableSetupColumn(
"Controls", ImGuiTableColumnFlags_WidthStretch,
1019 ImGui::TableHeadersRow();
1020 ImGui::TableNextRow();
1023 ImGui::TableSetColumnIndex(0);
1024 ImGui::PushID(
"ChatColumn");
1035 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 4));
1036 ImGui::TextColored(ImVec4(1.0f, 0.647f, 0.0f, 1.0f),
1041 ImVec4(0.6f, 0.6f, 0.6f, 1.0f),
"%s",
1044 ImGui::PopStyleVar();
1050 ImGui::TableSetColumnIndex(1);
1051 ImGui::PushID(
"ControlsColumn");
1052 ImGui::BeginChild(
"AgentChat_ControlPanels", ImVec2(0, 0),
false);
1055 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,
1057 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,
1060 if (ImGui::BeginTable(
"##commands_and_multimodal", 2)) {
1061 ImGui::TableSetupColumn(
"Commands", ImGuiTableColumnFlags_WidthFixed, 180);
1062 ImGui::TableSetupColumn(
"Multimodal", ImGuiTableColumnFlags_WidthFixed, ImGui::GetContentRegionAvail().x - 180);
1063 ImGui::TableNextRow();
1064 ImGui::TableSetColumnIndex(0);
1066 ImGui::TableSetColumnIndex(1);
1078 ImGui::PopStyleVar(2);
1090 ImGui::PushID(
"CollabPanel");
1093 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 3));
1094 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(3, 2));
1099 : ImVec4(0.6f, 0.6f, 0.6f, 1.0f);
1102 ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.12f, 0.14f, 0.18f, 0.95f));
1103 ImGui::BeginChild(
"CollabPanel", ImVec2(0, 140),
true);
1104 ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f),
ICON_MD_PEOPLE " Collaboration");
1112 ImGui::RadioButton(
ICON_MD_WIFI " Network##collab_mode_network",
1117 if (ImGui::BeginTable(
"Collab_MainTable", 2, ImGuiTableFlags_BordersInnerV)) {
1118 ImGui::TableSetupColumn(
"Status", ImGuiTableColumnFlags_WidthFixed, 150);
1119 ImGui::TableSetupColumn(
"Controls", ImGuiTableColumnFlags_WidthFixed,
1120 ImGui::GetContentRegionAvail().x - 150);
1121 ImGui::TableNextRow();
1124 ImGui::TableSetColumnIndex(0);
1125 ImGui::BeginGroup();
1126 ImGui::PushID(
"StatusColumn");
1128 ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.15f, 0.2f, 0.18f, 0.4f));
1129 ImGui::BeginChild(
"Collab_SessionDetails", ImVec2(0, 60),
true);
1130 ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f),
1140 ImGui::TextColored(ImVec4(0.196f, 0.6f, 0.8f, 1.0f),
1154 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.4f, 0.4f, 0.6f, 0.6f));
1155 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
1156 ImVec4(0.416f, 0.353f, 0.804f, 1.0f));
1163 ImGui::PopStyleColor(2);
1167 ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1.0f),
1170 absl::LocalTimeZone()).c_str());
1173 ImGui::PopStyleColor();
1176 ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.12f, 0.16f, 0.14f, 0.4f));
1177 ImGui::BeginChild(
"Collab_ParticipantsList", ImVec2(0, 0),
true);
1184 ImGui::PushID(
static_cast<int>(i));
1190 ImGui::PopStyleColor();
1196 ImGui::TableSetColumnIndex(1);
1197 ImGui::BeginGroup();
1198 ImGui::PushID(
"ControlsColumn");
1199 ImGui::BeginChild(
"Collab_Controls", ImVec2(0, 0),
false);
1201 const bool can_host =
1203 const bool can_join =
1205 const bool can_leave =
1207 const bool can_refresh =
1212 ImGui::TextColored(ImVec4(0.196f, 0.6f, 0.8f, 1.0f),
ICON_MD_CLOUD);
1214 ImGui::SetNextItemWidth(100);
1217 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.15f, 0.5f, 0.7f, 0.8f));
1218 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.196f, 0.6f, 0.8f, 1.0f));
1219 if (ImGui::SmallButton(
ICON_MD_LINK "##connect_server_btn")) {
1225 ImGui::PopStyleColor(2);
1226 if (ImGui::IsItemHovered()) {
1227 ImGui::SetTooltip(
"Connect to collaboration server");
1234 ImGui::SetNextItemWidth(100);
1237 if (!can_host) ImGui::BeginDisabled();
1238 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.6f, 0.5f, 0.0f, 0.8f));
1239 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 0.843f, 0.0f, 1.0f));
1248 if (session_or.ok()) {
1261 ImGui::PopStyleColor(2);
1263 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
1264 ImGui::SetTooltip(
"Provide host_session callback to enable hosting");
1266 ImGui::EndDisabled();
1267 }
else if (ImGui::IsItemHovered()) {
1268 ImGui::SetTooltip(
"Host a new collaboration session");
1272 ImGui::TextColored(ImVec4(0.133f, 0.545f, 0.133f, 1.0f),
ICON_MD_LOGIN);
1274 ImGui::SetNextItemWidth(100);
1277 if (!can_join) ImGui::BeginDisabled();
1278 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.1f, 0.4f, 0.1f, 0.8f));
1279 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.133f, 0.545f, 0.133f, 1.0f));
1288 if (session_or.ok()) {
1300 ImGui::PopStyleColor(2);
1302 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
1303 ImGui::SetTooltip(
"Provide join_session callback to enable joining");
1305 ImGui::EndDisabled();
1306 }
else if (ImGui::IsItemHovered()) {
1307 ImGui::SetTooltip(
"Join an existing collaboration session");
1312 if (!can_leave) ImGui::BeginDisabled();
1313 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.7f, 0.2f, 0.2f, 0.8f));
1314 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.863f, 0.078f, 0.235f, 1.0f));
1330 ImGui::PopStyleColor(2);
1331 if (!can_leave) ImGui::EndDisabled();
1334 if (!can_refresh) ImGui::BeginDisabled();
1335 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.4f, 0.4f, 0.6f, 0.8f));
1336 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.416f, 0.353f, 0.804f, 1.0f));
1340 ImGui::PopStyleColor(2);
1341 if (!can_refresh && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
1342 ImGui::SetTooltip(
"Provide refresh_session callback to enable");
1344 if (!can_refresh) ImGui::EndDisabled();
1346 ImGui::TextDisabled(
ICON_MD_INFO " Start or join a session to collaborate.");
1356 ImGui::PopStyleColor();
1357 ImGui::PopStyleVar(2);
1363 ImGui::PushID(
"MultimodalPanel");
1367 ImGui::BeginChild(
"Multimodal_Panel", ImVec2(0, 120),
true);
1374 ImGui::RadioButton(
"Full##mm_full",
1378 ImGui::RadioButton(
"Active##mm_active",
1382 ImGui::RadioButton(
"Window##mm_window",
1386 ImGui::RadioButton(
"Region##mm_region",
1391 ImGui::BeginDisabled();
1397 std::filesystem::path captured_path;
1398 absl::Status status =
1403 absl::StrFormat(
"Captured %s", captured_path.string());
1415 absl::StrFormat(
"Snapshot failed: %s", status.message()),
1422 ImGui::EndDisabled();
1430 if (ImGui::IsItemHovered() &&
1437 ImGui::BeginDisabled();
1439 if (ImGui::SmallButton(
ICON_MD_SEND " Analyze##mm_send")) {
1462 absl::StrFormat(
"Gemini request failed: %s", status.message()),
1469 ImGui::EndDisabled();
1483 ImGui::TextColored(theme.provider_ollama,
ICON_MD_CROP " Drag to select region");
1484 if (ImGui::SmallButton(
"Cancel##region_cancel")) {
1501 ImGui::PushID(
"AutomationPanel");
1514 if (ImGui::BeginChild(
"Automation_Panel", ImVec2(0, 240),
true)) {
1517 ImVec4 header_glow = ImVec4(
1518 theme.provider_ollama.x + 0.3f * pulse,
1519 theme.provider_ollama.y + 0.2f * pulse,
1520 theme.provider_ollama.z + 0.4f * pulse,
1524 ImGui::PushStyleColor(ImGuiCol_Text, header_glow);
1526 ImGui::PopStyleColor();
1529 ImGui::TextDisabled(
"[v0.4.x]");
1533 ImVec4 status_color;
1534 const char* status_text;
1535 const char* status_icon;
1540 status_color = ImVec4(0.1f, green_pulse, 0.3f, 1.0f);
1541 status_text =
"ONLINE";
1546 status_color = ImVec4(red_pulse, 0.2f, 0.2f, 1.0f);
1547 status_text =
"OFFLINE";
1552 ImGui::TextColored(status_color,
"%s %s", status_icon, status_text);
1562 if (auto_ref_pulse) {
1563 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.5f, 0.7f, 0.8f));
1573 if (auto_ref_pulse) {
1574 ImGui::PopStyleColor();
1577 if (ImGui::IsItemHovered()) {
1578 ImGui::SetTooltip(
"Refresh automation status\nAuto-refresh: %s (%.1fs)",
1586 if (ImGui::IsItemHovered()) {
1587 ImGui::SetTooltip(
"Auto-refresh connection status");
1597 if (ImGui::IsItemHovered()) {
1598 ImGui::SetTooltip(
"Open automation dashboard");
1607 if (ImGui::IsItemHovered()) {
1608 ImGui::SetTooltip(
"Replay last automation plan");
1613 ImGui::SetNextItemWidth(80.0f);
1615 0.5f, 10.0f,
"%.1fs");
1616 if (ImGui::IsItemHovered()) {
1617 ImGui::SetTooltip(
"Auto-refresh interval");
1625 ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 1.0f),
"%s RECENT ACTIONS",
ICON_MD_LIST);
1631 ImGui::TextDisabled(
" > No recent actions");
1632 ImGui::TextDisabled(
" > Waiting for automation tasks...");
1636 std::string dot_string(dots,
'.');
1637 ImGui::TextDisabled(
" > %s", dot_string.c_str());
1640 ImGui::BeginChild(
"ActionQueue", ImVec2(0, 100),
true,
1641 ImGuiWindowFlags_AlwaysVerticalScrollbar);
1644 ImDrawList* draw_list = ImGui::GetWindowDrawList();
1645 ImVec2 win_pos = ImGui::GetWindowPos();
1646 ImVec2 win_size = ImGui::GetWindowSize();
1649 for (
float y = 0; y < win_size.y; y += 4.0f) {
1651 if (offset_y < win_size.y) {
1653 ImVec2(win_pos.x, win_pos.y + offset_y),
1654 ImVec2(win_pos.x + win_size.x, win_pos.y + offset_y),
1655 IM_COL32(0, 0, 0, 20));
1660 ImGui::PushID(test.test_id.c_str());
1663 ImVec4 action_color;
1664 const char* status_icon;
1665 bool is_running =
false;
1667 if (test.status ==
"success" || test.status ==
"completed" || test.status ==
"passed") {
1668 action_color = theme.status_success;
1670 }
else if (test.status ==
"running" || test.status ==
"in_progress") {
1673 action_color = ImVec4(
1674 theme.provider_ollama.x * running_pulse,
1675 theme.provider_ollama.y * (0.8f + 0.2f * running_pulse),
1676 theme.provider_ollama.z * running_pulse,
1680 }
else if (test.status ==
"failed" || test.status ==
"error") {
1681 action_color = theme.status_error;
1684 action_color = theme.text_secondary_color;
1689 ImGui::TextColored(action_color,
"%s", status_icon);
1693 ImGui::Text(
"> %s", test.name.c_str());
1696 if (test.updated_at != absl::InfinitePast()) {
1698 auto elapsed = absl::Now() - test.updated_at;
1699 if (elapsed < absl::Seconds(60)) {
1700 ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f),
1701 "[%ds]",
static_cast<int>(absl::ToInt64Seconds(elapsed)));
1702 }
else if (elapsed < absl::Minutes(60)) {
1703 ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f),
1704 "[%dm]",
static_cast<int>(absl::ToInt64Minutes(elapsed)));
1706 ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f),
1707 "[%dh]",
static_cast<int>(absl::ToInt64Hours(elapsed)));
1712 if (!test.message.empty()) {
1713 ImGui::Indent(20.0f);
1714 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.7f, 0.7f, 0.7f, 1.0f));
1716 ImGui::PopStyleColor();
1717 ImGui::Unindent(20.0f);
1736 if (!session_or.ok()) {
1737 if (session_or.status().code() == absl::StatusCode::kNotFound) {
1744 session_or.status().message()),
1757 bool update_action_timestamp) {
1764 if (update_action_timestamp) {
1771 const absl::Time now = absl::Now();
1797 absl::StrFormat(
"Switched to shared chat history for session %s",
1827 const absl::Time now = absl::Now();
1842 const size_t new_size = result->history.size();
1854 absl::StrFormat(
"📬 %zu new message%s from collaborators",
1855 new_messages, new_messages == 1 ?
"" :
"s"),
1883 ImGui::PushStyleColor(ImGuiCol_ChildBg, theme.panel_bg_color);
1884 ImGui::BeginChild(
"AgentConfig", ImVec2(0, 140),
true);
1889 int provider_idx = 0;
1895 if (ImGui::RadioButton(
"Mock", &provider_idx, 0)) {
1901 if (ImGui::RadioButton(
"Ollama", &provider_idx, 1)) {
1907 if (ImGui::RadioButton(
"Gemini", &provider_idx, 2)) {
1924 ImGuiInputTextFlags_Password);
1931 ImGui::SetNextItemWidth(-1);
1943 ImGui::PopStyleColor();
1947 ImGui::PushID(
"Z3EDCmdPanel");
1948 ImVec4 command_color = ImVec4(1.0f, 0.647f, 0.0f, 1.0f);
1951 ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.14f, 0.12f, 0.18f, 0.95f));
1952 ImGui::BeginChild(
"Z3ED_CommandsChild", ImVec2(0, 100),
1958 ImGui::SetNextItemWidth(-60);
1959 ImGui::InputTextWithHint(
1975 ImGui::EndDisabled();
1976 if (ImGui::IsItemHovered()) {
1977 ImGui::SetTooltip(
"Run command");
1985 const auto& proposals = *result;
1990 if (ImGui::IsItemHovered())
1991 ImGui::SetTooltip(
"List");
2000 if (ImGui::IsItemHovered())
2001 ImGui::SetTooltip(
"Diff");
2008 if (ImGui::IsItemHovered())
2009 ImGui::SetTooltip(
"Accept");
2016 if (ImGui::IsItemHovered())
2017 ImGui::SetTooltip(
"Reject");
2021 ImGui::TextDisabled(
2026 ImGui::PopStyleColor();
2032 ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.18f, 0.14f, 0.12f, 1.0f));
2033 ImGui::BeginChild(
"RomSync", ImVec2(0, 130),
true);
2040 ImGui::Text(
"Hash: %s",
2050 ImGui::TextDisabled(
"No ROM loaded");
2054 ImGui::Text(
"Last Sync: %s",
2056 absl::LocalTimeZone())
2064 ImGui::SliderInt(
"Sync Interval (seconds)",
2076 ImGui::BeginDisabled();
2081 if (diff_result.ok()) {
2092 " ROM synced to collaborators",
2097 diff_result.status().message()),
2104 ImGui::EndDisabled();
2105 if (ImGui::IsItemHovered()) {
2106 ImGui::SetTooltip(
"Connect to a network session to sync ROM");
2117 ImGui::BeginChild(
"PendingSyncs", ImVec2(0, 80),
true);
2119 ImGui::BulletText(
"%s", sync.substr(0, 40).c_str());
2125 ImGui::PopStyleColor();
2130 ImGuiTreeNodeFlags_DefaultOpen)) {
2134 ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.12f, 0.12f, 0.16f, 1.0f));
2135 ImGui::BeginChild(
"SnapshotPreview", ImVec2(0, 200),
true);
2144 ImGui::TextDisabled(
"Preview: [Image preview not yet implemented]");
2152 ImGui::BeginDisabled();
2154 if (ImGui::Button(
ICON_MD_SHARE " Share with Collaborators",
2164 ImGui::EndDisabled();
2165 if (ImGui::IsItemHovered()) {
2166 ImGui::SetTooltip(
"Connect to a network session to share snapshots");
2171 ImGui::TextWrapped(
"Use the Multimodal panel to capture a snapshot");
2175 ImGui::PopStyleColor();
2185 if (proposals_result.ok()) {
2186 const auto& proposals = *proposals_result;
2188 ImGui::Text(
"Total Proposals: %zu", proposals.size());
2191 if (proposals.empty()) {
2192 ImGui::TextDisabled(
2193 "No proposals yet. Use the agent to create proposals.");
2195 if (ImGui::BeginTable(
"ProposalsTable", 3,
2196 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
2197 ImGuiTableFlags_Resizable)) {
2198 ImGui::TableSetupColumn(
"ID", ImGuiTableColumnFlags_WidthFixed,
2200 ImGui::TableSetupColumn(
"Description",
2201 ImGuiTableColumnFlags_WidthStretch);
2202 ImGui::TableSetupColumn(
"Actions", ImGuiTableColumnFlags_WidthFixed,
2204 ImGui::TableHeadersRow();
2206 for (
const auto& proposal_id : proposals) {
2207 ImGui::TableNextRow();
2208 ImGui::TableNextColumn();
2209 ImGui::TextUnformatted(proposal_id.c_str());
2211 ImGui::TableNextColumn();
2212 ImGui::TextDisabled(
"Proposal details...");
2214 ImGui::TableNextColumn();
2215 ImGui::PushID(proposal_id.c_str());
2244 std::string error_msg(proposals_result.status().message());
2245 ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f),
2246 "Failed to load proposals: %s", error_msg.c_str());
2249 ImGui::TextDisabled(
"Proposal management not available");
2250 ImGui::TextWrapped(
"Set up Z3ED command callbacks to enable this feature");
2255 ImGui::PushID(
"HarnessPanel");
2256 ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.12f, 0.16f, 0.22f, 0.95f));
2257 ImGui::BeginChild(
"HarnessPanel", ImVec2(0, 220),
true);
2259 ImGui::TextColored(ImVec4(0.392f, 0.863f, 1.0f, 1.0f),
ICON_MD_PLAY_CIRCLE " Harness Automation");
2262 ImGui::TextDisabled(
"Shared automation pipeline between CLI + Agent Chat");
2265 if (ImGui::BeginTable(
"HarnessActions", 2, ImGuiTableFlags_BordersInnerV)) {
2266 ImGui::TableSetupColumn(
"Actions", ImGuiTableColumnFlags_WidthFixed, 170.0f);
2267 ImGui::TableSetupColumn(
"Telemetry", ImGuiTableColumnFlags_WidthStretch);
2268 ImGui::TableNextRow();
2271 ImGui::TableSetColumnIndex(0);
2272 ImGui::BeginGroup();
2278 if (!has_callbacks) {
2279 ImGui::TextDisabled(
"Automation bridge not available");
2280 ImGui::TextWrapped(
"Hook up AutomationCallbacks via EditorManager to enable controls.");
2288 ImGui::Button(
ICON_MD_REPLAY " Replay Last Plan", ImVec2(-FLT_MIN, 0))) {
2293 ImGui::Button(
ICON_MD_LIST " Active Tests", ImVec2(-FLT_MIN, 0))) {
2299 ImGui::TextDisabled(
"Proposal tools");
2306 ImGui::TextDisabled(
"No proposal selected");
2314 ImGui::TableSetColumnIndex(1);
2315 ImGui::BeginGroup();
2317 ImGui::TextColored(ImVec4(0.6f, 0.78f, 1.0f, 1.0f),
ICON_MD_QUERY_STATS " Live Telemetry");
2321 const float row_height = ImGui::GetTextLineHeightWithSpacing() * 2.0f + 6.0f;
2322 if (ImGui::BeginTable(
"HarnessTelemetryRows", 4,
2323 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
2324 ImGuiTableFlags_SizingStretchProp)) {
2325 ImGui::TableSetupColumn(
"Test", ImGuiTableColumnFlags_WidthStretch, 0.3f);
2326 ImGui::TableSetupColumn(
"Status", ImGuiTableColumnFlags_WidthFixed, 90.0f);
2327 ImGui::TableSetupColumn(
"Message", ImGuiTableColumnFlags_WidthStretch, 0.5f);
2328 ImGui::TableSetupColumn(
"Updated", ImGuiTableColumnFlags_WidthFixed, 120.0f);
2329 ImGui::TableHeadersRow();
2332 ImGui::TableNextRow(ImGuiTableRowFlags_None, row_height);
2333 ImGui::TableSetColumnIndex(0);
2334 ImGui::TextWrapped(
"%s", entry.name.empty() ? entry.test_id.c_str()
2335 : entry.name.c_str());
2336 ImGui::TableSetColumnIndex(1);
2337 const char* status = entry.status.c_str();
2338 ImVec4 status_color = ImVec4(0.8f, 0.8f, 0.8f, 1.0f);
2339 if (absl::EqualsIgnoreCase(status,
"passed")) {
2340 status_color = ImVec4(0.2f, 0.8f, 0.4f, 1.0f);
2341 }
else if (absl::EqualsIgnoreCase(status,
"failed") ||
2342 absl::EqualsIgnoreCase(status,
"timeout")) {
2343 status_color = ImVec4(0.95f, 0.4f, 0.4f, 1.0f);
2344 }
else if (absl::EqualsIgnoreCase(status,
"running")) {
2345 status_color = ImVec4(0.95f, 0.75f, 0.3f, 1.0f);
2347 ImGui::TextColored(status_color,
"%s", status);
2349 ImGui::TableSetColumnIndex(2);
2350 ImGui::TextWrapped(
"%s", entry.message.c_str());
2352 ImGui::TableSetColumnIndex(3);
2353 if (entry.updated_at == absl::InfinitePast()) {
2354 ImGui::TextDisabled(
"-");
2356 const double seconds_ago = absl::ToDoubleSeconds(absl::Now() - entry.updated_at);
2357 ImGui::Text(
"%.0fs ago", seconds_ago);
2363 ImGui::TextDisabled(
"No harness activity recorded yet");
2371 ImGui::PopStyleColor();
2376 ImGui::BeginChild(
"SystemPromptEditor", ImVec2(0, 0),
false);
2381 std::string prompt_v1 =
util::LoadFile(
"assets/agent/system_prompt.txt");
2382 if (!prompt_v1.empty()) {
2386 if (tab.is_system_prompt) {
2387 tab.editor.SetText(prompt_v1);
2389 tab.filename =
"system_prompt_v1.txt (built-in)";
2397 tab.
filename =
"system_prompt_v1.txt (built-in)";
2419 std::string prompt_v2 =
util::LoadFile(
"assets/agent/system_prompt_v2.txt");
2420 if (!prompt_v2.empty()) {
2424 if (tab.is_system_prompt) {
2425 tab.editor.SetText(prompt_v2);
2427 tab.filename =
"system_prompt_v2.txt (built-in)";
2435 tab.
filename =
"system_prompt_v2.txt (built-in)";
2458 if (tab.is_system_prompt) {
2460 "custom_system_prompt",
"txt");
2461 if (!save_path.empty()) {
2462 std::ofstream file(save_path);
2463 if (file.is_open()) {
2464 file << tab.editor.GetText();
2465 tab.filepath = save_path;
2467 tab.modified =
false;
2470 absl::StrFormat(
"System prompt saved to %s", save_path),
2486 tab.
filename =
"custom_system_prompt.txt (unsaved)";
2493 "# Custom System Prompt\n\nEnter your custom system prompt here...\n");
2501 if (!filepath.empty()) {
2502 std::ifstream file(filepath);
2503 if (file.is_open()) {
2506 if (tab.is_system_prompt) {
2507 std::stringstream buffer;
2508 buffer << file.rdbuf();
2509 tab.editor.SetText(buffer.str());
2510 tab.filepath = filepath;
2512 tab.modified =
false;
2525 std::stringstream buffer;
2526 buffer << file.rdbuf();
2545 bool found_prompt =
false;
2548 found_prompt =
true;
2549 ImVec2 editor_size = ImVec2(0, ImGui::GetContentRegionAvail().y);
2550 open_files_[i].editor.Render(
"##SystemPromptEditor", editor_size);
2558 if (!found_prompt) {
2560 "No system prompt loaded. Click 'Load Default' to edit the system "
2568 ImGui::BeginChild(
"FileEditorArea", ImVec2(0, 0),
false);
2572 ImGui::OpenPopup(
"NewFilePopup");
2577 if (!filepath.empty()) {
2583 static char new_filename_buffer[256] = {};
2584 if (ImGui::BeginPopup(
"NewFilePopup")) {
2585 ImGui::Text(
"Create New File");
2587 ImGui::InputText(
"Filename", new_filename_buffer,
2588 sizeof(new_filename_buffer));
2589 if (ImGui::Button(
"Create")) {
2590 if (strlen(new_filename_buffer) > 0) {
2592 memset(new_filename_buffer, 0,
sizeof(new_filename_buffer));
2593 ImGui::CloseCurrentPopup();
2597 if (ImGui::Button(
"Cancel")) {
2598 ImGui::CloseCurrentPopup();
2607 if (ImGui::BeginTabBar(
"FileTabs",
2608 ImGuiTabBarFlags_Reorderable |
2609 ImGuiTabBarFlags_FittingPolicyScroll)) {
2620 if (ImGui::BeginTabItem(tab_label.c_str(), &open)) {
2627 if (file.is_open()) {
2639 if (!save_path.empty()) {
2640 std::ofstream file(save_path);
2641 if (file.is_open()) {
2654 ImGui::TextDisabled(
"%s",
open_files_[i].filepath.empty()
2661 ImVec2 editor_size = ImVec2(0, ImGui::GetContentRegionAvail().y);
2662 open_files_[i].editor.Render(
"##FileEditor", editor_size);
2667 ImGui::EndTabItem();
2683 "No files open. Create a new file or open an existing one.");
2699 std::ifstream file(filepath);
2700 if (!file.is_open()) {
2711 size_t last_slash = filepath.find_last_of(
"/\\");
2712 tab.
filename = (last_slash != std::string::npos)
2713 ? filepath.substr(last_slash + 1)
2718 if (ext ==
"cpp" || ext ==
"cc" || ext ==
"h" || ext ==
"hpp") {
2721 }
else if (ext ==
"c") {
2723 }
else if (ext ==
"lua") {
2727 std::stringstream buffer;
2728 buffer << file.rdbuf();
2746 if (ext ==
"cpp" || ext ==
"cc" || ext ==
"h" || ext ==
"hpp") {
2749 }
else if (ext ==
"c") {
2751 }
else if (ext ==
"lua") {
2785 std::string prompt_path =
2787 std::ifstream file(prompt_path);
2788 if (file.is_open()) {
2792 if (tab.is_system_prompt) {
2793 std::stringstream buffer;
2794 buffer << file.rdbuf();
2795 tab.editor.SetText(buffer.str());
2796 tab.filepath = prompt_path;
2810 std::stringstream buffer;
2811 buffer << file.rdbuf();
2833 if (tab.is_system_prompt && !tab.filepath.empty()) {
2855 return entry.test_id == telemetry.
test_id;
2885 absl::Time now = absl::Now();
2912#if defined(YAZE_WITH_GRPC)
2922 }
catch (
const std::exception& e) {
2949 SDL_Surface* surface = SDL_LoadBMP(image_path.string().c_str());
2959 ImGuiIO& io = ImGui::GetIO();
2960 auto* backend_data =
static_cast<void**
>(io.BackendRendererUserData);
2961 SDL_Renderer* renderer =
nullptr;
2966 renderer = *
reinterpret_cast<SDL_Renderer**
>(backend_data);
2970 SDL_FreeSurface(surface);
2978 SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
2980 SDL_FreeSurface(surface);
2982 toast_manager_->
Show(absl::StrFormat(
"Failed to create texture: %s", SDL_GetError()),
2995 SDL_FreeSurface(surface);
2999 surface->w, surface->h),
3008 SDL_DestroyTexture(texture);
3018 ImGui::TextDisabled(
"No screenshot to preview");
3026 ImGui::TextColored(theme.text_secondary_color,
"%s", filename.c_str());
3036 ImVec2 preview_size(
3043 ImGui::SetNextItemWidth(150);
3045 0.1f, 2.0f,
"Scale: %.1fx");
3048 ImGui::BeginChild(
"PreviewPlaceholder", ImVec2(200, 150),
true);
3049 ImGui::SetCursorPos(ImVec2(60, 60));
3050 ImGui::TextColored(theme.text_secondary_color,
ICON_MD_IMAGE);
3051 ImGui::SetCursorPosX(40);
3052 ImGui::TextWrapped(
"Preview placeholder");
3053 ImGui::TextDisabled(
"(Texture loading not yet implemented)");
3075 ImGuiViewport* viewport = ImGui::GetMainViewport();
3076 ImVec2 viewport_pos = viewport->Pos;
3077 ImVec2 viewport_size = viewport->Size;
3080 ImDrawList* draw_list = ImGui::GetForegroundDrawList();
3081 ImVec2 overlay_min = viewport_pos;
3082 ImVec2 overlay_max = ImVec2(viewport_pos.x + viewport_size.x,
3083 viewport_pos.y + viewport_size.y);
3085 draw_list->AddRectFilled(overlay_min, overlay_max,
3086 IM_COL32(0, 0, 0, 100));
3089 ImGuiIO& io = ImGui::GetIO();
3090 ImVec2 mouse_pos = io.MousePos;
3093 if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) &&
3102 ImGui::IsMouseDown(ImGuiMouseButton_Left)) {
3110 std::min(start.x, end.x),
3111 std::min(start.y, end.y)
3115 std::max(start.x, end.x),
3116 std::max(start.y, end.y)
3123 IM_COL32(100, 180, 255, 255), 0.0f, 0, 2.0f
3132 std::string dimensions = absl::StrFormat(
"%.0f x %.0f", width, height);
3133 ImVec2 label_pos = ImVec2(
3138 draw_list->AddText(label_pos, IM_COL32(255, 255, 255, 255),
3139 dimensions.c_str());
3144 ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
3151 if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
3160 ImVec2 text_pos = ImVec2(viewport_pos.x + 20, viewport_pos.y + 20);
3161 draw_list->AddText(text_pos, IM_COL32(255, 255, 255, 255),
3162 "Drag to select region (ESC to cancel)");
3170 float width = max.x - min.x;
3171 float height = max.y - min.y;
3174 if (width < 10 || height < 10) {
3182 ImGuiIO& io = ImGui::GetIO();
3183 auto* backend_data =
static_cast<void**
>(io.BackendRendererUserData);
3184 SDL_Renderer* renderer =
nullptr;
3187 renderer = *
reinterpret_cast<SDL_Renderer**
>(backend_data);
3199 int full_height = 0;
3200 if (SDL_GetRendererOutputSize(renderer, &full_width, &full_height) != 0) {
3202 toast_manager_->
Show(absl::StrFormat(
"Failed to get renderer size: %s", SDL_GetError()),
3209 int capture_x = std::max(0,
static_cast<int>(min.x));
3210 int capture_y = std::max(0,
static_cast<int>(min.y));
3211 int capture_width = std::min(
static_cast<int>(width), full_width - capture_x);
3212 int capture_height = std::min(
static_cast<int>(height), full_height - capture_y);
3214 if (capture_width <= 0 || capture_height <= 0) {
3222 SDL_Surface* surface = SDL_CreateRGBSurface(0, capture_width, capture_height,
3223 32, 0x00FF0000, 0x0000FF00,
3224 0x000000FF, 0xFF000000);
3227 toast_manager_->
Show(absl::StrFormat(
"Failed to create surface: %s", SDL_GetError()),
3234 SDL_Rect region_rect = {capture_x, capture_y, capture_width, capture_height};
3235 if (SDL_RenderReadPixels(renderer, ®ion_rect, SDL_PIXELFORMAT_ARGB8888,
3236 surface->pixels, surface->pitch) != 0) {
3237 SDL_FreeSurface(surface);
3246 std::filesystem::path screenshot_dir = std::filesystem::temp_directory_path() /
"yaze" /
"screenshots";
3248 std::filesystem::create_directories(screenshot_dir, ec);
3250 const int64_t timestamp_ms = absl::ToUnixMillis(absl::Now());
3251 std::filesystem::path output_path = screenshot_dir /
3252 std::filesystem::path(absl::StrFormat(
"region_%lld.bmp",
static_cast<long long>(timestamp_ms)));
3255 if (SDL_SaveBMP(surface, output_path.string().c_str()) != 0) {
3256 SDL_FreeSurface(surface);
3258 toast_manager_->
Show(absl::StrFormat(
"Failed to save screenshot: %s", SDL_GetError()),
3264 SDL_FreeSurface(surface);
3272 absl::StrFormat(
"Region captured: %dx%d", capture_width, capture_height),
3279 std::filesystem::path captured_path;
void SetText(const std::string &aText)
void SetLanguageDefinition(const LanguageDefinition &aLanguageDef)
The Rom class is used to load, save, and modify Rom data.
core::ResourceLabelManager * resource_label()
absl::StatusOr< ChatMessage > SendMessage(const std::string &message)
void SetRomContext(Rom *rom)
void SetConfig(const AgentConfig &config)
const std::vector< ChatMessage > & GetHistory() const
void ReplaceHistory(std::vector< ChatMessage > history)
static absl::Status Save(const std::filesystem::path &path, const Snapshot &snapshot)
static absl::StatusOr< Snapshot > Load(const std::filesystem::path &path)
ImGui drawer for displaying and managing agent proposals.
void FocusProposal(const std::string &proposal_id)
void Show(const std::string &message, ToastType type=ToastType::kInfo, float ttl_seconds=3.0f)
static TestManager & Get()
static std::string ShowSaveFileDialog(const std::string &default_name="", const std::string &default_extension="")
ShowSaveFileDialog opens a save file dialog and returns the selected filepath. Uses global feature fl...
static std::string ShowOpenFileDialog()
ShowOpenFileDialog opens a file dialog and returns the selected filepath. Uses global feature flag to...
#define ICON_MD_ROCKET_LAUNCH
#define ICON_MD_MEETING_ROOM
#define ICON_MD_FOLDER_OPEN
#define ICON_MD_DATA_OBJECT
#define ICON_MD_NO_PHOTOGRAPHY
#define ICON_MD_CAMERA_ALT
#define ICON_MD_PLAY_ARROW
#define ICON_MD_CLOUD_DONE
#define ICON_MD_DIFFERENCE
#define ICON_MD_VISIBILITY
#define ICON_MD_SETTINGS_ETHERNET
#define ICON_MD_PLAY_CIRCLE
#define ICON_MD_CHECK_CIRCLE
#define ICON_MD_ACCESS_TIME
#define ICON_MD_DASHBOARD
#define ICON_MD_CONTENT_COPY
#define ICON_MD_DELETE_FOREVER
#define ICON_MD_QUERY_STATS
#define ICON_MD_CLOUD_UPLOAD
#define ICON_MD_PHOTO_CAMERA
#define ICON_MD_ADD_CIRCLE
#define ICON_MD_SMART_TOY
const AgentUITheme & GetTheme()
void RenderSectionHeader(const char *icon, const char *label, const ImVec4 &color)
std::string GetFileName(const std::string &filename)
Gets the filename from a full path.
std::string GetFileExtension(const std::string &filename)
Gets the file extension from a filename.
std::string LoadFile(const std::string &filename)
Loads the entire contents of a file into a string.
Main namespace for the application.
static const LanguageDefinition & Lua()
static const LanguageDefinition & C()
static const LanguageDefinition & CPlusPlus()
std::vector< std::string > headers
std::vector< std::vector< std::string > > rows
std::unordered_map< std::string, std::unordered_map< std::string, std::string > > labels_
std::string gemini_api_key
std::string custom_system_prompt
Modern project structure with comprehensive settings consolidation.
std::unordered_map< std::string, std::unordered_map< std::string, std::string > > resource_labels
struct yaze::core::YazeProject::AgentSettings agent_settings
std::string GetRelativePath(const std::string &absolute_path) const
absl::Status InitializeEmbeddedLabels()
std::string GetAbsolutePath(const std::string &relative_path) const
std::vector< cli::agent::ChatMessage > history
CollaborationState collaboration
MultimodalState multimodal
std::vector< std::string > participants