mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-04 09:54:13 +02:00
Live mode: an AI assistant acts on the open project while the user watches
A new setting, Configurer QElectroTech > Général > "Autoriser un assistant IA à agir sur le projet ouvert", off by default. Off, nothing changes: no channel is opened. On, every start shows a warning first -- Continuer, Pas pour cette session, or Désactiver -- and nothing can connect until the user answers Continuer. It waits for any other start-up question to be answered, so the two never stack. Accepted, LiveServer opens a local socket only the user's account can use, with a random name and token written to live-session.json in the data folder for the qet MCP server, and removed when the channel closes. One JSON request per line: status (project, folio on screen, selection, last undo step, stored scripts), run_script and run_stored. Requests are queued out of the socket handler before they run (the lesson of PR #861). Each run is one undo step named "Assistant : <name>". The status bar shows the mode and the assistant's last action with a ✓ or ✗ and the time, and an Arrêter button that closes the channel for the session. Unticking the setting closes it at once. QetScripting::runSource() runs script text and, for a live run, returns what qet.log() wrote, the error with its line and the undo step instead of showing boxes; qet.showMessage() is logged rather than opening a box nobody asked for. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
#include "scripting/qetscripting.h"
|
||||
#include "scripting/scriptlibrary.h"
|
||||
#include "scripting/scriptmanagerdialog.h"
|
||||
#include "scripting/liveserver.h"
|
||||
#endif
|
||||
#include <QCoreApplication>
|
||||
#include <QToolButton>
|
||||
@@ -85,6 +86,11 @@
|
||||
#include <QDir>
|
||||
#include <QDesktopServices>
|
||||
#include <QTimer>
|
||||
#include <QJsonObject>
|
||||
#include <QTime>
|
||||
#include <QStatusBar>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QUrl>
|
||||
#include <algorithm>
|
||||
#ifdef BUILD_WITHOUT_KF
|
||||
@@ -167,6 +173,10 @@ QETDiagramEditor::QETDiagramEditor(const QStringList &files, QWidget *parent) :
|
||||
show();
|
||||
readSettingsState(); // restoreState() must be called after show() in Qt6
|
||||
#ifdef QET_HAS_SCRIPTING
|
||||
setUpLiveIndicator();
|
||||
//Live mode asks once per run, from the first window to open, and
|
||||
//only once that window is on screen to anchor its warning.
|
||||
QTimer::singleShot(0, this, [this]() { LiveServer::instance().askAndStart(this); });
|
||||
//A toolbar saved as shown while there were scripts stays out of
|
||||
//the way while there are none.
|
||||
if (m_script_actions.isEmpty()) m_scripts_tool_bar->hide();
|
||||
@@ -3898,6 +3908,53 @@ void QETDiagramEditor::rebuildScriptActions()
|
||||
updateScriptActions();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QETDiagramEditor::setUpLiveIndicator
|
||||
"Assistant connecté" and a Stop button on the status bar, shown
|
||||
whenever live mode is open, so it is never on without being seen.
|
||||
*/
|
||||
void QETDiagramEditor::setUpLiveIndicator()
|
||||
{
|
||||
auto *box = new QWidget(this);
|
||||
auto *layout = new QHBoxLayout(box);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
auto *label = new QLabel(box);
|
||||
auto *stop = new QToolButton(box);
|
||||
stop->setText(tr("Arrêter"));
|
||||
stop->setToolTip(tr("Couper la connexion de l'assistant pour le reste de la session"));
|
||||
layout->addWidget(label);
|
||||
layout->addWidget(stop);
|
||||
statusBar()->addPermanentWidget(box);
|
||||
connect(stop, &QToolButton::clicked, this, []() { LiveServer::instance().stop(); });
|
||||
|
||||
auto update = [box, label](LiveServer::State state) {
|
||||
box->setVisible(state != LiveServer::Off);
|
||||
label->setText(state == LiveServer::Connected
|
||||
? tr("Mode direct : assistant connecté")
|
||||
: tr("Mode direct : en attente d'un assistant"));
|
||||
label->setStyleSheet(state == LiveServer::Connected
|
||||
? QStringLiteral("font-weight: bold") : QString());
|
||||
};
|
||||
connect(&LiveServer::instance(), &LiveServer::stateChanged, box, update);
|
||||
update(LiveServer::instance().state());
|
||||
//What the assistant just did, where the user is already looking
|
||||
connect(&LiveServer::instance(), &LiveServer::handled, box,
|
||||
[label](const QJsonObject &request, const QJsonObject &answer) {
|
||||
const QString cmd = request.value(QStringLiteral("cmd")).toString();
|
||||
if (cmd == QLatin1String("status")) return;
|
||||
QString what = answer.value(QStringLiteral("undo")).toString();
|
||||
if (what.isEmpty())
|
||||
what = request.value(QStringLiteral("name")).toString();
|
||||
if (what.isEmpty())
|
||||
what = request.value(QStringLiteral("script")).toString();
|
||||
label->setText(tr("Mode direct : %1 %2 à %3")
|
||||
.arg(answer.value(QStringLiteral("ok")).toBool() ? QStringLiteral("✓")
|
||||
: QStringLiteral("✗"),
|
||||
what, QTime::currentTime().toString(QStringLiteral("HH:mm:ss"))));
|
||||
label->setToolTip(answer.value(QStringLiteral("error")).toString());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QETDiagramEditor::updateScriptActions
|
||||
Enable each script for what its header's @context asks for: always
|
||||
|
||||
Reference in New Issue
Block a user