Sprint 37: architecture refactor pass
This commit is contained in:
@@ -1,15 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "../EditorState.h"
|
||||
#include "../EditorUtils.h"
|
||||
#include "../AgentTaskStatusOverlay.h"
|
||||
#include "../ThemeEngine.h"
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <psapi.h>
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
static std::string detectGitBranch(const std::string& root) {
|
||||
@@ -17,12 +21,11 @@ static std::string detectGitBranch(const std::string& root) {
|
||||
std::filesystem::path headPath = std::filesystem::path(root) / ".git" / "HEAD";
|
||||
std::ifstream in(headPath.string());
|
||||
if (!in.is_open()) return "-";
|
||||
|
||||
std::string line;
|
||||
std::getline(in, line);
|
||||
const std::string refPrefix = "ref: refs/heads/";
|
||||
if (line.rfind(refPrefix, 0) == 0) {
|
||||
return line.substr(refPrefix.size());
|
||||
}
|
||||
if (line.rfind(refPrefix, 0) == 0) return line.substr(refPrefix.size());
|
||||
if (!line.empty()) return line.substr(0, 8);
|
||||
return "-";
|
||||
}
|
||||
@@ -56,49 +59,39 @@ static size_t processMemoryBytes() {
|
||||
static std::string formatMemory(size_t bytes) {
|
||||
if (bytes == 0) return "-";
|
||||
const double mb = 1024.0 * 1024.0;
|
||||
double value = bytes / mb;
|
||||
char buf[64];
|
||||
std::snprintf(buf, sizeof(buf), "%.0f MB", value);
|
||||
std::snprintf(buf, sizeof(buf), "%.0f MB", bytes / mb);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void renderStatusBar(EditorState& state) {
|
||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
ImGuiWindowFlags sbFlags = ImGuiWindowFlags_NoDecoration |
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar |
|
||||
ImGuiWindowFlags_NoSavedSettings;
|
||||
float sbHeight = ImGui::GetFrameHeight() + 2;
|
||||
ImVec2 sbPos(viewport->WorkPos.x, viewport->WorkPos.y + viewport->WorkSize.y - sbHeight);
|
||||
ImVec2 sbSize(viewport->WorkSize.x, sbHeight);
|
||||
ImGui::SetNextWindowPos(sbPos);
|
||||
ImGui::SetNextWindowSize(sbSize);
|
||||
ImVec4 bg = ImVec4(0.10f, 0.12f, 0.16f, 1.0f);
|
||||
static ImVec4 statusBarBackgroundColor(const EditorState& state) {
|
||||
bool hasError = false;
|
||||
for (const auto& d : state.whetstoneDiagnostics) {
|
||||
if (d.severity == 1) { hasError = true; break; }
|
||||
if (d.severity == 1) {
|
||||
hasError = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasError) {
|
||||
for (const auto& d : state.emacsDiagnostics) {
|
||||
if (d.severity == 1) { hasError = true; break; }
|
||||
if (d.severity == 1) {
|
||||
hasError = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state.build.runInProgress) {
|
||||
bg = ImVec4(0.10f, 0.35f, 0.25f, 1.0f);
|
||||
} else if (hasError) {
|
||||
bg = ImVec4(0.45f, 0.12f, 0.12f, 1.0f);
|
||||
}
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, bg);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8, 2));
|
||||
ImGui::Begin("##StatusBar", nullptr, sbFlags);
|
||||
ImGui::PushFont(state.uiFont);
|
||||
|
||||
// Left cluster: mode, language, encoding, line ending
|
||||
if (state.build.runInProgress) return ImVec4(0.10f, 0.35f, 0.25f, 1.0f);
|
||||
if (hasError) return ImVec4(0.45f, 0.12f, 0.12f, 1.0f);
|
||||
return ImVec4(0.10f, 0.12f, 0.16f, 1.0f);
|
||||
}
|
||||
|
||||
static void renderStatusBarLeftCluster(EditorState& state) {
|
||||
std::string modeLabel = state.active()
|
||||
? (state.active()->bufferMode == BufferManager::BufferMode::Text ? "Text Mode" : "Structured Mode")
|
||||
: "-";
|
||||
if (ImGui::Button(modeLabel.c_str())) {
|
||||
toggleBufferMode(state);
|
||||
}
|
||||
if (ImGui::Button(modeLabel.c_str())) toggleBufferMode(state);
|
||||
|
||||
if (state.active() &&
|
||||
state.active()->bufferMode == BufferManager::BufferMode::Text &&
|
||||
state.active()->fileSizeBytes > 0) {
|
||||
@@ -107,19 +100,19 @@ static void renderStatusBar(EditorState& state) {
|
||||
state.setActiveBufferMode(BufferManager::BufferMode::Structured);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine(0, 10);
|
||||
const char* langLabel = state.active() ? state.active()->language.c_str() : "-";
|
||||
if (ImGui::Button(langLabel)) {
|
||||
ImGui::OpenPopup("##langPopup");
|
||||
}
|
||||
if (ImGui::Button(langLabel)) ImGui::OpenPopup("##langPopup");
|
||||
if (ImGui::BeginPopup("##langPopup")) {
|
||||
const char* langs[] = {"python","cpp","elisp","javascript","typescript","java","rust","go","org"};
|
||||
const char* labels[] = {"Python","C++","Elisp","JavaScript","TypeScript","Java","Rust","Go","Org"};
|
||||
const char* langs[] = {"python", "cpp", "elisp", "javascript", "typescript", "java", "rust", "go", "org"};
|
||||
const char* labels[] = {"Python", "C++", "Elisp", "JavaScript", "TypeScript", "Java", "Rust", "Go", "Org"};
|
||||
for (int i = 0; i < IM_ARRAYSIZE(langs); ++i) {
|
||||
if (ImGui::MenuItem(labels[i])) state.setLanguage(langs[i]);
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::SameLine(0, 10);
|
||||
if (ImGui::Button("UTF-8")) {
|
||||
state.notify(NotificationLevel::Info, "Encoding settings not yet configurable.");
|
||||
@@ -129,48 +122,45 @@ static void renderStatusBar(EditorState& state) {
|
||||
if (ImGui::Button(lineEnding.c_str())) {
|
||||
state.notify(NotificationLevel::Info, "Line ending toggle not yet implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
// Center: notifications
|
||||
static void renderStatusBarCenter(EditorState& state, float windowWidth) {
|
||||
std::string centerText = "(no notifications)";
|
||||
if (const Notification* n = state.notifications.latest()) {
|
||||
centerText = n->message;
|
||||
}
|
||||
if (const Notification* n = state.notifications.latest()) centerText = n->message;
|
||||
|
||||
float centerWidth = ImGui::CalcTextSize(centerText.c_str()).x;
|
||||
float windowWidth = ImGui::GetWindowWidth();
|
||||
float centerX = std::max(0.0f, (windowWidth * 0.5f) - (centerWidth * 0.5f));
|
||||
ImGui::SetCursorPosX(centerX);
|
||||
ImGui::TextUnformatted(centerText.c_str());
|
||||
if (ImGui::IsItemClicked()) {
|
||||
state.notifications.showHistory = !state.notifications.showHistory;
|
||||
}
|
||||
}
|
||||
|
||||
// Recording indicator
|
||||
static void renderRecordingAndAgentIndicator(EditorState& state) {
|
||||
ImGui::SameLine(0, 10);
|
||||
bool recording = state.agent.workflowRecorder.isRecording();
|
||||
const bool recording = state.agent.workflowRecorder.isRecording();
|
||||
if (recording) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.75f, 0.15f, 0.15f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.85f, 0.20f, 0.20f, 1.0f));
|
||||
}
|
||||
if (ImGui::Button("REC")) {
|
||||
state.toggleSessionRecording();
|
||||
}
|
||||
if (ImGui::Button("REC")) state.toggleSessionRecording();
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(recording ? "Session recording active" : "Start session recording");
|
||||
}
|
||||
if (recording) {
|
||||
ImGui::PopStyleColor(2);
|
||||
}
|
||||
if (recording) ImGui::PopStyleColor(2);
|
||||
|
||||
AgentTaskStatusSummary taskSummary =
|
||||
AgentTaskStatusOverlay::buildSummary(state.agent.taskSlots);
|
||||
if (taskSummary.hasWork) {
|
||||
ImGui::SameLine(0, 10);
|
||||
if (ImGui::Button(taskSummary.label.c_str())) {
|
||||
state.ui.showAgentChatPanel = true;
|
||||
}
|
||||
}
|
||||
if (!taskSummary.hasWork) return;
|
||||
|
||||
// Right cluster: Ln/Col, selection, zoom, git
|
||||
ImGui::SameLine(0, 10);
|
||||
if (ImGui::Button(taskSummary.label.c_str())) {
|
||||
state.ui.showAgentChatPanel = true;
|
||||
}
|
||||
}
|
||||
|
||||
static std::string buildStatusBarRightText(EditorState& state) {
|
||||
int selStart = -1;
|
||||
int selEnd = -1;
|
||||
int selChars = 0;
|
||||
@@ -185,31 +175,54 @@ static void renderStatusBar(EditorState& state) {
|
||||
}
|
||||
}
|
||||
}
|
||||
std::string rightText;
|
||||
if (state.active()) {
|
||||
rightText = "Ln " + std::to_string(state.active()->cursorLine) +
|
||||
", Col " + std::to_string(state.active()->cursorCol);
|
||||
} else {
|
||||
rightText = "Ln -, Col -";
|
||||
}
|
||||
if (selChars > 0) {
|
||||
rightText += " | Sel " + std::to_string(selChars) + "c " +
|
||||
std::to_string(selLines) + "l";
|
||||
}
|
||||
if (state.active() && state.active()->largeFileMode) {
|
||||
rightText += " | Large File";
|
||||
}
|
||||
size_t memBytes = processMemoryBytes();
|
||||
if (memBytes > 0) {
|
||||
rightText += " | Mem " + formatMemory(memBytes);
|
||||
}
|
||||
rightText += " | Zoom " + std::to_string(zoomPercent(state.settings.getFontSize(),
|
||||
state.baseFontSize)) + "%";
|
||||
rightText += " | Git " + detectGitBranch(state.workspaceRoot);
|
||||
|
||||
std::string rightText = state.active()
|
||||
? ("Ln " + std::to_string(state.active()->cursorLine) +
|
||||
", Col " + std::to_string(state.active()->cursorCol))
|
||||
: "Ln -, Col -";
|
||||
if (selChars > 0) {
|
||||
rightText += " | Sel " + std::to_string(selChars) + "c " + std::to_string(selLines) + "l";
|
||||
}
|
||||
if (state.active() && state.active()->largeFileMode) rightText += " | Large File";
|
||||
|
||||
size_t memBytes = processMemoryBytes();
|
||||
if (memBytes > 0) rightText += " | Mem " + formatMemory(memBytes);
|
||||
|
||||
rightText += " | Zoom " + std::to_string(zoomPercent(state.settings.getFontSize(),
|
||||
state.baseFontSize)) + "%";
|
||||
rightText += " | Git " + detectGitBranch(state.workspaceRoot);
|
||||
return rightText;
|
||||
}
|
||||
|
||||
static void renderStatusBarRight(EditorState& state, float windowWidth) {
|
||||
std::string rightText = buildStatusBarRightText(state);
|
||||
float rightWidth = ImGui::CalcTextSize(rightText.c_str()).x;
|
||||
ImGui::SetCursorPosX(std::max(0.0f, windowWidth - rightWidth - 8.0f));
|
||||
ImGui::TextUnformatted(rightText.c_str());
|
||||
}
|
||||
|
||||
static void renderStatusBar(EditorState& state) {
|
||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
ImGuiWindowFlags sbFlags = ImGuiWindowFlags_NoDecoration |
|
||||
ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoScrollbar |
|
||||
ImGuiWindowFlags_NoSavedSettings;
|
||||
const float sbHeight = ImGui::GetFrameHeight() + 2;
|
||||
const ImVec2 sbPos(viewport->WorkPos.x, viewport->WorkPos.y + viewport->WorkSize.y - sbHeight);
|
||||
const ImVec2 sbSize(viewport->WorkSize.x, sbHeight);
|
||||
|
||||
ImGui::SetNextWindowPos(sbPos);
|
||||
ImGui::SetNextWindowSize(sbSize);
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, statusBarBackgroundColor(state));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8, 2));
|
||||
ImGui::Begin("##StatusBar", nullptr, sbFlags);
|
||||
ImGui::PushFont(state.uiFont);
|
||||
|
||||
const float windowWidth = ImGui::GetWindowWidth();
|
||||
renderStatusBarLeftCluster(state);
|
||||
renderStatusBarCenter(state, windowWidth);
|
||||
renderRecordingAndAgentIndicator(state);
|
||||
renderStatusBarRight(state, windowWidth);
|
||||
|
||||
ImGui::PopFont();
|
||||
ImGui::End();
|
||||
|
||||
Reference in New Issue
Block a user