Commit Graph

11 Commits

Author SHA1 Message Date
c2b1f4c4ae Extract duplicate into core; support it in the script engine + wx GUI
A script using `duplicate` failed with "unsupported command 'duplicate'"
because the clone logic was still inline in the tui command. Extract it to
core/app/edit.hpp::duplicate_module(System*, src, dst) -> {ok, error, parts,
signals}: a deep clone of a module (parts, pins with spec + nc_origin, signals
with type overrides, pin→signal wiring; no connections), refusing on an unknown
source or an already-taken destination name.

  - the tui `duplicate` command renders the result (output unchanged);
  - the script engine dispatches `duplicate` to it — the failing script now runs;
  - the wx GUI gains Edit ▸ Duplicate module… (PickModule + a name prompt).

tests/test_edit.cpp: deep clone wires to the clone's own signal (not the
source's) and preserves the type; unknown source / existing destination
refused. 412 core assertions green; tui + wx build clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 22:04:45 +02:00
794430e86c wx: stop tree/log content from freezing the layout after a script
Real cause of the log not resizing: on GTK a wxTreeCtrl/wxTextCtrl reports a
natural size that grows with its content. Once a script populated the tree with
many pins, the top area's minimum ballooned and consumed the vertical space,
pinning the log at its minimum so it no longer tracked the window (it worked on
an empty tree, hence "only after running a script").

Cap each control's min size (tree/overview 260x120, log 420x90) so content
can't inflate the sizer minimum; the proportions then govern and content
scrolls inside the controls. Keeps the frame sizer from the previous fix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 21:59:47 +02:00
a9039a8eea wx: fix layout — log adapts and stays at the bottom on resize
The frame had no sizer; it relied on the implicit single-child fill, which
didn't reliably resize the panel, so the panel's vertical sizer never
redistributed and the log kept its size when the window grew. Add a frame
sizer holding the panel at proportion 1 + wxEXPAND, so a resize re-lays-out
the panel and the log (1/3) tracks the window.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 21:55:44 +02:00
fc71cce647 Add a core script engine; wire File ▸ Run script in the wx GUI.
The wx frontend had no way to run essim command scripts (only the tui shell
did). Add a frontend-agnostic engine in core/app/script.{hpp,cpp}:
run_script(unique_ptr<System>&, path, ostream&) -> {ok, error, lines, errors}.
It parses essim scripts (# comments, blank lines, "quoted" args, set + $var /
${var} expansion, nested source with a depth guard) and dispatches the
scriptable, system-building commands — new, load, connect, set-connector-type,
set-signal-type, attach-bsdl, verify, export, save, restore — to the existing
app::* operations, writing their (TUI-identical) output to the stream.
Unsupported/interactive commands are reported and counted, execution continues.
System is taken by reference so new/restore can replace it.

wx gains File ▸ Run script…: pick a .essim, run it, echo the output into the
log and refresh. WxFrontend exposes system_ptr() for the engine.

tests/test_script.cpp: a full script (comment + set/$var + new + load + set-
signal-type + verify) builds the system and produces the expected log; missing
file reported; unsupported command flagged and skipped. 400 core assertions
green; tui + wx build clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 21:49:05 +02:00
184b0d306f wx: drive the edit ops from the tree selection + a right-click menu
Each tree node now carries a NodeData (kind + Module/Part/Signal pointers), so
edits can act on what's selected instead of always re-asking:
  - Set connector type / Attach BSDL / Connect act on the selected Part (or the
    Pin's owning part); Connect uses it as the first endpoint, then prompts for
    the second. Set signal type acts on the selected Signal.
  - With nothing relevant selected, each falls back to its modal picker, so the
    menu-driven flow still works.
  - Right-click a Part or Signal → a context menu of the actions valid for it;
    the items reuse the menu IDs and select the clicked node first, so they run
    the same handlers.

wx-only; builds clean, window opens with no asserts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 21:38:23 +02:00
d4eac9557b wx: enrich the explore tree down to pins and signals
The model tree was modules → parts only. Drill it down:
  - each Part now lists its Pins, each labelled with the signal it is wired to
    and that signal's type, or "(NC[, imported|dropped])";
  - each Module gains a "Signals (N)" branch — the per-module net view, each
    signal labelled with its type and fan-out.
Pin/part/signal lists sort in natural order ("2" < "10") via a small helper.
Modules expand to show their parts + Signals node; pins and the signal list
stay collapsed (revealed on demand) so large parts don't flood the view.

wx-only change (no core, no tui); builds clean, window opens with no asserts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 21:32:21 +02:00
19dbec9672 Extract set-signal-type into core; add it to the wx GUI.
Fourth editing op into the wx frontend. Extract the type-name parse + apply
into core/app/edit.hpp::set_signal_type(Signal*, name) -> {ok, error, type},
failing without mutation on an unrecognised name. The interactive sigtype modal
keeps its own SignalType-cycling path (different interaction, trivial mutation).

The TUI `set-signal-type` command now renders that result (output unchanged).
The wx GUI gains Edit ▸ Set signal type…: a shared PickModule() helper (PickPart
now builds on it) + inline signal choice + a power/gnd/other dropdown, then the
core op, logged as "module/signal: signal type = …" and reflected.

tests/test_edit.cpp: name parsed and applied; unknown name refused without
mutation. 387 core assertions green; tui + wx build clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 21:27:44 +02:00
fc3ef333fa wx: add Connect parts to the Edit menu
Third editing op in the wx GUI. No core change — app::connect_parts was already
extracted and unit-tested; this is pure wiring. Edit ▸ Connect parts… picks two
parts (PickPart twice, now caption-parameterised to label "first/second part"),
derives their parent modules from Part::prnt, calls app::connect_parts and
renders the same outcomes the TUI does: refused / identity NC fill / connected
(N wires) / failed.

wx builds clean, window opens with no asserts; tui + tests unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 21:21:49 +02:00
b999446151 Extract attach-bsdl into core; add it to the wx GUI.
Second editing op into the wx frontend. Extract the logic (parse the .bsd,
apply it to the part, record bsdl_path) into core/app/edit.hpp::attach_bsdl
(Part*, path) -> {ok, error, entity, bound, unbound, ports_total}, failing
without mutation when the file can't be parsed.

The TUI `attach-bsdl` command now renders that result (output unchanged); the
wx GUI gains an Edit ▸ Attach BSDL… menu reusing PickPart() + a .bsd file
dialog. Prune the now-dead bsdl_model include from commands.cpp.

tests/test_edit.cpp: parse failure leaves the part untouched; null part. The
success path is covered by a batch run (entity + bound/total ports). 381 core
assertions green; tui + wx build clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 21:18:46 +02:00
7e88f82446 Extract set-connector-type into core; add it to the wx GUI.
First of the editing ops to reach the wx frontend. Extract the business logic
(validate the kind, tag the part, apply the connector model) into
core/app/edit.{hpp,cpp}: app::set_connector_type(Part*, kind) -> {ok, error,
materialised}, refusing without mutation when the kind is invalid for the part.

Both TUI call sites now use it: the `set-connector-type` command and the
interactive settype screen (de-dup) — output unchanged. The wx GUI gains an
Edit ▸ Set connector type… menu: a reusable PickPart() (module → part choice
dialogs) + a kind prompt, then the same core op, logged and reflected in the
model tree. Prune the now-dead pin_model/transform_vpx includes from
commands.cpp.

Unit-tested by tests/test_edit.cpp (free-form kind tags; invalid kind refused
without mutation; null part). tui + wx build clean; 376 core assertions green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 21:13:08 +02:00
4803d7d01c Add a wxWidgets GUI frontend (second frontend, proves the split).
A native wx GUI built entirely on essim_core via the Frontend interface — no
Tui reuse, no command shell. Demonstrates the core/frontends architecture by
adding a real second frontend:

  - WxFrontend : public Frontend — owns the System + a console buffer; handles
    boot headlessly (restore for --restore/--batch; honest note for source);
    Run() boots wx without a generated main (SetInstance + wxEntryStart/
    CallOnInit/OnRun) so the shared frontend_main stays in control.
  - EssimFrame (wxFrame) — menu-driven window: Load (app::load_module), Restore/
    Save (persist), Export (app::export_connections), Verify (app::verify),
    rendered into a model tree (modules → parts), an overview + verify-health
    panel, and a log. Each handler is a thin wrapper over a core/app op.
  - main.cpp: construct WxFrontend, call frontend_main — same 4 lines as tui.
  - CMakeLists.txt: find_package(wxWidgets) + essim_add_frontend(wx LIBS …);
    select with -DESSIM_FRONTEND=wx. ESSIM_FRONTEND gains wx in its STRINGS.

Set LC_CTYPE from the environment at GUI init so wxString decodes the UTF-8 in
narrow literals (em dash, ellipsis); LC_NUMERIC stays "C". .gitignore: build*/.

Needs libwxgtk3.2-dev. Verified: builds clean (wx 3.2.9 GTK3), window opens and
renders with no wx asserts; --commands-md/--restore/--batch behave headlessly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 21:01:02 +02:00