Commit Graph

130 Commits

Author SHA1 Message Date
ispyisail a5da2e9b8d Merge pull request #1532 from DaTweety/fix/check-labels-per-location
qet MCP: count duplicate labels per plant and location
2026-10-19 16:21:59 +13:00
ispyisail cafae25c31 Merge pull request #1531 from DaTweety/fix/mcp-edit-output-tail
qet MCP: keep the edit script's report lines when cutting long output
2026-10-19 16:21:43 +13:00
ispyisail 6dce85f2fb Merge pull request #1526 from DaTweety/feature/script-add-cable
Scripting and qet MCP: draw a cable across conductors
2026-10-19 16:21:26 +13:00
DaTweety f325b8001e qet-mcp: count duplicate labels per plant and location
qet_check reported -F1 as a duplicate master when one sits in +UV1 and one
in +UV2, although they are two devices: the place is part of the name, as
the %M/%LM label formulas show it and as IEC 81346 tools treat it. Group
the two duplicate-label checks by label and the effective place, the
element's own plant/location or else its folio's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 11:47:49 +02:00
DaTweety 1ff2fa5dcf qet MCP: keep the edit script's report lines when cutting long output
_run_qet() kept only the last 400 000 characters of a run's output. A
large qet_edit (a whole multi-folio drawing in one call, some 2000 ops)
logs more than that, and the cut took the capability report the script
makes first: the tool then answered "the binary never ran the script"
although QElectroTech had run it and saved the result.

Lines the script reports through its marker are now kept whole wherever
they stand; only the other output is cut to the tail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 11:47:49 +02:00
DaTweety 04716b0731 Scripting and qet MCP: draw a cable across conductors
qet.addCable(folio, designation, type, coreColors, length, x1, y1, x2, y2)
does what the cable tool does after its dialog: a new cable whose trunk
is the line between the two points, every conductor the line crosses
taking the next core in order along the line, with the colour coreColors
lists at that place. One undo step, through AddCableCommand. An empty
designation takes the next free name. A conductor another cable already
holds is refused rather than taken away, since a script cannot be asked
the question the dialog asks. qet.cables() lists the project's cables.

The qet MCP server gains the add_cable op for qet_edit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 11:47:48 +02:00
DaTweety a0e2193971 Joining potentials: agreeing ones leave nothing to choose
ConductorCreator asked which potential's properties to keep whenever a
new wire joined two potentials, even when they agreed in every
property; headless (scripting, qet MCP) such a wire was refused. Only
distinct properties are offered now, and with one left the wire takes it.

A potential's properties seen through one of a cable's cores carried
the core with them -- cable, cable uuid, core slot, and the core label
CableManager writes as text -- so a new wire claimed to be that core.
They are cleared; the text is left to the formula.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 11:21:09 +02:00
DaTweety fa498a3d20 A new wire takes the potential of a terminal it reaches through another
ConductorCreator::existingPotential() looked for an existing potential
only at the terminals being wired and, for a report, at its partner's
first conductor. A wire drawn to the free side of a terminal element
whose other side is wired, or to the far side of such a chain, found
none and got the folio's default properties: the neutral after a
terminal strip came out solid and unnumbered while relatedPotentialConductors()
counts it as part of the dashed, numbered N potential.

Each terminal is now followed through relatedPotentialTerminal() -- the
rule relatedPotentialConductors() applies -- until a wired terminal is
found, and two terminals on one potential count once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 11:10:10 +02:00
ispyisail f244f6dda0 qet-mcp: clean up .qetz copies, never keep an unfinished one, bound scans
Review of #1472 (scorpio810, points 4-7):

- The .qet made from each .qetz lived in its own temporary folder that was
  never removed. Only the newest 8 are kept now, an older version of the
  same file is dropped when a newer one is read, and all of them are
  removed when the server exits.
- A --resave that failed or timed out could leave a partial .qet behind,
  which was kept and read. Only a run that succeeded is used now.
- qet_scan read every .qetz in the folder, each through QElectroTech with
  up to 900 s. It reads the first max_qetz (default 20) and lists the rest
  as skipped.
- test_edit_writes_a_qetz checks the rotated symbol is the one reported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 06:00:23 +13:00
ispyisail b8390c853c Read and write .qetz in the qet MCP server
Every tool now takes a .qetz as it takes a .qet. The tools that read a
project's XML read the .qet the configured QElectroTech saves from the
.qetz, made once per version of the file (path, size, modification
time); without a QElectroTech, they say to set QET_BINARY. The tools that
launch QElectroTech hand it the .qetz, and write one when the output path
ends in .qetz. qet_scan counts .qetz files too. The server's description
and README say so.

Reading through QElectroTech instead of the file's own layout keeps the
server right while the zipped format still changes; the first read of a
big .qetz takes as long as QElectroTech needs to save it.

test_qet_mcp.py: the read tools answer the same for a .qetz and its .qet,
qet_diff finds nothing between them, qet_scan counts both, a .qetz is
converted once, qet_query reads it, qet_edit writes one (four fail when
_root() parses the file itself); without QElectroTech the answer names
QET_BINARY. Whole suite: 430 tests pass with QET_ENABLE_SCRIPTING=1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 23:03:51 +13:00
ispyisail 1457851c56 qet-mcp: give search results the scheme of the collection searched (#1450)
qet_element_search returned common:// for every folder, so a symbol found
in the user or company collection could not be placed by qet_edit. The
scheme now follows the folder: custom:// and company:// for the
collections qet-assistant.json names, common:// for any other directory.

The sandboxed run is also given those two collections. On Linux it
replaces XDG_DATA_HOME, so custom:// failed there even when written by
hand; it worked on macOS, where Qt does not read that variable.

The qet_edit schema no longer says an absolute .elmt path works anywhere:
QElectroTech places one only from inside a collection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 22:26:19 +13:00
ispyisail d895be6569 qet-mcp: run the original QElectroTech on macOS, not a private copy (#1178)
Every headless call failed with the macOS package: _launch_executable()
copied the program out of the signed .app bundle, and macOS kills such a
copy at launch (exit 137). The copy has also lost the path to the bundle's
Qt frameworks. Windows already ran the original for the same kind of
reason (DLLs beside the program); macOS now does too.

That is safe for the same reason as on Windows: the export flags and
--run return before QElectroTech constructs SingleApplication, so there
is no running instance to hand the call to. Linux keeps the private copy.

Reported and tested by DarkBiscuits on macOS 26.7 with the r10796 DMG:
with this one change and QT_QPA_PLATFORM=offscreen, every tool works
headless with no Dock icon.

test_qet_mcp: 421 tests pass on Linux against a master build with
QET_BINARY/QET_ELEMENTS/QET_EXAMPLES and scripting on, none skipped;
test_which_systems_copy fails with darwin removed from the rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 12:49:23 +13:00
ispyisail 592cbdbf0b Keep the texts of turned symbols horizontal, per project (#1335)
A project can now keep the texts drawn in its symbols, and the names of
their terminals, horizontal when a symbol is turned: Project properties >
General, "Garder horizontaux les textes des symboles pivotés". The box of
each text turns with the symbol; the text does not, and reads as it does
in the symbol itself.

It is a project setting, saved as <symbol_texts upright="true"/> and
only when on. A new project starts with it on; a project saved without
it (every existing one) reads with it off, so it looks and saves exactly
as before, and looks the same on every computer. The MCP server's new
projects start with it on too.

It builds on the mirror of #1354, which already redraws the texts of a
mirrored symbol readable: what the symbol does to its texts is now its
mirrors and, with the setting on, its turn (Element::symbolTextsTransform()).
ElementPictureFactory caches one drawing per such transform, terminal
names undo it the same way, and the DXF export places the texts alike.

The fields of a symbol (label, comment...) already keep their angle with
"Garder la rotation visuelle" and are left as they are.

Known limit: two texts stacked in a symbol end up side by side when it is
turned, and can overlap when kept horizontal; the setting can be turned
off for such a project.

Test: tst_uprightsymboltexts turns the symbols of a folio and checks the
angle of each motor's "M" in the DXF, with the setting off and on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 12:43:04 +13:00
ispyisail a5b5d49b98 Add horizontal and vertical mirror for symbols on a folio (#1335)
Edit > "Miroir horizontal" (M) and "Miroir vertical" (F), also in the
folio's right-click menu, mirror the selected symbols in place. The keys
are the ones the element editor uses for the same two actions.

An element keeps two mirrors about its own axes, applied before its
rotation, and saves them as mirror="horizontal|vertical|both" on its
<element> (written only when set, so other projects save byte for byte
as before). On a symbol turned by 90 or 270 degrees, a mirror of the
folio is the other mirror of the symbol itself, so the rotation never
changes: a label kept upright does not swing round, and "Pivoter" still
turns a mirrored symbol clockwise.

- Terminals face the mirrored way (Terminal::orientation()), so wires
  follow.
- The symbol stays where it was: its centre is kept, on the grid, since
  the hotspot is often a corner.
- Texts read normally. The element's texts, text groups and cross
  reference are mirrored a second time about the centre of their own box
  (Element::keepReadable()), and ElementPictureFactory draws the texts
  of the symbol itself the same way, in a cached picture per mirror.
  Groups and cross references held at the bottom of the folio stay
  centred under their element.
- DXF export mirrors the symbol's lines, arcs and texts.
- Scripting: qet.mirrorElement(folio, uuid, vertical) and
  qet.elementMirror(folio, uuid); live mode may run both menu commands;
  the MCP server gets a mirror_element op and qet_diff reports mirrors.

Not done: the parts of a PLC table drawn at run time
(Element::drawPlcTable()) are not kept readable on a mirrored PLC; the
project database has no column for it, as it has none for the rotation.

Test: tst_scriptmirror mirrors a symbol through --run, checks every
terminal's side and facing, the round trip, undo, a save and reload,
and a symbol turned by 90 degrees.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 11:05:17 +13:00
Laurent Trinques 85342f4b56 Merge pull request #1318 from ispyisail/feature/bom-skip-junctions-slaves
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 2m7s
Add options to leave junctions and contact blocks out of the parts list
2026-10-05 17:59:05 +02:00
ispyisail 16173c2070 Merge pull request #1316 from ispyisail/perf/live-timing
Add an optional timing log for live mode
2026-10-05 22:14:50 +13:00
ispyisail 97e81f7ebf Merge pull request #1314 from ispyisail/feature/live-remember
Remember live mode's start warning and "Always" across restarts
2026-10-05 22:14:31 +13:00
ispyisail 8434b88eb8 Add options to leave junctions and contact blocks out of the parts list (#1178)
Since #849 the parts list has a row for every contact block (slave) and
terminal-type element. Users who draw the dots and bends where wires
branch as terminal-type symbols (114_connections) get one empty row per
junction: 109 of 151 rows on one reported cabinet.

Two options, both off by default so existing exports are unchanged:
- leave out the contact blocks: --no-slaves, "no_slaves" in qet_export,
  or uncheck the new "Contacts esclaves" element type in the dialog;
- leave out the junctions: terminal-type elements with no label,
  designation, manufacturer or manufacturer reference. --no-junctions,
  "no_junctions", or "Laisser de côté les jonctions" in the dialog.
  A terminal block with a label or a part number stays.

The element type filter had no box for slaves, so every query it built
left them out: the export dialog never listed contact blocks, although
the command line has since #849. The dialog now checks the new box by
default and gives the same rows as --export-bom. The box is unchecked by
default elsewhere, so nomenclature tables keep their rows.

docs/smart-device-bom.md still said slaves and terminals were excluded;
corrected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 22:08:44 +13:00
ispyisail db9ba6c1ca Merge remote-tracking branch 'upstream/master' into perf/live-timing
# Conflicts:
#	sources/scripting/liveserver.cpp
2026-10-05 21:36:17 +13:00
ispyisail 7ddf2ca14f Merge remote-tracking branch 'upstream/master' into feature/assistant-house-style
# Conflicts:
#	misc/qet-mcp/README.md
#	misc/qet-mcp/qet_mcp.py
2026-10-05 21:36:08 +13:00
ispyisail 33f7d27388 Merge remote-tracking branch 'upstream/master' into feature/live-remember
# Conflicts:
#	lang/qet_en.ts
2026-10-05 21:35:52 +13:00
ispyisail 9b775baf6e qet-mcp README: the live timing log
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 20:35:19 +13:00
ispyisail a4f12917bc qet-mcp README: the house style
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 20:34:57 +13:00
ispyisail 44dfb9d8d5 Assistant house style: a drawing-convention text every AI assistant reads
QetSettings::houseStyle()/setHouseStyle() (assistant/house_style),
qet.houseStyle()/qet.setHouseStyle() in the script API, a house_style
field in qet-assistant.json, and qet_about in the qet MCP server
showing it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 20:34:56 +13:00
ispyisail 193d177d95 qet-mcp README: remembered live-mode choices
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 20:34:44 +13:00
ispyisail 8b130497e0 qet-mcp README: print, save/close, changes, live layout check, index cache, standard symbols
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 20:34:26 +13:00
ispyisail 0df8ca5b3c Live mode: print, save/close, changes, snapshot; house-style layout rules; saved symbol index
QElectroTech (LiveServer):
- print: folios to the default (or a named) printer with no print dialog,
  always after asking the user (paper cannot be undone); or to a new PDF
  file. Rendered as the PDF export draws a folio (no grid, no terminals).
- save_project (own file, or save as a new file, never over one) and
  close_project (only without unsaved changes: never discards work).
- changes: the undo history, each step marked assistant or user, with
  "since" to ask what changed after a point.
- snapshot: a copy of the open project as on screen, for a check to read,
  without saving it or changing its file.
- qet.elementTextGeometry(): where a symbol's text is drawn on the folio.

qet MCP server:
- qet_live_print, qet_live_save_project, qet_live_close_project,
  qet_live_changes, qet_live_layout_check (snapshot + qet_layout_check, no
  "run this script?" window); write paths never overwrite.
- Layout rules from the house style, for file and live checks alike:
  label_on_wire, four_way_junction, misaligned_branch.
- The element index is saved under ~/.cache/qet-mcp: a new server process
  loads 8839 symbols in 0.4 s instead of parsing them for 6.9 s; the
  change check is a third faster.
- qet_about lists the installation's standard symbols
  (standard-symbols.json in QElectroTech's data folder) when there are any.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 20:34:14 +13:00
ispyisail 4ceeeed302 qet-mcp README: the live project tools
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 20:34:14 +13:00
ispyisail ad20eb94ba Live mode: open a saved project, switch between open projects
open_project opens a .qet and makes it current, as File > Open does but
with no file dialog and no error windows: a missing, unreadable or
foreign file is refused in the answer. A project already open is only
made current. switch_project makes another open project current, by its
index or its file. status now lists every open project (index, title,
file, folios, unsaved changes, current), so the assistant sees what it
can switch to. MCP: qet_live_open_project, qet_live_switch_project; both
paths go through the workspace policy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 19:00:13 +13:00
ispyisail 42b1e43e4a Live mode: create a new project (new_project, qet_live_new_project)
An assistant in live mode could only work on the project already open.
new_project makes one as File > New does -- this installation's
new-folio defaults, optional title and number of folios -- and makes it
the current project, so the next request works on it. An optional
absolute path saves it at once, never over an existing file: QElectroTech
refuses, and the MCP server refuses before asking (a new never-overwrite
class in its path policy, so an "overwrite" flag cannot get round it).

Pages and links across pages already worked through scripts
(qet.addFolio, setFolioTitle, linkElements across folios); the tool's
description now says so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 18:48:38 +13:00
ispyisail e77a175fc9 Live mode: optional timing of each request (QET_LIVE_PERF_LOG)
A request with "timing": true gets queue, confirm and exec times in its
answer; with QET_LIVE_PERF_LOG set, QElectroTech also appends one JSON
line per request, with the time until the folio is next painted. The
qet MCP server logs its side of each live call to the same file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 18:32:42 +13:00
Laurent Trinques 1c09c4e391 Merge pull request #1282 from arummler/formula-improvements
Auto numeration: a lot of changes for elements, condutors, sheets
2026-10-04 11:52:56 +02:00
ispyisail c49561a76c qet-mcp: better symbol search for NO/NC and schematic symbols (#1178)
qet_element_search matched every word as a substring of the names and
the path, so "NC contact" offered a remanence coil ("nc" in "remanence",
"contact" in its folder) and an outlet before any auxiliary contact,
"normally closed contact" found nothing, and "emergency stop" ranked a
maker's part and two assembly-plan drawings above the push button.

- A word of one or two letters must now be a whole word. Longer words,
  and short ones with a digit, still match inside a word, so German
  compounds ("schutz", "klemme") and "3p" find what they found before;
  on 37 sample queries no result is lost.
- NO, N/O, "normally open", "normalement ouvert" are one word, and NC,
  N/C, NF, "normally closed", "normalement fermé" another.
- Symbols under *_graphics, *_manufacturers_articles and
  *_miscellaneous_unsorted come after every other match.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 08:34:29 +13:00
Andre Rummler d76f3528ae AutoNum: a lot of changes
Schemas now get saved with a UUID and do not have to be looked up according to the form. They can be renamed and it gets reflected in existing elements. The GUI for
editing them is now much easier, e.g. forms can be reshuffled. A formula can be applied. Many options now also for conductors and sheets.
2026-10-03 18:09:33 +02:00
ispyisail 173346c712 qet-mcp: qet_check finds crowded terminals and folio reports with several wires
Discussion #1158 asks for a limit on the wires per terminal. Before any
limit, two read-only checks let anyone count their own projects:

- crowded_terminals: terminals with more than four wires (two double
  ferrules, one either side of the screw), folio reports left out;
- reports_with_several_wires: folio report arrows with more than one
  wire, the rule plc-user proposes for reports.

Both are info, not errors: cable, busbar and single-line symbols carry
more wires on purpose, and a third of the shipped examples' reports have
several. Over the 24 examples: 23 crowded terminals in 10 projects
(the worst, 19, is a cable symbol in affuteuse_250h.qet) and 130 reports
in 4 projects. Plain SQL over the conductor table, so they work on any
current QElectroTech.

test_qet_mcp: the checks are read-only, the exact-answer test lists them,
and a coil wired to five others is reported once with its count (checked
to fail with the threshold raised). 398 tests pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FPuYPS4T7QuEwjNu22rXD
2026-10-03 22:26:42 +13:00
ispyisail b899d46453 Merge master (#1264, #1265) into feature/qet-mcp-layout-ops
README and tests: both PRs added text at the same places; kept both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FPuYPS4T7QuEwjNu22rXD
2026-10-03 15:56:00 +13:00
ispyisail d952fb9a0d qet-mcp: place_element takes an angle, for symbols turned along a rung
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FPuYPS4T7QuEwjNu22rXD
2026-10-03 13:41:25 +13:00
ispyisail 372e541a00 qet-mcp: ops that line symbols up so wires come out straight
A wire is straight only when its two terminals are exactly in line, and
qet_edit placed a symbol only by its origin, so an assistant had to guess
where the terminals would land -- and usually missed by a few pixels.

- place_element adds a symbol with one of its terminals in line with
  another symbol's, gap pixels away on the side that terminal faces, and
  notes when the new symbol's terminal faces the wrong way to meet it.
- align_terminal moves a placed symbol across so two terminals line up.
- align_elements lines up symbols' boxes (left/center/right/top/middle/
  bottom), as Edit > Align does.
- distribute_elements spaces symbols' origins evenly, or a pitch apart,
  keeping symbols on the grid on it.

place_element and align_terminal need qet.terminalPosition(); on a build
without it the edit stops before running, naming the missing call, and
every other op still works there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FPuYPS4T7QuEwjNu22rXD
2026-10-03 13:37:00 +13:00
ispyisail 5f2d31bff7 qet-mcp: add qet_layout_check, a score and fixes for how a drawing reads
A wire is straight only when its two terminals are exactly in line, and a
symbol is placed by its origin with its terminals at an offset from it, so
symbols an assistant places "under each other" by eye land a few pixels
apart and the wire jogs. Nothing told it so.

qet_layout_check reads every symbol and every wire's drawn path through
the scripting API (the default path is not saved in the file) and reports
a 0-100 score with: wires that jog where moving one symbol would make them
straight, extra bends, wires through symbols, overlaps, symbols off the
grid and crossings. "fixes" is one move_element per symbol, planned
together so they can be applied in one qet_edit call: a straight wire pins
its symbols, a move never lands a symbol on another or across a wire, and
symbols lined up with each other go onto the grid together. Styles: iec
(columns), nfpa (rungs) or auto. Read-only.

Over the 23 shipped examples, applying the fixes never lowers a score and
a second check proposes no further moves on 21 of them; a sloppy
assistant-style drawing goes from 40 to 100 in one round, in both styles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FPuYPS4T7QuEwjNu22rXD
2026-10-03 13:17:09 +13:00
Laurent Trinques e1f14e4b2f Merge pull request #1258 from ispyisail/fix/route-close-symbols
Fix wire routing looping between close symbols (#1178)
2026-10-02 23:15:33 +02:00
Laurent Trinques d8add2b8a0 Merge pull request #1257 from ispyisail/feature/pdf-reproducible
Add repeatable PDF export when SOURCE_DATE_EPOCH is set
2026-10-02 23:15:05 +02:00
Laurent Trinques ff64fdcb00 Merge pull request #1256 from ispyisail/fix/route-review-followups
Fix the follow-ups from the review of wire routing (#1245)
2026-10-02 23:14:51 +02:00
Laurent Trinques 7d61acebd9 Merge pull request #1260 from ispyisail/fix/live-mode-polish
Fix live mode's English text, settings path and screenshot (#1178)
2026-10-02 23:02:54 +02:00
Laurent Trinques 69a71d775f Merge pull request #1254 from ispyisail/fix/settings-dir-macos
Fix #1246: QET_SETTINGS_DIR reads the wrong folder on macOS
2026-10-02 22:58:14 +02:00
ispyisail 5859bc3353 Live mode: English text, the right settings tab, folio-only screenshot
Reported on #1178 after a first try of live mode on macOS:

- The English interface showed the live-mode setting in French. The
  strings were in lang/qet_en.ts but untranslated. This fills in English
  for live mode, the script buttons, the macro recorder and the Connect
  an AI assistant dialog (101 entries); other features' untranslated
  English entries are left for their own change.
- The setting is on Configurer QElectroTech > Général > Projets, but the
  start-up warning, the MCP server's error and its README all said
  "Configurer > Général". All three now name the Projects tab, and the
  README and the server name the English menus and buttons.
- qet_live_screenshot grabbed the whole viewport, so a tall window around
  a landscape folio came back mostly empty. It now crops to the folio's
  frame and title block where they are on screen (the whole viewport if
  the folio is scrolled out of sight), and says so in cropped_to_folio.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 09:14:57 +13:00
ispyisail 84cda1f0b0 Routing: no loops between close symbols, and route inside frames
Reported on #1178: with "route": "avoid", two contacts one above the
other, wired bottom terminal to top terminal 30 px apart or closer, got
a five-segment loop (at 20 px: v 80, h -20, v -140, h 20, v 80) that ran
down through the lower contact and back up past the upper one.

The cause was exitPoint(): it walked out of a terminal until clear of
every obstacle, so with another symbol in front it walked through that
symbol, and the search then had to come back. The same walk made a wire
between two symbols inside a frame (a cabinet drawn as one element)
leave through the frame's side, go round, and cross back in.

The router now knows each terminal's own symbol (Request::start_symbol,
end_symbol; applyRoute() fills them):
- two terminals facing each other on one line with nothing between
  them are joined straight, however close;
- the exit walks through the margin around other symbols but never
  through one; a terminal pointing straight into another symbol gets
  "no-route" instead of a route through it;
- an obstacle drawn around either end's own symbol is left out.
Without the symbols (the old Request), routes are as before.

Rerouting every wire of four shipped examples (perceuse, affuteuse_250h,
Polonez, industrial; 1331 wires): master routes 121 of them through
another symbol, this none (10 pass through a second symbol lying
exactly on an end symbol's rectangle, which no route can avoid). 99
wires that master routed through a symbol now get "no-route" and keep
their path. The 1232 wires both route are 6 % shorter in total
(436,144 -> 410,608 units) with 11 % fewer bends (2256 -> 2010).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:43:27 +13:00
ispyisail b0d73080ce Routing: follow up scorpio810's review of #1245
- Rename the `near` lambda in Conductor::setPathPoints() to `isNear`:
  `near` and `far` are empty macros in the Windows headers (minwindef.h).
- Reword the log when setPathPoints() refuses a route. "does not join the
  two terminals" named only one of its reasons; the router always returns
  the two terminals and at least one exit point, so a refusal means the
  route is not a run of horizontal and vertical segments between them.
- README: running along another wire is penalised, not forbidden, so two
  wires can end up drawn on top of each other.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:13:31 +13:00
ispyisail 8bbdafe2e4 qet-mcp README: the preset note says frame, not PDF page
The note the op returns reads "...; frame 1223.25 x 791.25 pt"; the README
quoted it as "PDF page".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 07:51:44 +13:00
ispyisail 51d20abca4 Write the same PDF for the same project when SOURCE_DATE_EPOCH is set
Two exports of one project differed in the creation and modification
dates (the time of the export, in local time) and in the document id
(random). With SOURCE_DATE_EPOCH set (reproducible-builds.org),
--export-pdf now uses the time it names, in UTC, and a document id
derived from the project: version 5 of the project uuid with the
SHA-256 of the project file, so it is the same for the same file and
changes when the file does. Qt has no setter for the dates, so
PdfLinks::setDocumentDate() rewrites them in the document information
and the XMP metadata after the file is written, fixing the XMP /Length
and shifting the xref table. Without the variable nothing changes.

Only --export-pdf reads it; the print window's PDF export does not, as
a person exporting by hand wants the real date.

qet_export (misc/qet-mcp) gains "reproducible" and "source_date_epoch".
It reports whether the build honoured the variable, since an older one
ignores it.

tst_pdfreproducible exports examples/741.qet four times and requires
identical bytes and the epoch's date; it fails with either this change
or the hash seed fix reverted. Projects with cross-reference links also
need their links in a fixed order, which is a separate fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 07:50:28 +13:00
ispyisail 580cbb1cb1 Merge remote-tracking branch 'upstream/master' into feature/mcp-folio-presets
# Conflicts:
#	misc/qet-mcp/README.md
#	misc/qet-mcp/qet_mcp.py
2026-10-03 07:44:54 +13:00