Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
# Changelog

## 1.1.2 - 2026-07-24

### Fixed

- Pane divider visuals no longer draw ghost lines away from the actual split
boundary. The visible divider previously used a primary-axis Center
alignment with a leading-edge margin, which re-centered the line in the
space remaining after the margin; it now shares the leading-edge coordinate
system of its pointer target, so exactly one line renders at each logical
split position and nested dividers stay inside their owning split node.
- Added source-level regression coverage that locks the visible divider, the
pointer target, and the logical split position to one shared leading-edge
alignment model and keeps divider visuals reattached across split, close,
swap, restore, and workspace rebuild paths.

### Distribution

- Publishes the corrected pane-divider rendering as the verified x64 Setup EXE
and Portable ZIP.
- Workspace schema 2, docking model 1, shell protocol 1, and theme schema 1
are unchanged.
- The Setup EXE is not code-signed unless a trusted Authenticode certificate
is configured at publication; verify it with `SHA256SUMS.txt`.

## 1.1.1 - 2026-07-24

### Fixed
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ application downloads are:
- `winTerm-<version>-setup-x64.exe` — current-user or all-users installation;
- `winTerm-<version>-portable-x64.zip` — extract and run without installation.

The current source version is `1.1.1`. See the
The current source version is `1.1.2`. See the
[latest official Release](https://github.com/HelloThisWorld/winTerm/releases/latest)
for the complete published asset list and checksums.

Expand All @@ -39,7 +39,7 @@ Release. No MSIX certificate, Developer Mode, Visual Studio, Windows SDK, or
`Add-AppxPackage` is required to install a release EXE.

See [installation guidance](docs/user/installation.md) and the
[1.1.1 release notes](docs/releases/1.1.1.md).
[1.1.2 release notes](docs/releases/1.1.2.md).

## Core features

Expand Down Expand Up @@ -84,8 +84,8 @@ Use PowerShell 7 and the Microsoft Terminal toolchain described in
.\scripts\winterm\build.ps1 -Configuration Release -Platform x64 -IncludeTests
.\scripts\winterm\test.ps1 -Suite Relevant -Configuration Release -Platform x64
.\scripts\winterm\build-unpackaged.ps1 -Configuration Release -Platform x64
.\scripts\winterm\build-installer.ps1 -Version 1.1.1 -Platform x64
.\scripts\winterm\build-portable.ps1 -Version 1.1.1 -Platform x64
.\scripts\winterm\build-installer.ps1 -Version 1.1.2 -Platform x64
.\scripts\winterm\build-portable.ps1 -Version 1.1.2 -Platform x64
```

The unpackaged generator uses an unsigned MSIX only as an upstream build
Expand Down
43 changes: 24 additions & 19 deletions docs/current-progress.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,36 @@ Last updated: 2026-07-24

## Repository state

- Branch: `codex/release-v1.1.1`
- Starting commit: `a503b8dfaee05c533ba5981d83d22e92e31b3e20`
- Branch: `codex/fix-v1.1.2-ghost-divider-lines`
- Starting commit: `031f403a1bf7bd36a1df0e9ab1024e2aa78df262`
- Microsoft Terminal upstream revision:
`1cea42d433253d95c4487a3037db48197b5e72f4`
- Application and module version: `1.1.1`
- Package version: `1.1.1.0`
- Intended tag: `v1.1.1`
- Application and module version: `1.1.2`
- Package version: `1.1.2.0`
- Intended tag: `v1.1.2`
- Supported release target: Windows 11 x64

## Implemented in the working tree

- Removed winTerm pane repositioning sources, overlay/target/session models,
settings, menus, command-line entry point, keyboard mode, and tests.
- Added branch-local border resize transactions, minimum-size filtering,
common-ratio snapping, 8/14-pixel hysteresis, Alt bypass, rollback,
one-entry history, undo/redo, and Balance Panes.
- Added native Settings controls, centralized design tokens, and website-aligned
titlebar, tab, pane-header, divider, and terminal-shell resources.
- Updated version metadata, source validation, migration guidance, user
documentation, acceptance matrix, release notes, and website handoff.
- Fixed the 1.1.1 ghost pane-divider rendering: the visible divider now uses
the same owner-local, leading-edge alignment model as its pointer target
(vertical dividers align Left, horizontal dividers align Top, offset by the
split position minus half the visible thickness).
- Extended the pane-resize source validation with divider-placement
invariants: shared leading-edge alignment across the visible line and
pointer targets, half-thickness centering on the split position, a guard
against reintroducing the primary-axis Center alignment, a wider pointer
target than the visible line, and divider reattachment across the split,
swap, close, restore, and workspace rebuild paths.
- Updated application, package, shell module, About, workspace-fallback, and
release metadata to `1.1.2` with intended tag `v1.1.2`.
- Added the 1.1.2 changelog entry and release notes; the README points to the
1.1.2 release notes while downloads keep the stable `/releases/latest` URL.

## Evidence

The directed-split/pane-control and pane-resize source validations pass in
Windows PowerShell. Compiled native tests, Release application launch, visual
inspection, manual DPI/accessibility/rendering/shell scenarios, and 1.1
installer/Portable creation remain release gates until their results are
recorded.
The pane-resize and Smoke source validations pass in Windows PowerShell on
this branch. Compiled native tests, Release application launch, the manual
divider acceptance matrix (split ratios, nesting, DPI scales, High Contrast),
and 1.1.2 installer/Portable creation run through the pull-request GitHub
Actions workflows and remain release gates until their results are recorded.
55 changes: 55 additions & 0 deletions docs/releases/1.1.2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# winTerm 1.1.2

winTerm 1.1.2 fixes the ghost pane-divider lines visible in 1.1.1 while
preserving the complete border-drag pane-resize feature set.

## Fixed

- In 1.1.1 the visible pane divider could render away from the actual split
boundary — for example a full-height vertical line inside the larger pane
while the real vertical split sat farther left. The visible line used a
primary-axis Center alignment combined with a leading-edge margin, so XAML
re-centered it in the space remaining after the margin while the drag hit
target stayed on the real boundary.
- The visible divider, the pointer target, and the logical split position now
share one owner-local, leading-edge coordinate system: vertical dividers
align Left and horizontal dividers align Top, offset by the split position
minus half the line's own thickness.
- Exactly one divider line renders at each logical split boundary, and a
nested divider stays visually confined to its owning split node instead of
crossing unrelated sibling panes.

## Preserved behavior

- One logical-pixel idle divider with a centered 12-logical-pixel pointer
target and east-west / north-south resize cursors.
- Continuous live resize with minimum-size clamping and snapping at 25%,
33.333%, 50%, 66.667%, and 75%; holding Alt bypasses snapping.
- Escape and pointer-cancellation rollback, one history record per committed
drag, Undo, Redo, and Balance Panes.
- High Contrast divider brushes, hover and active-resize feedback, and compact
pane headers.

## Compatibility

Workspace schema remains version 2, the docking model remains version 1, the
shell protocol remains version 1, and the theme schema remains version 1.
Existing settings and workspaces load unchanged, and no pane session restarts
during a resize. Package identity, the `winterm.exe` alias, privacy
boundaries, and Microsoft Terminal coexistence are unchanged.

## Distribution

Primary x64 assets:

- `winTerm-1.1.2-setup-x64.exe`
- `winTerm-1.1.2-portable-x64.zip`

Publisher: `helloThisWorld`. Tag: `v1.1.2`.

## Signing

Unless a trusted Authenticode certificate is configured in the release
workflow at publication time, the v1.1.2 Setup EXE is not code-signed. Windows
may display a SmartScreen warning; verify the downloaded file against
`SHA256SUMS.txt` before running it.
4 changes: 2 additions & 2 deletions scripts/winterm/package-shell-assets.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ foreach ($relativePath in $sourceAssets)
}

$version = Get-Content -LiteralPath (Join-Path $repositoryRoot 'shell\shared\version.json') -Raw | ConvertFrom-Json
if ($version.moduleVersion -ne '1.1.1' -or
if ($version.moduleVersion -ne '1.1.2' -or
$version.modulePrerelease -ne '' -or
$version.applicationVersion -ne '1.1.1' -or
$version.applicationVersion -ne '1.1.2' -or
$version.protocolVersion -ne 1)
{
throw 'The winTerm Shell asset version metadata is invalid.'
Expand Down
145 changes: 144 additions & 1 deletion scripts/winterm/test-pane-resizing.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,149 @@ try
throw 'Divider visuals are not reattached across all branch rebuild paths.'
}

# Divider visuals are reattached through every branch rebuild path:
# split creation/workspace restore (the split-parent constructor), swap,
# close/collapse, and zoom restore. Attribute each call site to its
# enclosing column-0 definition instead of relying on line numbers.
$dividerRebuildOwners = [System.Collections.Generic.HashSet[string]]::new()
$currentFunction = ''
foreach ($line in ($runtime -split "`r?`n"))
{
if ($line -match '^(?=\S).*\bPane::([A-Za-z_~]+)\s*\(')
{
$currentFunction = $Matches[1]
}
if ($line -match '_CreateDividerVisual\(\);')
{
[void]$dividerRebuildOwners.Add($currentFunction)
}
}
foreach ($required in @('Pane', 'SwapPanes', '_CloseChild', '_Split', 'Restore'))
{
if (-not $dividerRebuildOwners.Contains($required))
{
throw "Divider visuals are not reattached in the '$required' rebuild path."
}
}

# v1.1.2 ghost-divider regression: the visible divider, the pointer/hit
# targets, and the logical split position must share one leading-edge
# coordinate system inside Pane::_UpdateDividerPlacement. A primary-axis
# Center alignment re-centers the visual in the space left after its
# margin and paints a ghost line away from the real split boundary.
$placementMarker = 'void Pane::_UpdateDividerPlacement'
$placementStart = $runtime.IndexOf($placementMarker)
if ($placementStart -lt 0)
{
throw 'Divider placement routine could not be located.'
}
$nextDefinition = [regex]::Match(
$runtime.Substring($placementStart + $placementMarker.Length),
'(?m)^(?=\S).*\bPane::[A-Za-z_~]+\s*\(')
$placement = if ($nextDefinition.Success)
{
$runtime.Substring($placementStart, $placementMarker.Length + $nextDefinition.Index)
}
else
{
$runtime.Substring($placementStart)
}

$verticalStart = $placement.IndexOf('SplitState::Vertical')
if ($verticalStart -lt 0)
{
throw 'Divider placement vertical branch could not be located.'
}
$elseMatch = [regex]::Match($placement.Substring($verticalStart), '(?m)^\s*else\b')
if (-not $elseMatch.Success)
{
throw 'Divider placement horizontal branch could not be located.'
}
$verticalBranch = $placement.Substring($verticalStart, $elseMatch.Index)
$horizontalBranch = $placement.Substring($verticalStart + $elseMatch.Index)

$alignmentOf = {
param([string]$branch, [string]$element, [string]$axis)
$match = [regex]::Match(
$branch,
[string]::Format('{0}\s*\.\s*{1}Alignment\s*\(\s*{1}Alignment::(\w+)', $element, $axis))
if (-not $match.Success)
{
throw "Divider placement does not set the $axis alignment of $element."
}
$match.Groups[1].Value
}

# Vertical split: every divider element shares the leading-edge (Left)
# primary-axis alignment model of the hit target.
$verticalHit = & $alignmentOf $verticalBranch '_dividerHitTarget' 'Horizontal'
$verticalPointer = & $alignmentOf $verticalBranch '_dividerPointerTarget' 'Horizontal'
$verticalVisual = & $alignmentOf $verticalBranch '_dividerVisual' 'Horizontal'
if ($verticalHit -ne 'Left')
{
throw "Vertical divider hit target must use leading-edge Left alignment, found '$verticalHit'."
}
if ($verticalPointer -ne $verticalHit -or $verticalVisual -ne $verticalHit)
{
throw "Vertical divider visuals must share the hit target's leading-edge alignment (hit '$verticalHit', pointer '$verticalPointer', visible '$verticalVisual')."
}

# Horizontal split: same invariant on the vertical (Top) primary axis.
$horizontalHit = & $alignmentOf $horizontalBranch '_dividerHitTarget' 'Vertical'
$horizontalPointer = & $alignmentOf $horizontalBranch '_dividerPointerTarget' 'Vertical'
$horizontalVisual = & $alignmentOf $horizontalBranch '_dividerVisual' 'Vertical'
if ($horizontalHit -ne 'Top')
{
throw "Horizontal divider hit target must use leading-edge Top alignment, found '$horizontalHit'."
}
if ($horizontalPointer -ne $horizontalHit -or $horizontalVisual -ne $horizontalHit)
{
throw "Horizontal divider visuals must share the hit target's leading-edge alignment (hit '$horizontalHit', pointer '$horizontalPointer', visible '$horizontalVisual')."
}

# The visible line stays centered on the split position by subtracting
# half of its own thickness inside the leading-edge margin.
if (-not [regex]::IsMatch(
$verticalBranch,
'_dividerVisual\s*\.\s*Margin\s*\(\s*ThicknessHelper::FromLengths\s*\(\s*dividerPosition\s*-\s*\(\s*visibleThickness\s*/\s*2\.0\s*\)'))
{
throw 'Vertical visible divider margin is not centered on dividerPosition by half its thickness.'
}
if (-not [regex]::IsMatch(
$horizontalBranch,
'_dividerVisual\s*\.\s*Margin\s*\(\s*ThicknessHelper::FromLengths\s*\(\s*0\s*,\s*dividerPosition\s*-\s*\(\s*visibleThickness\s*/\s*2\.0\s*\)'))
{
throw 'Horizontal visible divider margin is not centered on dividerPosition by half its thickness.'
}

# The primary-axis Center alignment that produced the v1.1.1 ghost line
# must not silently return on any divider element.
foreach ($element in @('_dividerVisual', '_dividerPointerTarget', '_dividerHitTarget'))
{
foreach ($axis in @('Horizontal', 'Vertical'))
{
if ([regex]::IsMatch(
$placement,
[string]::Format('{0}\s*\.\s*{1}Alignment\s*\(\s*{1}Alignment::Center', $element, $axis)))
{
throw "Divider placement re-introduced the $axis Center alignment on $element."
}
}
}

# The pointer target must stay wider than the visible line so the drag
# acquisition area keeps surrounding the rendered divider.
$visibleTokenMatch = [regex]::Match($tokens, 'DividerVisibleThickness\s*\{\s*([0-9.]+)')
$hitTokenMatch = [regex]::Match($tokens, 'DividerPointerHitThickness\s*\{\s*([0-9.]+)')
if (-not $visibleTokenMatch.Success -or -not $hitTokenMatch.Success)
{
throw 'Divider thickness tokens could not be parsed.'
}
if ([double]$hitTokenMatch.Groups[1].Value -le [double]$visibleTokenMatch.Groups[1].Value)
{
throw 'Divider pointer target must remain wider than the visible line.'
}

$moveStart = $runtime.IndexOf('void Pane::_DividerPointerMoved')
$moveEnd = $runtime.IndexOf('void Pane::_DividerPointerReleased', $moveStart)
if ($moveStart -lt 0 -or $moveEnd -le $moveStart)
Expand Down Expand Up @@ -150,7 +293,7 @@ try
Write-Host 'SKIP: compiled pane resize tests are unavailable.' -ForegroundColor Yellow
}

Write-Host 'PASS: pane resize snapping, cancellation, DPI, and history source boundaries.' -ForegroundColor Green
Write-Host 'PASS: pane resize snapping, cancellation, DPI, history, and divider placement source boundaries.' -ForegroundColor Green
}
catch
{
Expand Down
2 changes: 1 addition & 1 deletion scripts/winterm/test.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ function Test-ShellExperienceFoundations
}

$manifest = Import-PowerShellDataFile -LiteralPath $moduleManifest
if ($manifest.ModuleVersion -ne '1.1.1' -or
if ($manifest.ModuleVersion -ne '1.1.2' -or
$manifest.PrivateData.PSData.Prerelease -ne '' -or
$manifest.PowerShellVersion -ne '5.1')
{
Expand Down
2 changes: 1 addition & 1 deletion scripts/winterm/verify-branding.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ function Test-Manifest
Test-Requirement -Condition ($null -ne $identity -and $identity.Name -eq 'HelloThisWorld.winTerm') -Message "$Path uses package identity HelloThisWorld.winTerm"
Test-Requirement -Condition ($null -ne $identity -and $identity.Name -notmatch '^Microsoft\.') -Message "$Path does not use a Microsoft package name"
Test-Requirement -Condition ($null -ne $identity -and $identity.Publisher -ceq $ExpectedPublisher) -Message "$Path uses the expected non-Microsoft publisher"
Test-Requirement -Condition ($null -ne $identity -and $identity.Version -eq '1.1.1.0') -Message "$Path uses package version 1.1.1.0"
Test-Requirement -Condition ($null -ne $identity -and $identity.Version -eq '1.1.2.0') -Message "$Path uses package version 1.1.2.0"
Test-Requirement -Condition ($null -ne $properties -and $properties.DisplayName -eq 'winTerm') -Message "$Path package display name is winTerm"
Test-Requirement -Condition ($null -ne $application -and $application.Id -eq 'winTerm') -Message "$Path application ID is winTerm"
Test-Requirement -Condition ($null -ne $visualElements -and $visualElements.DisplayName -eq 'winTerm') -Message "$Path application display name is winTerm"
Expand Down
Loading