mirror of
https://github.com/NohamR/Reclass.git
synced 2026-05-10 19:59:21 +00:00
feat: shimmer status bar for MCP activity, auto-start MCP, remove "Ready" spam
- Add ShimmerLabel widget with animated glow band for MCP tool activity - Separate app/MCP status channels (setAppStatus/setMcpStatus/clearMcpStatus) - 750ms delayed clear so shimmer stays visible after fast tool calls - MCP auto-starts on launch by default - Remove "Ready" text that was overwriting useful status info - Add statusText field to project.state MCP response
This commit is contained in:
149
src/main.cpp
149
src/main.cpp
@@ -454,7 +454,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) {
|
|||||||
|
|
||||||
// Start MCP bridge
|
// Start MCP bridge
|
||||||
m_mcp = new McpBridge(this, this);
|
m_mcp = new McpBridge(this, this);
|
||||||
if (QSettings("Reclass", "Reclass").value("autoStartMcp", false).toBool())
|
if (QSettings("Reclass", "Reclass").value("autoStartMcp", true).toBool())
|
||||||
m_mcp->start();
|
m_mcp->start();
|
||||||
|
|
||||||
connect(m_mdiArea, &QMdiArea::subWindowActivated,
|
connect(m_mdiArea, &QMdiArea::subWindowActivated,
|
||||||
@@ -526,7 +526,7 @@ void MainWindow::createMenus() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
file->addSeparator();
|
file->addSeparator();
|
||||||
const auto itemName = QSettings("Reclass", "Reclass").value("autoStartMcp", false).toBool() ? "Stop &MCP Server" : "Start &MCP Server";
|
const auto itemName = QSettings("Reclass", "Reclass").value("autoStartMcp", true).toBool() ? "Stop &MCP Server" : "Start &MCP Server";
|
||||||
m_mcpAction = Qt5Qt6AddAction(file, itemName, QKeySequence::UnknownKey, QIcon(), this, &MainWindow::toggleMcp);
|
m_mcpAction = Qt5Qt6AddAction(file, itemName, QKeySequence::UnknownKey, QIcon(), this, &MainWindow::toggleMcp);
|
||||||
file->addSeparator();
|
file->addSeparator();
|
||||||
Qt5Qt6AddAction(file, "&Options...", QKeySequence::UnknownKey, makeIcon(":/vsicons/settings-gear.svg"), this, &MainWindow::showOptionsDialog);
|
Qt5Qt6AddAction(file, "&Options...", QKeySequence::UnknownKey, makeIcon(":/vsicons/settings-gear.svg"), this, &MainWindow::showOptionsDialog);
|
||||||
@@ -716,6 +716,80 @@ protected:
|
|||||||
void leaveEvent(QEvent*) override { update(); }
|
void leaveEvent(QEvent*) override { update(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ── Shimmer label — gradient text sweep for MCP activity ──
|
||||||
|
class ShimmerLabel : public QWidget {
|
||||||
|
public:
|
||||||
|
explicit ShimmerLabel(QWidget* parent = nullptr) : QWidget(parent) {
|
||||||
|
m_timer.setInterval(30);
|
||||||
|
connect(&m_timer, &QTimer::timeout, this, [this]() {
|
||||||
|
m_phase += 0.012f;
|
||||||
|
if (m_phase > 1.0f) m_phase -= 1.0f;
|
||||||
|
update();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void setText(const QString& t) { m_text = t; update(); }
|
||||||
|
QString text() const { return m_text; }
|
||||||
|
|
||||||
|
void setShimmerActive(bool on) {
|
||||||
|
if (m_shimmer == on) return;
|
||||||
|
m_shimmer = on;
|
||||||
|
if (on) { m_phase = 0.0f; m_timer.start(); }
|
||||||
|
else { m_timer.stop(); }
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
bool shimmerActive() const { return m_shimmer; }
|
||||||
|
|
||||||
|
void setAlignment(Qt::Alignment a) { m_align = a; update(); }
|
||||||
|
|
||||||
|
// Colours configurable from theme
|
||||||
|
QColor colBase; // dim text (normal)
|
||||||
|
QColor colBright; // highlight sweep
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent*) override {
|
||||||
|
if (m_text.isEmpty()) return;
|
||||||
|
QPainter p(this);
|
||||||
|
p.setRenderHint(QPainter::TextAntialiasing);
|
||||||
|
p.setFont(font());
|
||||||
|
|
||||||
|
QRect r = contentsRect();
|
||||||
|
|
||||||
|
if (!m_shimmer) {
|
||||||
|
QColor c = colBase.isValid() ? colBase
|
||||||
|
: palette().color(QPalette::WindowText);
|
||||||
|
p.setPen(c);
|
||||||
|
p.drawText(r, m_align, m_text);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shimmer: sweeping glow band behind text + bright text
|
||||||
|
QColor bright = colBright.isValid() ? colBright : QColor(255, 200, 80);
|
||||||
|
|
||||||
|
// 1. Sweeping glow band (semi-transparent background highlight)
|
||||||
|
qreal bandW = width() * 0.20;
|
||||||
|
qreal bandCenter = -bandW + (width() + 2 * bandW) * m_phase;
|
||||||
|
QLinearGradient bgGrad(bandCenter - bandW, 0, bandCenter + bandW, 0);
|
||||||
|
QColor glow = bright;
|
||||||
|
glow.setAlpha(35);
|
||||||
|
bgGrad.setColorAt(0.0, Qt::transparent);
|
||||||
|
bgGrad.setColorAt(0.5, glow);
|
||||||
|
bgGrad.setColorAt(1.0, Qt::transparent);
|
||||||
|
p.fillRect(rect(), QBrush(bgGrad));
|
||||||
|
|
||||||
|
// 2. Text in bright color
|
||||||
|
p.setPen(bright);
|
||||||
|
p.drawText(r, m_align, m_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_text;
|
||||||
|
bool m_shimmer = false;
|
||||||
|
float m_phase = 0.0f;
|
||||||
|
Qt::Alignment m_align = Qt::AlignLeft | Qt::AlignVCenter;
|
||||||
|
QTimer m_timer;
|
||||||
|
};
|
||||||
|
|
||||||
// ── Borderless status bar with manual child layout ──
|
// ── Borderless status bar with manual child layout ──
|
||||||
// QStatusBarLayout hardcodes 2px margins that can't be overridden.
|
// QStatusBarLayout hardcodes 2px margins that can't be overridden.
|
||||||
// We bypass it entirely: children are placed manually in resizeEvent,
|
// We bypass it entirely: children are placed manually in resizeEvent,
|
||||||
@@ -723,8 +797,8 @@ protected:
|
|||||||
// children and call manualLayout() to position them.
|
// children and call manualLayout() to position them.
|
||||||
class FlatStatusBar : public QStatusBar {
|
class FlatStatusBar : public QStatusBar {
|
||||||
public:
|
public:
|
||||||
QWidget* tabRow = nullptr; // set by createStatusBar
|
QWidget* tabRow = nullptr; // set by createStatusBar
|
||||||
QLabel* label = nullptr; // set by createStatusBar
|
ShimmerLabel* label = nullptr; // set by createStatusBar
|
||||||
|
|
||||||
void setDividerColor(const QColor& c) { m_div = c; update(); }
|
void setDividerColor(const QColor& c) { m_div = c; update(); }
|
||||||
void setTopLineColor(const QColor& c) { m_top = c; update(); }
|
void setTopLineColor(const QColor& c) { m_top = c; update(); }
|
||||||
@@ -802,7 +876,8 @@ void MainWindow::createStatusBar() {
|
|||||||
auto* sb = new FlatStatusBar;
|
auto* sb = new FlatStatusBar;
|
||||||
setStatusBar(sb);
|
setStatusBar(sb);
|
||||||
|
|
||||||
m_statusLabel = new QLabel("Ready", sb);
|
m_statusLabel = new ShimmerLabel(sb);
|
||||||
|
m_statusLabel->setText("");
|
||||||
m_statusLabel->setContentsMargins(0, 0, 0, 0);
|
m_statusLabel->setContentsMargins(0, 0, 0, 0);
|
||||||
m_statusLabel->setAlignment(Qt::AlignVCenter | Qt::AlignLeft);
|
m_statusLabel->setAlignment(Qt::AlignVCenter | Qt::AlignLeft);
|
||||||
|
|
||||||
@@ -865,10 +940,42 @@ void MainWindow::createStatusBar() {
|
|||||||
};
|
};
|
||||||
applyViewTabColors(static_cast<ViewTabButton*>(m_btnReclass));
|
applyViewTabColors(static_cast<ViewTabButton*>(m_btnReclass));
|
||||||
applyViewTabColors(static_cast<ViewTabButton*>(m_btnRendered));
|
applyViewTabColors(static_cast<ViewTabButton*>(m_btnRendered));
|
||||||
|
|
||||||
|
m_statusLabel->colBase = t.textDim;
|
||||||
|
m_statusLabel->colBright = t.indHoverSpan;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::setAppStatus(const QString& text) {
|
||||||
|
m_appStatus = text;
|
||||||
|
if (!m_mcpBusy) {
|
||||||
|
m_statusLabel->setText(text);
|
||||||
|
m_statusLabel->setShimmerActive(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::setMcpStatus(const QString& text) {
|
||||||
|
// Cancel any pending clear — new activity extends the shimmer
|
||||||
|
if (m_mcpClearTimer) m_mcpClearTimer->stop();
|
||||||
|
m_mcpBusy = true;
|
||||||
|
m_statusLabel->setText(text);
|
||||||
|
m_statusLabel->setShimmerActive(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::clearMcpStatus() {
|
||||||
|
// Delay the clear so the shimmer stays visible for at least 750ms
|
||||||
|
if (!m_mcpClearTimer) {
|
||||||
|
m_mcpClearTimer = new QTimer(this);
|
||||||
|
m_mcpClearTimer->setSingleShot(true);
|
||||||
|
connect(m_mcpClearTimer, &QTimer::timeout, this, [this]() {
|
||||||
|
m_mcpBusy = false;
|
||||||
|
m_statusLabel->setText(m_appStatus);
|
||||||
|
m_statusLabel->setShimmerActive(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
m_mcpClearTimer->start(750);
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::styleTabCloseButtons() {
|
void MainWindow::styleTabCloseButtons() {
|
||||||
auto* tabBar = m_mdiArea->findChild<QTabBar*>();
|
auto* tabBar = m_mdiArea->findChild<QTabBar*>();
|
||||||
@@ -1033,19 +1140,17 @@ QMdiSubWindow* MainWindow::createTab(RcxDocument* doc) {
|
|||||||
auto& node = ctrl->document()->tree.nodes[nodeIdx];
|
auto& node = ctrl->document()->tree.nodes[nodeIdx];
|
||||||
auto* ap = findActiveSplitPane();
|
auto* ap = findActiveSplitPane();
|
||||||
if (ap && ap->viewMode == VM_Rendered)
|
if (ap && ap->viewMode == VM_Rendered)
|
||||||
m_statusLabel->setText(
|
setAppStatus(
|
||||||
QString("Rendered: %1 %2")
|
QString("Rendered: %1 %2")
|
||||||
.arg(kindToString(node.kind))
|
.arg(kindToString(node.kind))
|
||||||
.arg(node.name));
|
.arg(node.name));
|
||||||
else
|
else
|
||||||
m_statusLabel->setText(
|
setAppStatus(
|
||||||
QString("%1 %2 offset: 0x%3 size: %4 bytes")
|
QString("%1 %2 offset: 0x%3 size: %4 bytes")
|
||||||
.arg(kindToString(node.kind))
|
.arg(kindToString(node.kind))
|
||||||
.arg(node.name)
|
.arg(node.name)
|
||||||
.arg(node.offset, 4, 16, QChar('0'))
|
.arg(node.offset, 4, 16, QChar('0'))
|
||||||
.arg(node.byteSize()));
|
.arg(node.byteSize()));
|
||||||
} else {
|
|
||||||
m_statusLabel->setText("Ready");
|
|
||||||
}
|
}
|
||||||
// Update all rendered panes on selection change
|
// Update all rendered panes on selection change
|
||||||
auto it = m_tabs.find(sub);
|
auto it = m_tabs.find(sub);
|
||||||
@@ -1054,10 +1159,8 @@ QMdiSubWindow* MainWindow::createTab(RcxDocument* doc) {
|
|||||||
});
|
});
|
||||||
connect(ctrl, &RcxController::selectionChanged,
|
connect(ctrl, &RcxController::selectionChanged,
|
||||||
this, [this](int count) {
|
this, [this](int count) {
|
||||||
if (count == 0)
|
if (count > 1)
|
||||||
m_statusLabel->setText("Ready");
|
setAppStatus(QString("%1 nodes selected").arg(count));
|
||||||
else if (count > 1)
|
|
||||||
m_statusLabel->setText(QString("%1 nodes selected").arg(count));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update rendered panes and workspace on document changes and undo/redo
|
// Update rendered panes and workspace on document changes and undo/redo
|
||||||
@@ -1524,11 +1627,11 @@ void MainWindow::toggleMcp() {
|
|||||||
if (m_mcp->isRunning()) {
|
if (m_mcp->isRunning()) {
|
||||||
m_mcp->stop();
|
m_mcp->stop();
|
||||||
m_mcpAction->setText("Start &MCP Server");
|
m_mcpAction->setText("Start &MCP Server");
|
||||||
m_statusLabel->setText("MCP server stopped");
|
setAppStatus("MCP server stopped");
|
||||||
} else {
|
} else {
|
||||||
m_mcp->start();
|
m_mcp->start();
|
||||||
m_mcpAction->setText("Stop &MCP Server");
|
m_mcpAction->setText("Stop &MCP Server");
|
||||||
m_statusLabel->setText("MCP server listening on pipe: ReclassMcpBridge");
|
setAppStatus("MCP server listening on pipe: ReclassMcpBridge");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1944,7 +2047,7 @@ void MainWindow::exportCpp() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
file.write(text.toUtf8());
|
file.write(text.toUtf8());
|
||||||
m_statusLabel->setText("Exported to " + QFileInfo(path).fileName());
|
setAppStatus("Exported to " + QFileInfo(path).fileName());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Export ReClass XML ──
|
// ── Export ReClass XML ──
|
||||||
@@ -1968,7 +2071,7 @@ void MainWindow::exportReclassXmlAction() {
|
|||||||
for (const auto& n : tab->doc->tree.nodes)
|
for (const auto& n : tab->doc->tree.nodes)
|
||||||
if (n.parentId == 0 && n.kind == NodeKind::Struct) classCount++;
|
if (n.parentId == 0 && n.kind == NodeKind::Struct) classCount++;
|
||||||
|
|
||||||
m_statusLabel->setText(QStringLiteral("Exported %1 classes to %2")
|
setAppStatus(QStringLiteral("Exported %1 classes to %2")
|
||||||
.arg(classCount).arg(QFileInfo(path).fileName()));
|
.arg(classCount).arg(QFileInfo(path).fileName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1999,7 +2102,7 @@ void MainWindow::importReclassXml() {
|
|||||||
m_mdiArea->closeAllSubWindows();
|
m_mdiArea->closeAllSubWindows();
|
||||||
createTab(doc);
|
createTab(doc);
|
||||||
rebuildWorkspaceModel();
|
rebuildWorkspaceModel();
|
||||||
m_statusLabel->setText(QStringLiteral("Imported %1 classes from %2")
|
setAppStatus(QStringLiteral("Imported %1 classes from %2")
|
||||||
.arg(classCount).arg(QFileInfo(filePath).fileName()));
|
.arg(classCount).arg(QFileInfo(filePath).fileName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2049,7 +2152,7 @@ void MainWindow::importFromSource() {
|
|||||||
createTab(doc);
|
createTab(doc);
|
||||||
rebuildWorkspaceModel();
|
rebuildWorkspaceModel();
|
||||||
m_workspaceDock->show();
|
m_workspaceDock->show();
|
||||||
m_statusLabel->setText(QStringLiteral("Imported %1 classes from source").arg(classCount));
|
setAppStatus(QStringLiteral("Imported %1 classes from source").arg(classCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Import PDB ──
|
// ── Import PDB ──
|
||||||
@@ -2099,7 +2202,7 @@ void MainWindow::importPdb() {
|
|||||||
createTab(doc);
|
createTab(doc);
|
||||||
rebuildWorkspaceModel();
|
rebuildWorkspaceModel();
|
||||||
m_workspaceDock->show();
|
m_workspaceDock->show();
|
||||||
m_statusLabel->setText(QStringLiteral("Imported %1 classes from %2")
|
setAppStatus(QStringLiteral("Imported %1 classes from %2")
|
||||||
.arg(classCount).arg(QFileInfo(pdbPath).fileName()));
|
.arg(classCount).arg(QFileInfo(pdbPath).fileName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2232,7 +2335,7 @@ QMdiSubWindow* MainWindow::project_open(const QString& path) {
|
|||||||
int classCount = 0;
|
int classCount = 0;
|
||||||
for (const auto& n : doc->tree.nodes)
|
for (const auto& n : doc->tree.nodes)
|
||||||
if (n.parentId == 0 && n.kind == NodeKind::Struct) classCount++;
|
if (n.parentId == 0 && n.kind == NodeKind::Struct) classCount++;
|
||||||
m_statusLabel->setText(QStringLiteral("Imported %1 classes from %2")
|
setAppStatus(QStringLiteral("Imported %1 classes from %2")
|
||||||
.arg(classCount).arg(QFileInfo(filePath).fileName()));
|
.arg(classCount).arg(QFileInfo(filePath).fileName()));
|
||||||
return sub;
|
return sub;
|
||||||
}
|
}
|
||||||
@@ -2610,7 +2713,7 @@ void MainWindow::showPluginsDialog() {
|
|||||||
if (!path.isEmpty()) {
|
if (!path.isEmpty()) {
|
||||||
if (m_pluginManager.LoadPluginFromPath(path)) {
|
if (m_pluginManager.LoadPluginFromPath(path)) {
|
||||||
refreshList();
|
refreshList();
|
||||||
m_statusLabel->setText("Plugin loaded successfully");
|
setAppStatus("Plugin loaded successfully");
|
||||||
} else {
|
} else {
|
||||||
QMessageBox::warning(&dialog, "Failed to Load Plugin",
|
QMessageBox::warning(&dialog, "Failed to Load Plugin",
|
||||||
"Could not load the selected plugin.\nCheck the console for details.");
|
"Could not load the selected plugin.\nCheck the console for details.");
|
||||||
@@ -2636,7 +2739,7 @@ void MainWindow::showPluginsDialog() {
|
|||||||
if (reply == QMessageBox::Yes) {
|
if (reply == QMessageBox::Yes) {
|
||||||
if (m_pluginManager.UnloadPlugin(pluginName)) {
|
if (m_pluginManager.UnloadPlugin(pluginName)) {
|
||||||
refreshList();
|
refreshList();
|
||||||
m_statusLabel->setText("Plugin unloaded");
|
setAppStatus("Plugin unloaded");
|
||||||
} else {
|
} else {
|
||||||
QMessageBox::warning(&dialog, "Failed to Unload",
|
QMessageBox::warning(&dialog, "Failed to Unload",
|
||||||
"Could not unload the selected plugin.");
|
"Could not unload the selected plugin.");
|
||||||
|
|||||||
@@ -14,11 +14,13 @@
|
|||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QButtonGroup>
|
#include <QButtonGroup>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
#include <QTimer>
|
||||||
#include <Qsci/qsciscintilla.h>
|
#include <Qsci/qsciscintilla.h>
|
||||||
|
|
||||||
namespace rcx {
|
namespace rcx {
|
||||||
|
|
||||||
class McpBridge;
|
class McpBridge;
|
||||||
|
class ShimmerLabel;
|
||||||
|
|
||||||
class MainWindow : public QMainWindow {
|
class MainWindow : public QMainWindow {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -59,6 +61,11 @@ private slots:
|
|||||||
void showOptionsDialog();
|
void showOptionsDialog();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// Status bar helpers — separate app / MCP channels
|
||||||
|
void setAppStatus(const QString& text);
|
||||||
|
void setMcpStatus(const QString& text);
|
||||||
|
void clearMcpStatus();
|
||||||
|
|
||||||
// Project Lifecycle API
|
// Project Lifecycle API
|
||||||
QMdiSubWindow* project_new(const QString& classKeyword = QString());
|
QMdiSubWindow* project_new(const QString& classKeyword = QString());
|
||||||
QMdiSubWindow* project_open(const QString& path = {});
|
QMdiSubWindow* project_open(const QString& path = {});
|
||||||
@@ -69,7 +76,10 @@ private:
|
|||||||
enum ViewMode { VM_Reclass, VM_Rendered };
|
enum ViewMode { VM_Reclass, VM_Rendered };
|
||||||
|
|
||||||
QMdiArea* m_mdiArea;
|
QMdiArea* m_mdiArea;
|
||||||
QLabel* m_statusLabel;
|
ShimmerLabel* m_statusLabel;
|
||||||
|
QString m_appStatus;
|
||||||
|
bool m_mcpBusy = false;
|
||||||
|
QTimer* m_mcpClearTimer = nullptr;
|
||||||
QButtonGroup* m_viewBtnGroup = nullptr;
|
QButtonGroup* m_viewBtnGroup = nullptr;
|
||||||
QPushButton* m_btnReclass = nullptr;
|
QPushButton* m_btnReclass = nullptr;
|
||||||
QPushButton* m_btnRendered = nullptr;
|
QPushButton* m_btnRendered = nullptr;
|
||||||
|
|||||||
@@ -170,12 +170,15 @@ void McpBridge::processLine(const QByteArray& line) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (method == "initialize") {
|
if (method == "initialize") {
|
||||||
m_mainWindow->m_statusLabel->setText(QStringLiteral("MCP: client connected"));
|
m_mainWindow->setMcpStatus(QStringLiteral("MCP: client connected"));
|
||||||
|
QCoreApplication::processEvents();
|
||||||
sendJson(handleInitialize(id, req.value("params").toObject()));
|
sendJson(handleInitialize(id, req.value("params").toObject()));
|
||||||
|
m_mainWindow->clearMcpStatus();
|
||||||
} else if (method == "tools/list") {
|
} else if (method == "tools/list") {
|
||||||
m_mainWindow->m_statusLabel->setText(QStringLiteral("MCP: tools/list"));
|
m_mainWindow->setMcpStatus(QStringLiteral("MCP: tools/list"));
|
||||||
|
QCoreApplication::processEvents();
|
||||||
sendJson(handleToolsList(id));
|
sendJson(handleToolsList(id));
|
||||||
m_mainWindow->m_statusLabel->setText(QStringLiteral("Ready"));
|
m_mainWindow->clearMcpStatus();
|
||||||
} else if (method == "tools/call") {
|
} else if (method == "tools/call") {
|
||||||
sendJson(handleToolsCall(id, req.value("params").toObject()));
|
sendJson(handleToolsCall(id, req.value("params").toObject()));
|
||||||
} else {
|
} else {
|
||||||
@@ -403,8 +406,8 @@ QJsonObject McpBridge::handleToolsCall(const QJsonValue& id, const QJsonObject&
|
|||||||
QString toolName = params.value("name").toString();
|
QString toolName = params.value("name").toString();
|
||||||
QJsonObject args = params.value("arguments").toObject();
|
QJsonObject args = params.value("arguments").toObject();
|
||||||
|
|
||||||
// Show tool activity in status bar
|
// Show tool activity in status bar (with shimmer)
|
||||||
m_mainWindow->m_statusLabel->setText(QStringLiteral("MCP: %1").arg(toolName));
|
m_mainWindow->setMcpStatus(QStringLiteral("MCP: %1").arg(toolName));
|
||||||
QCoreApplication::processEvents(); // paint immediately
|
QCoreApplication::processEvents(); // paint immediately
|
||||||
|
|
||||||
QJsonObject result;
|
QJsonObject result;
|
||||||
@@ -418,7 +421,7 @@ QJsonObject McpBridge::handleToolsCall(const QJsonValue& id, const QJsonObject&
|
|||||||
else if (toolName == "tree.search") result = toolTreeSearch(args);
|
else if (toolName == "tree.search") result = toolTreeSearch(args);
|
||||||
else return errReply(id, -32601, "Unknown tool: " + toolName);
|
else return errReply(id, -32601, "Unknown tool: " + toolName);
|
||||||
|
|
||||||
m_mainWindow->m_statusLabel->setText(QStringLiteral("Ready"));
|
m_mainWindow->clearMcpStatus();
|
||||||
|
|
||||||
return okReply(id, result);
|
return okReply(id, result);
|
||||||
}
|
}
|
||||||
@@ -526,6 +529,7 @@ QJsonObject McpBridge::toolProjectState(const QJsonObject& args) {
|
|||||||
state["modified"] = doc->modified;
|
state["modified"] = doc->modified;
|
||||||
state["undoAvailable"] = doc->undoStack.canUndo();
|
state["undoAvailable"] = doc->undoStack.canUndo();
|
||||||
state["redoAvailable"] = doc->undoStack.canRedo();
|
state["redoAvailable"] = doc->undoStack.canRedo();
|
||||||
|
state["statusText"] = m_mainWindow->m_appStatus;
|
||||||
|
|
||||||
// Filtered tree: only emit nodes up to maxDepth from the filter root
|
// Filtered tree: only emit nodes up to maxDepth from the filter root
|
||||||
if (includeTree) {
|
if (includeTree) {
|
||||||
@@ -1042,7 +1046,7 @@ QJsonObject McpBridge::toolStatusSet(const QJsonObject& args) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (target == "statusBar" || target == "both") {
|
if (target == "statusBar" || target == "both") {
|
||||||
m_mainWindow->m_statusLabel->setText(text);
|
m_mainWindow->setAppStatus(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
return makeTextResult("Status set: " + text);
|
return makeTextResult("Status set: " + text);
|
||||||
|
|||||||
Reference in New Issue
Block a user