A filament_id is now exactly what the preset's own filament_vendor,
filament_type and filament name mint, wherever it inherits from. Inheriting
settings no longer limits what a preset may claim, so the checks that policed
inheritance are gone, and so are the four grandfather lists that held thousands
of presets as permanent exceptions. The snapshot records sanctioned state rather
than excuses: one entry per id, carrying the product it names beside the presets
claiming it.
Profiles that disagreed are corrected instead of excused. The Elegoo TPU and
PAHT roots were named for a different product than all of their variants and
become TPU 95A and PAHT-CF; Elegoo PET-CF gains the filament_type its variants
already set; the Snapmaker breakaway support presets get an id of their own
rather than riding the PVA chain; and a BBL preset name carrying a doubled space
is fixed behind renamed_from. Their ids re-mint from the corrected identities.
The tooling and the design note also drop the word "family", which invited
reading a brand's Lite and Pro spools as one id.
No change to slicing output — only filament_id values, three preset names, the
inherits lines following those renames and the vendor indexes move.
Orca content-addresses every system filament, Bambu's included, but a printer,
its AMS and its vendor's cloud know only that vendor's own catalog ids. The
printer agent now translates between the two: outbound MQTT and FTP traffic, the
AMS mapping sent with a print job, and the ids written into a 3mf bound for the
printer all leave in the printer's own ids, while status messages, loaded
projects and SD-card prints arrive in Orca's. An id with no mapping passes
through unchanged, and an agent whose printers already speak Orca's ids
translates nothing at all.
Bambu's map is generated from BambuStudio's own shipped bundle; a missing or
unreadable file leaves every lookup an identity rather than taking the app down.
The profile check validates the map's shape, and profile CI now runs on the paths
that can change it. docs/HLSD/filament_id.md records the places the map
deliberately does not reach.
Two harness fixes, both paid for by hours of chasing product bugs that were not there.
ROUNDED RECTANGLE. It is the shape the user reported and the gate could not reach it:
the rectangle family binds R to CornerRect and leaves the other modes in the toolbar
flyout, so the three-step Width -> Height -> Radius chain was never exercised.
rung_rounded_rect() arms it over MCP the way the offer menu does. The verb id is the
OFFER id `sk_rect_rounded` — `design_rect_rounded` is the ACTION name, run_verb
throws on it, and the tool silently stays Select; a run that misses that draws
nothing and still reaches its assertions, so the rung arms AND verifies.
THE NETWORK PLUGIN MODAL. GUI_App::post_init() re-raises "Bambu Network Plug-in
Required" from an IDLE event, after any startup sweep has closed it, and
ShowModal() runs a nested event loop: the app is alive, its window is on screen,
and the MCP socket answers nothing. That is indistinguishable from a hang and was
investigated as one, with gdb, twice — the attached stack finally read
ShowModal <- show_network_plugin_download_dialog <- post_init. Seeding
`installed_networking` false stops the whole networking path, so the dialog never
exists to be swept.
Also: check() returns its verdict, so a rung can abandon itself when a precondition
fails instead of asserting into a dead end.
38 checks hold on behemoth against e5659e0f0f.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011FbJKJAJxxkhDTs9XdZzKA
check-gui-sketching.py located the value field by hunting for a small top-level
window and clicked into it before typing. Both halves are now wrong, and the second
was always a problem:
- field_win() cannot find a field that is not a window any more, so every check
built on it silently became one that cannot fail. Replaced by field_open(),
which asks the app: sketch_describe's `editing` is value_field_open().
- focus_field() clicked into the field first, and its own docstring said why —
"WITHOUT THIS THE TYPED VALUE IS SILENTLY DISCARDED". That workaround is exactly
what made this suite blind to the defect the user reported: a ladder that clicks
the field first can never notice that typing WITHOUT clicking is broken. Now a
documented no-op; the field is in the canvas and the canvas has the keyboard.
Measured on behemoth against e5659e0f0f: 64 checks pass, 2 fail. The two are
polygon regularity and area (sides {29.999986, 29.975164, 30.0}, area 2336.98 vs
2338.27) — geometry tolerances, nothing to do with typed values; before this change
the same run failed 5, all of them dimensions and constraints that never received
their value because focus_field() was clicking at a window that no longer exists.
No baseline exists for the remaining two, so they are reported, not claimed as
pre-existing. The suite also still stops at D3 with the app gone; tracked separately.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011FbJKJAJxxkhDTs9XdZzKA
Rebases the in-canvas work onto cad-mainline and finishes it. The field is drawn
by ImGui inside the GL canvas instead of being a borderless top-level wxFrame.
WHY THE FLOATING FRAME COULD NOT BE FIXED. Whether a borderless top-level may hold
the keyboard is the window manager's decision, and it differs per desktop: openbox
grants it, mutter refuses it, macOS denies key status outright. Seven workarounds
fought that and one cost a macOS regression. Drawn inside the canvas there is no
second top-level for anyone to refuse, so the question is never asked. The field is
fed exactly like every other ImGui widget in the app — GLCanvas3D::on_char ->
ImGuiWrapper::update_key_data -> io.AddInputCharacter.
MEASURED, on behemoth: the click-edit ladder holds 28 checks — Line, Rectangle,
Circle, Slot, Polygon, Ellipse, Arc, and click-to-edit on a placed dimension label
— typing with NO click into the field first, committed == typed != prefill every
time, and 27 [UX] imgui_char lines showing the characters arriving.
WHAT WAS ACTUALLY WRONG. Not the field. The belief that "characters never reach the
ImGui InputText" came from the harness: the ladder was delivering keys with
`xdotool type --window` (XSendEvent), which GTK discards, so no build of any kind
could have received them. The new probe in ImGuiWrapper::update_key_data — the one
place ImGui is ever handed a character — is what separated that from a real defect,
and it stays, because a canvas-side probe provably cannot answer the question:
GLCanvas3D::on_char is bound later than any constructor-time probe, wx runs handlers
in reverse bind order, and on_char returns without Skip(), so such a probe is silent
whether or not the key arrived. A day was lost reading that silence as evidence.
Also drops DesignPanel's content-based forwarder and DesignCanvas::inline_type_char.
They were the right rule for a field that could not be focused; with the field
inside the canvas there is nothing to forward, and keeping them would have masked
whether the normal path works.
STILL UNVERIFIED: behaviour under mutter itself. Neither focus-stealing-prevention
WM available here survives long enough to judge — metacity SEGVs ~20s in and xfwm4
dies with BadWindow on SetInputFocus, both before the sketch opens and both
unrelated to this field. The design's claim is structural rather than measured: no
second top-level means no focus to refuse.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011FbJKJAJxxkhDTs9XdZzKA
focus= in [KEYTRACE] was never a class name. GetClassName() returns const wxChar* — wchar_t* in
this build — and the line cast that to const char* and printed it with %s, so it emitted the first
byte and stopped at the padding NUL. "wxGLCanvas" came out as "w". So did "wxWindow". Every focus
reading taken from this instrument for a whole day of diagnosis was a single character, and the
one question it existed to answer — WHICH widget has the keyboard — was the one it could not
answer. Printed through wxString now, and it says focus=wxGLCanvas: the canvas does hold wx focus
while the field is open, which removes the focus hypothesis for good.
Also here, and HONESTLY LABELLED AS INCONCLUSIVE: a wxEVT_CHAR probe on the canvas. It logged
nothing (cc=0), and the tempting reading is "the characters never reach the canvas". That reading
is not available, because the probe is bound in the DesignCanvas constructor BEFORE
GLCanvas3D::bind_event_handlers(), and wx runs the most recently bound handler first —
GLCanvas3D::on_char returns without Skip() exactly when ImGui consumes a character, which is
precisely the case under test. A silent probe is therefore consistent with ImGui consuming the
keys correctly AND with them never arriving. It measures nothing. Rebind it after
bind_event_handlers(), or instrument update_key_data itself, before believing anything about it.
Writing this down rather than acting on it: I came within one commit of "fixing" a mechanism I had
inferred from an instrument that could not see it, which is the same mistake as the focus= field
above and the same mistake that cost a whole session in September.
Deployed binary restored to 00d6c191dc (md5 0eeb9a58cef5).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011FbJKJAJxxkhDTs9XdZzKA
Third measured step, and the last one I will take without a second pair of eyes.
set_as_dirty() BEFORE Refresh(): GLCanvas3D's paint handler returns without rendering when the
canvas is not marked dirty, so the previous commit's bare Refresh() posted paint events that drew
nothing and the frames stopped anyway. With both halves the pump sustains, and the trace shows the
field holding the keyboard frame after frame:
[UX] frame want_text=1 want_kb=1 active=1 buf=158.74
[UX] frame want_text=1 want_kb=1 active=1 buf=158.74 (repeating)
STILL OPEN, and now narrowed to one question: buf never changes. ImGui owns the keyboard and our
InputText is the active item, so what is missing is upstream of ImGui — the characters are not
reaching io.AddInputCharacter at all. The next thing to MEASURE (not to change) is whether
wxEVT_CHAR arrives at the GL canvas in the Design tab: DesignPanel's wxEVT_CHAR_HOOK Skips digits
while inline_busy(), but Skip only helps if the focused widget is the canvas, and nothing has yet
proved that it is at the moment the keys are sent.
Three attempts have now gone into this one point. Per the standing rule that is where solo
iteration stops.
Deployed binary restored to 00d6c191dc (md5 0eeb9a58cef5) — nothing from this branch is installed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011FbJKJAJxxkhDTs9XdZzKA
Measured, not reasoned. A per-frame trace of the ImGui state is what finally named the mechanism,
and it is a deadlock, not a focus problem:
[UX] frame want_text=0 want_kb=0 active=0 buf=158.74 <- frame 1: the widget is not active yet
[UX] frame want_text=0 want_kb=0 active=1 buf=158.74 <- frame 2: now it is
(nothing further) <- and the canvas stops
This canvas repaints ON DEMAND. ImGui decides whether it wants the keyboard at the END of a frame,
from the active item; GLCanvas3D::on_char only calls render() when update_key_data() says ImGui
wants it. So: no frames -> WantTextInput never turns on -> no render on a keystroke -> still no
frames. The characters sit in ImGui's input queue and the field is exactly as deaf as the window
it replaced, for a completely different reason.
request_frame breaks the circle, and the same trace says so:
[UX] frame want_text=1 want_kb=1 active=1
That is the first time in this file's history that the value field has owned the keyboard without
asking a window manager for it.
STILL OPEN: the typed characters do not reach the buffer (buf stays at the prefill) and the frames
stop after nine. The pump is the suspect — on software GL request_repaint() calls m_canvas->render()
SYNCHRONOUSLY, so this asks for a render from inside a render; it needs to schedule one instead.
That is the next thing to measure, not to guess.
The deployed binary on behemoth is restored to 00d6c191dc (md5 0eeb9a58cef5), byte-identical to
the last good build. Nothing from this branch is installed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011FbJKJAJxxkhDTs9XdZzKA
The decision (Tommaso's, put to him with the trade-offs): the field stops being a separate
top-level window, because whether such a window may receive typing is the window manager's
call and not ours. openbox grants it, mutter on his desktop refuses, and seven previous
workarounds fought that — one of them causing a macOS regression, and the test harness ending
up clicking the field before typing, which is a workaround no user can be asked to perform and
is exactly the "label value not editable" report.
DONE and proved on the rig: SketchInlineEditor is no longer a wxFrame + wxTextCtrl. It is state
plus an ImGui overlay drawn by DesignSketchTool::render(), at the same screen anchor, in the same
vocabulary as the dimension labels next to it (draw_dim_label is already an ImGui window). The
field opens where it should — [UX] open title=Length prefill=158.74 from the running app.
NOT DONE: typing does not reach it. ImGui is fed from GLCanvas3D's own key handler, so the keys
have to arrive at the canvas; giving the canvas wx focus when the field opens was not enough.
The remaining question is where a keystroke goes between DesignPanel's wxEVT_CHAR_HOOK and
GLCanvas3D::on_char in the Design tab, and whether the canvas repaints often enough for ImGui to
advance its input state. That is attempt three on this specific point, so it goes to a second
opinion rather than a third guess.
Also here: scripts/CAD/check-gui-click-edit.py, the ladder Tommaso asked for. It types into the
field WITHOUT clicking it first — the click is what check-gui-sketching.py's focus_field() does
and why that suite can never see this defect — and fails when the prefill is what gets committed.
It currently fails, correctly, on the above.
Not on cad-mainline: the deployed binary must stay the last good build until typing works.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011FbJKJAJxxkhDTs9XdZzKA
present_toplevel() already asked for focus with a server timestamp, but only when
the widget happened to be realized. An unrealized widget has no GdkWindow, so
there was nothing to read a timestamp from and control fell through to
wxFrame::Raise() — which asks for activation with GDK_CURRENT_TIME, i.e. 0.
Zero is exactly what focus-stealing prevention discards. metacity says it out loud
when a sketch value field opens:
Buggy client sent a _NET_ACTIVE_WINDOW message with a timestamp of 0
and mutter, same lineage, refuses it silently on the user's desktop. That refusal
is the reported defect: the field is visible, never receives the keyboard, and
Enter commits the as-drawn prefill.
So realize the widget and retry, and do NOT fall back to Raise() on X11 — a
timestamp-0 activation is refused anyway, and on some window managers it only
marks the window as demanding attention.
Not yet confirmed end to end: both window managers with focus-stealing prevention
available here abort on this frame — metacity at frames.c:1239, xfwm4 with
BadWindow on SetInputFocus as the frame is destroyed under it — so the ladder
cannot yet return a trustworthy verdict under one. Two WMs crashing on the same
borderless, repeatedly re-mapped STAY_ON_TOP frame is its own signal about this
design. Tracked in projects-1p5 and projects-40m.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011FbJKJAJxxkhDTs9XdZzKA
The reported defect: sketch dimension labels are "not editable" — you draw a
rectangle, its Width field opens, you type, and the as-drawn number is committed
instead. It affects every sketch tool, not just the rounded rectangle.
WHAT THIS ADDS
1. The arbiter (DesignPanel CHAR_HOOK -> DesignCanvas::inline_type_char ->
SketchInlineEditor::type_char). Routes a key by what it IS, not by who the
window manager focused: digits, sign, decimal separator and Backspace/Delete
go to the open value field, Enter/Tab commit, letters stay tool shortcuts.
This is FreeCAD Sketcher's rule (DrawSketchKeyboardManager::
detectKeyboardEventHandlingMode), and the reason its sketcher behaves the same
on every desktop: it never asks who has focus.
2. The [UX] trace (SNAPORCA_UXTRACE) in SketchInlineEditor: open/commit/refused/
cancel, with the prefill and what the control actually held at Enter. It did
not exist — the ladder below was written against a surface no build emitted,
so it could only ever report "nothing opened". typed == prefill on a commit is
the defect's signature and nothing else makes it visible.
3. A draw-then-edit trace in DesignSketchTool: four early returns can swallow the
value-field chain and from outside they are indistinguishable.
4. scripts/CAD/check-gui-click-edit.py — types WITHOUT clicking the field, as a
person does, across Line/Rectangle/Circle/Slot/Polygon/Ellipse/Arc plus label
click-to-edit, and asserts committed == typed != prefill.
5. scripts/CAD/focus-loop.sh — sync/build/assert on behemoth. NOT the orcacad-gui
rig: its image pins deps 216 non-CAD files behind cad-mainline, so today's CAD
sources cannot build there without a deps rebuild.
WHAT IS PROVEN, AND WHAT IS NOT
Green under openbox: 28 checks, every tool, committed == typed != prefill.
But openbox CANNOT adjudicate this bug and the ladder says so in place. There the
field always wins the keyboard, so the same ladder also passes against a binary
with the arbiter compiled out — measured twice. Two ways of removing the keyboard
were tried and both are recorded as dead ends: XSetInputFocus loses to the field's
own re-focus CallAfter, and XSendEvent (xdotool --window) is dropped by GTK, which
made every run red regardless of the code.
Under metacity — same focus-stealing-prevention lineage as the user's mutter — the
mechanism appears in the WM's own log:
Buggy client sent a _NET_ACTIVE_WINDOW message with a timestamp of 0
That is the activation being refused, which is exactly the reported symptom.
present_toplevel() already asks for a server timestamp, so a path is still falling
through to frame->Raise(), which sends time 0. That is the next thing to fix, and
it is tracked; the arbiter alone does not close it. metacity also aborts on this
window (frames.c:1239), so the gate needs a WM that survives before it can return
a verdict.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011FbJKJAJxxkhDTs9XdZzKA
"Still cannot edit labels in rounded rectangles." Reproduced on the rig in a few minutes, and it
is NOT the window-manager defect the rest of this week has been about — it happens on openbox,
where typing into a value field works perfectly. The value was never the problem. The LABEL was
not there.
render_live_quotes picks the entity to speak for like this:
else if (m_selection.size() == 1) ei = m_selection[0];
if (ei < 0 || ...) return;
A rounded rectangle is EIGHT entities — four lines and four arcs — so selecting the shape makes
m_selection.size() == 8 and the pass returns before drawing anything. Its Width, Height and fillet
Radius are live labels and nothing else, so with them gone there is no affordance at all: no
number to click, no field to open, no value to refuse. The rule hid the characteristic quotes for
precisely the shapes that have nothing but characteristic quotes.
A plain rectangle looked fine only by accident. Typing into its auto-edit chain creates a DRIVEN
dimension, which render_dimensions draws from the annotation list, so its labels survive. The
rounded rect's W/H/R go through set_rounded_rect, which rebuilds the geometry and leaves no
annotation behind. Same for slot, arc-slot and polygon: every grouped feature was in this hole.
A selection that is entirely ONE feature now speaks through any member. The switch below already
keys off feature_of(ei) rather than the entity, so nothing else had to change.
Measured on behemoth :10, before and after, same binary path:
before 8 selected -> no labels at all
after 8 selected -> R26.6 / 117.4 / 150.7 drawn; clicking R26.6 opens Radius prefilled 26.60;
typing 8 gives R8.0 mm and visibly sharper corners.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011FbJKJAJxxkhDTs9XdZzKA
# Description
Under `core.autocrlf=input`, a Windows dependency build fails while
building CPython:
```
The system cannot find the batch label specified - begin_search
Cannot locate python.exe on PATH or as PYTHON variable
error MSB8066: ... exited with code 3
```
`git apply` inherits the caller's git configuration, so it rewrites the
patched `PCbuild/find_python.bat` to LF, and `cmd.exe` cannot resolve
`goto` labels in an LF batch file. `git init` already runs in the
extracted source, so setting `core.autocrlf` on that repository fixes it
and leaves the shared `PATCH_CMD` alone.
Only python3 is affected, its patch being the only one under `deps/`
that touches a `.bat`. `core.autocrlf=true` (the Git for Windows
default) and `false`/unset were never affected.
If a dependency build later fails with `patch does not apply`, delete
`deps/build` and rebuild. `git apply` is not idempotent, and that is
independent of this change.
## Tests
Visual Studio 2026 (18.6.3), `core.autocrlf=input`, built from an empty
`deps/build-dbg`:
| `deps debug` on | `find_python.bat` | CPython |
|---|---|---|
| `upstream/main` | 0 CRLF / 95 LF | fails at `begin_search` |
| this branch | 94 CRLF / 1 LF | builds |
It then stops at the debug staging step, a separate bug fixed by #15353;
with both applied it installs `libpython`. I hit this on a debug build,
but the patch step has no Debug/Release conditional.
[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
Two reports, three defects, all three measured on the running app rather than reasoned about.
"Keyboard focus in drawing tool is broken so that now they are slow and cumbersome." After a
dimensioned entity the bottom-right readout chip — 119x31, borderless, a wxFrame — held the X
input focus. Pressing r produced NO [KEYTRACE] line at all: the key never reached the panel's
CHAR_HOOK. One bare canvas click moved focus back to the main window and the identical key armed
the tool. So every shortcut was dead after every dimension, and the way to get the keyboard back
was to click somewhere harmless. That is the whole of "slow and cumbersome".
Its sibling, the status chip, is a wxPopupWindow for exactly this reason and carries a comment
warning against turning it back into a frame. The readout was left a frame on the premise that
"it appears mid-gesture and the next input is the mouse" — which the measurement falsifies: the
chip keeps the last value on screen after the gesture ends, and a frame that has the focus does
not give it back. It is now a popup too, with the placement and the iconise/deactivate lifecycle
its sibling already needed, because an override-redirect window would otherwise sit on the bare
desktop when the app is minimised.
"Planes hide the bed." Literally true, twice over. The reference planes were half-extent 0.6 *
the bed's larger side — a square 1.2x the plate — and all three are drawn with depth testing
off, so they painted over the plate grid from edge to edge. 0.3 puts them inside the bed, which
is also the Onshape look the size was reaching for: a modest square at the origin, not a
tablecloth.
And the other half was mine. 3f52166e32 muted the bed for the duration of a sketch, on the
argument that a plate grid and a sketch grid are the same visual language. The argument is right
and the call was wrong, because there IS no sketch grid to take over. Pick XY, arm Line, and the
viewport was an empty grey field: no bed, no grid, no origin, nothing to judge a length or a
direction against. The plate grid was carrying the ground reference for the whole tab. The banner
already says where you are; taking the floor away as well only made the sketch harder to draw.
The Bed checkbox is the one thing that governs the bed, in every mode.
Verified on behemoth :10 with the rebuilt binary: focus after a dimension chain is the main
window, r arms Rectangle with no click in between, and the plate grid is under the sketch.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011FbJKJAJxxkhDTs9XdZzKA
Measured on the running app, not deduced. After a queued dimension chain the value field's frame
is left MAPPED on purpose (mutter refuses keyboard focus to a re-mapped window), so there is a
window in which m_open is already false and the frame is still on screen holding the X input
focus. GTK meanwhile reports that window inactive and routes nothing into the text control. Every
key then lands somewhere that cannot use it and will not give it back:
[KEYTRACE] key=27 ui_mode=1 inline_busy=0 <- the last key the panel ever sees
=== MARK press Delete === <- no trace line at all
xdotool getwindowfocus -> 0xe00404 86x60 <- the value field, still mapped
Delete, Esc and typing all read as dead, which is exactly the report. And nothing could recover
it: close(), cancel() and do_cancel() all return early on !m_open, so the one window still
receiving keystrokes was also the one window no code could dismiss.
is_mapped() asks the question the flag cannot answer, and dismiss() tears the frame down with no
m_open guard, since m_open is precisely what lies in this state. Esc inside the field falls back
to it — while the frame holds focus that handler is the only code the keyboard can still reach,
so if it refuses, nothing else gets a turn. Every close now hands focus back to the canvas
explicitly, because hiding a window does not move the X input focus off it. And inline_busy()
reports the union of "a value is pending" and "a frame is mapped", so Esc routes to the field
whenever one is on screen at all.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011FbJKJAJxxkhDTs9XdZzKA
Three cues, because one is missed. A teal banner across the top of the viewport names the session
("Editing: Sketch N") and where its exits are; the printer bed is muted for the duration, since a
plate grid and a sketch grid are the same visual language and reading one as the other is how a
sketch gets drawn against the wrong reference; and N looks straight down the plane normal at the
current zoom, with the plane's own y axis as up, because no hand-orbit lands exactly square and a
sketch read at an angle is one whose right angles do not look like right angles.
The banner is an INDICATOR. Finish and Cancel stay on the single ribbon action bar — the tab had
three competing confirm surfaces once and that is not being reopened for a strip of colour. It
sits above the canvas rather than floating inside it: a child window over a wxGLCanvas is a native
window on GTK with no reliable stacking over GL, and being unmissable beats being clever.
The bed checkbox stays the stored preference and is restored on leaving the sketch; ticking it
mid-sketch still shows the bed, because that is a deliberate act and this is only a default.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011FbJKJAJxxkhDTs9XdZzKA
Two presses used to discard a live sketch. The key was answered in four places that could not
see each other — the inline value field, a sketch branch, a feature-card branch, and the canvas
— so a press aimed at one fell through to the next, and request_exit() carried a fourth layer
that deliberately let the SECOND consecutive press through to cancel_sketch(). The warning it
showed first did not help: the two presses are never one decision, the first is aimed at a field
or a tool and the second at whatever was underneath it.
The stack is now explicit. CadLevel (DesignInteraction.hpp) is four levels deep, the enum value
IS the LIFO depth, and cad_escape_level() is a constexpr function over a POD of four booleans —
so the ordering that is the entire contract is checked by static_assert at compile time, with no
window, GL context or event loop. DesignPanel::escape() acts on the one level escape_level()
names and on no other, and every Esc in the tab routes through it.
The destructive layer is gone from request_exit() itself rather than guarded at its callers, so
the guarantee cannot be re-opened by adding a route: a session holding geometry is left only
through Finish (keep) or Cancel (discard). Cancel now asks before discarding — it used to refuse
and tell the user to press the button they had just pressed, which meant a drawn sketch could be
kept but never thrown away.
Right-click also stops rewarding navigation with a menu: the offer needs BOTH budgets, released
within 200 ms and moved no more than 3 px, and the raycast uses the press position, so the menu
describes what was pointed at rather than where the camera stopped. Two budgets because drift
alone still popped a menu at the end of a slow, careful orbit.
docs/ux/interaction-model.md carries the state machine, the routing and the transition table.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011FbJKJAJxxkhDTs9XdZzKA
* Fix incorrect early exit for CLI mode no-support preventing parameters from being read
* Use PartPlate's m_height to allow CLI to perform proper BuildVolume check
* Add safeguard against extruder_pintable_heights and extruder_areas vector size mismatch
* Preserve printable_height precision in PartPlate/PartPlateList
* Fixed multiple BuildVolume warning issue, and keep check_outside diff minimal
# Description
<!--
> Please provide a summary of the changes made in this PR. Include
details such as:
> * What issue does this PR address or fix?
> * What new features or enhancements does this PR introduce?
> * Are there any breaking changes or dependencies that need to be
considered?
-->
Part 1 of 3 of the CLI-mode bug sweep, split out of #15452 per review
feedback there. This PR contains the crash fixes.
## Fixes
- **`--outputdir`/`--datadir` with a missing parent directory aborted**
via unguarded `create_directory`. Directories are now created
recursively, with a graceful early exit and a specific error message if
creation fails.
- **`--slice` + `--export-3mf` segfaulted on a from-scratch slice**:
`ConfigOptionVector::get_at()` on an empty vector is `.front()` of an
empty vector (UB). Guards added for `filament_color`/`filament_id` at
the CLI call site, and inside `DynamicPrintConfig::get_filament_type`
for `filament_type`/`filament_is_support`/`filament_id`. Only *empty*
vectors are treated as missing — the existing clamp-to-front behavior
for merely out-of-range indices is preserved, so GUI callers are
unaffected.
- **OOB heap write from stale `filament_self_index` on
`--load-filaments`** (fixes#14181): a 3MF carrying more
`filament_self_index` entries than loaded filaments wrote past the end
of `old_variant_counts`. The guard validates both bounds — entries `>
filament_count` *and* non-positive entries (`< 1`), since a single `0`
in an otherwise-valid array indexes `old_variant_counts[-1]`.
- **Wrong printable-area check** for non-rectangular beds: use the
printable area's bounding box instead of a naive vertex calculation
(fixes#15363).
- **`nozzle_height` and `align_center` were not read into the arrange
config** in CLI mode.
# Screenshots/Recordings/Graphs
<!--
> Please attach relevant screenshots to showcase the UI changes.
> Please attach images that can help explain the changes.
-->
## Tests
<!--
> Please describe the tests that you have conducted to verify the
changes made in this PR.
-->
- Repro'd each crash on CLI before the fix; all resolved after.
- `tests/libslic3r` suite passes; full binary builds clean on Linux.
- Added `get_filament_type` unit tests
<!--
> A guide for users on how to download the artifacts from this PR.
-->
[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
The tree-wide length check (freed from its BBL/OFL carve-out last commit)
was flagging 21 pre-existing SeeMeCNC files that no vendor index references
and that therefore never load, turning CI red for files with no bearing on
what ships. The rule now only fires on presets a vendor's filament_list
actually references; every .json under the vendor's filament directory is
still parsed through the duplicate-key hook, so that coverage is unchanged.
Also removed a duplicated BAMBU_MAP_PATH definition: update_bambu_filament_ids.py
now imports the constant from assign_filament_ids.py, which it already imports
several other constants from, instead of recomputing the same path independently.
Every vendor's filament_id declarations, Bambu's own bundle included, are now
minted and checked the same way: the GF* catalog space is reserved but
ownerless, format is validated unconditionally with no snapshot or BBL
exemption, and both --remint and the default assign pass treat a non-OF
declaration as one needing a fresh mint (so a future BambuStudio sync
self-heals instead of needing a manual pass). A new check validates
resources/printers/bambu_filament_ids.json against the tree it describes:
that it parses, carries its header, keys only OF ids, maps each Bambu id once,
and agrees with the tree on every product it shares. The redundant BBL/OFL
carve-out in the profile checker's length check is dropped too, so it runs
the same way for every vendor.
The BBL bundle itself hasn't been touched yet and still declares its old GF
ids, so TestRealTree.test_shipped_snapshot_matches_tree is expected to fail
here (209 declarations flagged) until the next commit re-mints the bundle
onto OF ids.
scripts/update_bambu_filament_ids.py derives resources/printers/bambu_filament_ids.json
from BambuStudio's own shipped BBL bundle (cloned from upstream, or read from a local
checkout via --bambustudio-dir), pairing each Bambu catalog id with the Orca filament_id
we already ship for that product where we ship one, else a freshly generated one. Nothing
reads the map yet; later work uses it to translate ids at the Bambu printer boundary.
* Reject invalid CLI argument values instead of silently accepting them
* Add read_cli accept/reject tests
* Update Option Type for LogFile argument
* Add read_cli vector option tests
* Accept common bool spellings on the CLI, cover --logfile in tests
* Add unit tests for truthy bool parsing
Refresh detach-from-parent checkbox state
Allow the detach checkbox toggle event to propagate to the custom CheckBox control so it refreshes its bitmap after the value changes.