fix(mindmap): autonomous FreeMind SVG export — desktop attach + focus + tabs reset - #275
Merged
Merged
Conversation
…-foreground + tabs reset
Six fixes so the FreeMind SVG export loop survives unattended runs without RDP
focus, and large mindmaps stop silently exporting to the wrong file:
- **TryAttachToInteractiveDesktop**: switch the worker thread to WinSta0\Default
before SendKeys, so a process spawned from a non-interactive context can see
the FreeMind window. Idempotent (silently no-ops on ERROR_BUSY when names
match), releases the desktop handle in finally.
- **ForceForeground (AttachThreadInput + verify loop)**: plain SetForegroundWindow
is silently refused unless the caller is already foreground. We attach our
input queue to the current foreground thread for the call, BringWindowToTop,
then verify GetForegroundWindow actually points at the target — up to 5
attempts. Re-asserted right before sending keystrokes in case focus drifted.
- **SendKeysSafe**: swallows the spurious Win32Exception 'L'opération a réussi.'
(NativeErrorCode 0) that SendKeys.SendWait raises as a false negative —
same swallow as Mindmapper/xslt/Export-FreeMindSvg.ps1. Without this, the
very first keystroke aborts the whole export.
- **ClearFreeMindAutoOpenedTabs**: empties lastOpened= and
mindmap_last_state_map_storage= in ~/.freemind/auto.properties before each
launch. Without it, FreeMind silently restores 5 stale tabs from previous
runs, and our keystrokes target whichever tab gets focus first — the SVG
export goes to the wrong file or nowhere.
- **Broader KillAllFreeMind**: kill ALL javaw, not just those whose
MainWindowTitle.Contains('FreeMind'). A FreeMind that's still loading a
large .mm has an empty/unstable title and would otherwise survive between
iterations and steal focus from the next run. Also kill on window-not-found
timeout.
- **90s polling timeout** (was 30s): the cards-per-fallacy variants (~1MB .mm)
take 30-60s to load on FreeMind 1.0.1; 30s was failing them.
Validated 2026-05-16 on Windows 11 RDP session: 17/20 SVGs regenerated
autonomously in run #9 (#272 / #274 cover the residual broken-viewBox output
on Virtues post-processing, separate from this fix).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Six fixes so the FreeMind GUI automation in
FallacyMindMapDocumentConfigsurvives unattended runs and stops silently exporting to the wrong file on large mindmaps. Validated tonight on a Windows 11 RDP session: 17/20 SVGs regenerated autonomously.Changes
TryAttachToInteractiveDesktop(P/Invoke OpenInputDesktop + SetThreadDesktop)WinSta0\DefaultletsGetForegroundWindowactually see FreeMind. Idempotent.ForceForeground(hWnd)— AttachThreadInput + verify loop (up to 5 attempts)SetForegroundWindowis silently refused by Windows when the caller isn't already foreground. We attach our input queue to the current foreground thread for the call,BringWindowToTop, then verifyGetForegroundWindow == hWndbefore continuing. Re-asserted right before typing in case focus drifted.SendKeysSafe(keys)— wrapsSendKeys.SendWaitand swallows the spuriousWin32Exception(NativeErrorCode 0, "L'opération a réussi.")Mindmapper/xslt/Export-FreeMindSvg.ps1.ClearFreeMindAutoOpenedTabs(freemindUserDir)— emptieslastOpened=andmindmap_last_state_map_storage=in~/.freemind/auto.propertiesbefore each launch.svg(or none).KillAllFreeMind— kill ALLjavaw, noMainWindowTitle.Contains("FreeMind")filter.mmhas an empty/unstable title; the previous filter let it survive between iterations and steal focus from the next run. Also called on window-not-found timeout..mm) take 30-60 s to load on FreeMind 1.0.1. 30 s was timing out, leaving orphanjavaw.All wrapped with a
try/finallythat releases the input desktop handle.Validation
Argumentum_Virtues_*.content.svgshowingviewBox=" NaN NaN"is a separate post-processing regression — tracked in fix(mindmap-svg): Virtues .content.svg post-processing produces broken viewBox=" NaN NaN" — regression #274. DO NOT copy fresh Virtues SVGs back toCards/Fallacies/Mindmaps/until fix(mindmap-svg): Virtues .content.svg post-processing produces broken viewBox=" NaN NaN" — regression #274 is fixed.Test plan
dotnet buildclean (0 errors, 15 warnings, all pre-existing)Limitations
OpenInputDesktopsucceeding — we log the Win32 error if it doesn't and fall through to legacy behaviour rather than crashing.viewBox=" NaN NaN"post-processing bug (fix(mindmap-svg): Virtues .content.svg post-processing produces broken viewBox=" NaN NaN" — regression #274) is upstream of this PR and unaffected.🤖 Generated with Claude Code