Skip to content

Fix tab close, Debug startup crash, Iseberg empty-line completion and first-tab startup - #58

Merged
Marc-André Moreau (mamoreau-devolutions) merged 6 commits into
masterfrom
claude/build-and-run-8d1445
Oct 21, 2026
Merged

Marc-André Moreau (mamoreau-devolutions) merged 6 commits into
masterfrom
claude/build-and-run-8d1445

Conversation

@mamoreau-devolutions

Copy link
Copy Markdown
Contributor

Summary

Four fixes found while building and running DT locally: tabs could not be closed with the mouse, Debug builds crashed at startup, Iseberg completion failed on an empty line, and the first Iseberg tab started two cold pwsh processes back to back.

Tab close button did nothing (regression from #56)

#56 made the tab header's drag handlers observe handled pointer events so tab dragging works on a Button. The press on the nested close X then also bubbled to the header, whose BeginTabDrag captured the pointer away from the close button, so it never raised Click. Clicking X only activated the tab.

BeginTabDrag now ignores presses that start on a nested button. Dragging from the header still works.

Debug builds crashed at startup

Debug builds attached Avalonia developer tools twice: WithDeveloperTools() in Program.BuildAvaloniaApp and AttachDeveloperTools() in App.Initialize (added in #52). The second call threw InvalidOperationException: Developer tools have already been attached. The duplicate in App.Initialize is removed. Release builds were not affected.

Iseberg completion on an empty line showed "Operation failed"

On empty or whitespace-only input, CommandCompletion.CompleteInput returns ReplacementIndex/ReplacementLength of -1. ManagedPowerShellSession passed that through, and building the editor's replacement span threw start ('-1') must be a non-negative value. A negative range is now mapped to an empty span at the cursor, in one helper shared by the normal, nested-prompt and paused-debugger completion paths.

First Iseberg tab: one pwsh instead of two

Opening the first Iseberg tab ran PowerShellInstallation.Find, which started pwsh only to read $PSHOME, its version, .NET runtime and architecture, and then the session started the bridge pwsh. The bridge bootstrap now prints that description on one stdout line before importing the module, and PowerShellSession validates it while the module loads, so it is off the critical path.

  • An installation too old to load the net8.0 module still fails with the version it found, instead of waiting for the bridge pipe to time out.
  • Error messages are unchanged. PowerShellInstallation moves to Iseberg.Core; PowerShellIseRuntime is removed.
  • Measured from tab request to ready (Debug build, warm): 1.70 s → 1.23 s. A first launch right after a build is still dominated by app-side JIT and disk I/O.

Merged with master (#57). #57 already includes an equivalent fix for a newer Zig on PATH, so this branch takes its version of native/Zig.ps1.

Test plan

  • New WindowActionRegistrationTests.ClickingTabCloseButtonClosesTheTab drives the close button with real headless pointer input. It fails without the fix ("Click raised: False") and passes with it. Also checked by real mouse clicks in the running app.
  • New IsebergNativeContinuationTests.CompletionWithNothingToReplaceAnchorsEmptySpanAtCaret covers the engine and the script editor on "" and " ". It fails without the fix (-1).
  • New PortableIseInstallationTests cases: real pwsh report output parses and validates; malformed reports are rejected; a fake pwsh reporting 7.3 fails in about 0.25 s with "found 7.3.0" (it times out on the old code); a pwsh that exits without reporting, and a missing executable, keep the existing messages.
  • After merging master, on Windows (Release, 0 warnings): App.Tests 606/606, Settings.Tests 206/206, UI.Tests 670/670. Linux and macOS were not run locally; the fake-pwsh tests include a POSIX shell variant.

🤖 Generated with Claude Code

Resolve-ZigPath took any zig on PATH, so a newer system Zig (e.g. 0.17.0)
was used for Ghostty, which only accepts the pinned version. Only accept a
PATH zig whose version matches the pin; otherwise fall back to the pinned
download under artifacts/tools.

Debug builds also crashed at startup because developer tools were attached
twice: WithDeveloperTools() in Program.BuildAvaloniaApp and
AttachDeveloperTools() in App.Initialize. Drop the duplicate in App.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On empty or whitespace-only input, CommandCompletion.CompleteInput returns
ReplacementIndex/ReplacementLength of -1. ManagedPowerShellSession passed
that through, so Ctrl+Space in an empty script editor failed with
"start ('-1') must be a non-negative value" when building the editor's
replacement span.

Map a negative replacement range to an empty span at the cursor in one
helper shared by the normal, nested-prompt and paused-debugger completion
paths, and add a regression test covering the engine and script editor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#56 made the tab header's drag handlers observe handled pointer events so
dragging works on a Button. The press on the nested close X then also
bubbled to the header, whose BeginTabDrag captured the pointer away from
the close button, so it never raised Click and tabs could not be closed
with the mouse.

Skip drag tracking when the press starts on a nested button, and add a
regression test that clicks a tab's close button with real pointer input.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Opening the first Iseberg tab started two pwsh processes one after the
other: PowerShellInstallation.Find ran pwsh only to read $PSHOME, its
version, .NET runtime and architecture, and then the session started the
bridge pwsh. The probe cost 0.5-1.3 s on every first tab.

The bridge bootstrap now prints that description on one stdout line
before importing the module, and PowerShellSession validates it while
the module loads. An installation too old to load the net8.0 module
still fails with the version it found instead of a pipe timeout. The
error messages are unchanged; PowerShellInstallation moves to
Iseberg.Core, and PowerShellIseRuntime is removed.

Measured from tab request to ready (Debug build, warm): 1.70 s -> 1.23 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n-8d1445

# Conflicts:
#	native/Zig.ps1
#	src/Devolutions.Terminal.Ise/Iseberg.Core/PowerShellSession.cs
…load

The tab close regression test used PowerShellIseTestHarness.WaitForAsync,
but the harness is excluded when EnablePowerShellIse is false, so the
managed Linux CI build failed with CS0103. Move the dispatcher polling
helper into DispatcherWait, which is always compiled, and have the
Iseberg harness delegate to it.

The macOS Iseberg job also failed when GitHub returned a 5xx page while
downloading the pinned test PowerShell. Retry that download.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
publish-dry-run — a5306ecd Deployed Oct 21, 2026 by mamoreau-devolutions via MSIX packages #366
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant