mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-03 17:34:12 +02:00
25084a44da3e2bf7d1ff9cdc30b5690b9c237f8b
5 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2fb14c5bd0 |
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> |
||
|
|
e03e523069 |
Script buttons: stored scripts become commands with an icon
Every .js file in the "scripts" folder of the user's data folder that starts with a // ==QETScript== header becomes a command: in Projet > Scripts, as a button on a new Scripts toolbar, and, because it is registered with ShortcutManager as diagrameditor.script.<file name>, in the shortcut settings, the shortcut bar (S) and command search. The header gives its name, icon (a file next to the script or a theme icon; a tile with its initials otherwise), tooltip, default shortcut and when it is enabled (always, with a selection, with a conductor selected). The folder is watched, so a script added, edited or deleted while QET is open appears, changes or goes without a restart. A file with a header that cannot be used gets no button; the Scripts menu lists it with the reason. The menu also opens the folder, and holds "Exécuter un script…". A click runs the script on the current project as one undo step named after it, and asks to switch scripting on first, like "Exécuter un script…" does: scripting stays off by default. ShortcutManager::unregisterAction() takes a command out of the lists when its script is deleted, and lets it come back under a new name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
1c1f9db3eb |
Scripting: qet.currentFolio(), and one undo step per script run in the editor
A script started from the editor had no way to know which folio is on screen, and each qet.* call was its own undo step, so a script that adds twenty items needed twenty Ctrl+Z to take back. qet.currentFolio() returns the folio shown in the editor; through --run, which has no view, the first folio, or -1 when there is none. A run from the editor is now one undo macro, named after the script. A run that changed nothing leaves no empty entry behind. qet.undo() and qet.redo() inside a grouped run say why they cannot work (QUndoStack ignores them inside a macro) instead of failing silently. --run is unchanged: one step per call, so scripts that call qet.undo() keep working. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
0920e82188 |
Add geometry editing, undo integration, and navigation to scripting
Extends the scripting surface from the previous commit with exactly the three things explicitly scoped out there, per follow-up direction: editing geometry, undo integration, and driving the GUI -- the last one narrowed to select/zoom/message after discussion, since "invoke any menu action by name" would let a script trigger a modal QDialog::exec() with nobody there to dismiss it, the same hang class investigated for bugtracker #882. ## New capabilities - addElement/setElementPosition/moveElement/deleteElement, through the same undo commands the GUI itself uses: AddGraphicsObjectCommand (the same one drag-from-collection-panel placement uses), QPropertyUndoCommand on the standard `pos` property, and DeleteQGraphicsItemCommand (refuses a non-deletable terminal, same as the Delete key). - undo/redo/canUndo/canRedo against the project's real QUndoStack -- the same one QETDiagramEditor's Ctrl+Z is wired to via undo_group.activeStack(), not a parallel mechanism. - selectElement/deselectAll (scene state, no view required -- works headless), zoomFit/zoomToContent/zoomReset (need the active DiagramView, so false headless where there is nothing to zoom), and showMessage (a modal QET::QetMessageBox::information -- safe headless because non-interactive mode is already on for the whole process before any script runs). ## Two real bugs caught by testing this, not assumed away 1. save() was still going through the same reopen-from-disk path as every export method: it opened a *second*, unmodified copy of the project from its file on disk and rewrote that. addElement() and friends operate on the live in-memory project, so nothing they did ever reached the saved file -- an element counted correctly in memory and then silently vanished from the output. Fixed by having save() write m_project->toXml() directly, the only method that touches the live instance rather than a fresh copy of the file. 2. A script calling setElementPosition() then moveElement() on the same element produced a saved position that didn't match either call, and undo/redo didn't step through them independently. Traced to QPropertyUndoCommand::mergeWith() (pre-existing, not new): consecutive commands on the same object+property merge when their text() also matches, and both calls build the identical "Déplacer %1" text for a given element -- exactly the same collapsing dragging an item repeatedly gets. Not a bug in the new code; a wrong assumption in the first test. Re-verified against the correct, merge-aware expectation: add -> merged move -> undo (back to first position) -> undo (element removed) -> redo (element back) -> redo (merged move reapplied) landed at the exact predicted final position, read back from the saved XML. ## Verified Qt6, build clean from a fresh reconfigure, ctest 6/6. - Headless: addElement returns a real uuid and the count updates; select/set-position/move all report correctly; the merge-aware undo/redo/save round trip above, confirmed against the saved file's actual XML, not just in-memory counters. - Corpus: the existing read-model smoke script re-run against all 24 shipped example projects on the fixed binary, 0 failures. - zoomFit correctly returns false headless (no view to act on), confirming the "narrowed GUI-driving" scope holds in code, not just in the doc comment. - GUI: running the add-only script via "Exécuter un script..." marked the project [modifié] in the title bar, the same change-tracking path a manual edit goes through -- consistent with the undo command actually being pushed onto the project's real stack rather than some side channel invisible to the rest of the application. Refs #162. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
eba258f6cd |
Add JavaScript scripting: --run and "Run Script..." (bugtracker #162)
Following up on my own comments there: a deliberately small, mostly read-only scripting surface, exposed to scripts as a single global `qet` object (QetScriptApi) built on QJSEngine rather than an embedded Python interpreter -- no new toolchain to package (QJSEngine ships in every Qt SDK QET already targets, via the Qml module), no GIL, no version pinning, automatic reflection of the QObject-derived core classes' own methods with no hand-written binding layer. ## What a script can do - Read the model: project title, file path, folio count/titles, element/conductor counts per folio. - Trigger the same operations the --export-* CLI flags already do (pdf/png/svg/cables/wires/bom/wiring/nets/links/info), plus set-titleblock and save -- thin wrappers around CLIExport::run(), reusing its already-tested logic rather than duplicating it. Deliberately NOT in this version: creating or editing diagram geometry, undo integration, driving the GUI. All explicitly out of scope per the discussion on #162. ## Two entry points, both built and tested - `qelectrotech --run script.js project.qet` -- headless/CI. - Projet > "Exécuter un script..." -- an interactive macro against the currently open project. Export/save calls act on the project's file on disk (see QetScriptApi's class comment for why), so unsaved GUI edits aren't visible to the script; save first if that matters. ## Optional dependency, not a hard requirement Qt::Qml is probed the same way QtPdf already is in this codebase: QUIET, non-fatal, behind a QET_HAS_SCRIPTING compile definition. A build without it compiles and links identically; the CLI flag and menu action are simply absent (main.cpp) or compile to a clear "not available" stderr message rather than silently disappearing (qetscripting.cpp), matching the existing QtPdf pattern rather than introducing a new one. One real bug caught building this, not assumed away: my first pass conditionally excluded the new source files from QET_SRC_FILES behind `if(QET_HAS_SCRIPTING)` inside qet_compilation_vars.cmake -- but that file is included before QET_HAS_SCRIPTING is set in the top-level CMakeLists.txt, so the variable didn't exist yet at that point and the files were silently never compiled, only caught by an undefined-symbol link error. Fixed by following the QtPdf file's own precedent: compile the files unconditionally, guard their Qt::Qml-dependent content internally instead. ## Verified Qt6, build clean, ctest 6/6. - Headless: a script reading project/folio/element/conductor counts, calling exportInfo() and exportPdf() against a real project -- correct JSON, a real single-page PDF confirmed with `file`. Error paths: a thrown script exception reports file:line:message and exit 1; missing script/project arguments exit 2 (matching CLIExport's own usage-error convention); a missing project file is reported and does not hang. - Corpus: the same read-model script run against all 24 shipped example projects, 0 failures. - GUI: "Exécuter un script..." opens a real file dialog filtered to *.js, running the picked script against the live open project produced the exact expected JSON export file, and the application was still fully responsive afterward. Refs #162. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |