zsui 0.3.0-alpha.2

Rust-first feature-gated native UI framework for Win32, AppKit, and Linux.
Documentation
param(
    [switch]$Locked
)

$ErrorActionPreference = "Stop"
$workspace = Split-Path -Parent $PSScriptRoot
$cargoArguments = @(
    "build",
    "--example", "zsui_notepad",
    "--no-default-features",
    "--features", "notepad-demo,accessibility"
)
if ($Locked) {
    $cargoArguments = @("build", "--locked") + $cargoArguments[1..($cargoArguments.Length - 1)]
}

Push-Location $workspace
try {
    & cargo @cargoArguments
    if ($LASTEXITCODE -ne 0) {
        throw "failed to build the native accessibility probe application"
    }
} finally {
    Pop-Location
}

$targetRoot = if ([string]::IsNullOrWhiteSpace($env:CARGO_TARGET_DIR)) {
    Join-Path $workspace "target"
} elseif ([System.IO.Path]::IsPathRooted($env:CARGO_TARGET_DIR)) {
    $env:CARGO_TARGET_DIR
} else {
    Join-Path $workspace $env:CARGO_TARGET_DIR
}
$notepad = Join-Path $targetRoot "debug\examples\zsui_notepad.exe"
if (-not (Test-Path -LiteralPath $notepad -PathType Leaf)) {
    throw "native accessibility probe executable was not produced: $notepad"
}

Add-Type -AssemblyName UIAutomationClient
Add-Type -AssemblyName UIAutomationTypes
Add-Type -TypeDefinition @'
using System;
using System.Runtime.InteropServices;
using System.Text;

public static class ZsuiWindowsTextAccessibilityProbeNative
{
    private delegate bool EnumWindowsCallback(IntPtr hwnd, IntPtr lparam);

    [DllImport("user32.dll")]
    private static extern bool EnumWindows(EnumWindowsCallback callback, IntPtr lparam);

    [DllImport("user32.dll")]
    private static extern uint GetWindowThreadProcessId(IntPtr hwnd, out uint processId);

    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    private static extern int GetClassName(IntPtr hwnd, StringBuilder className, int capacity);

    [DllImport("user32.dll")]
    public static extern IntPtr SendMessage(IntPtr hwnd, uint message, IntPtr wparam, IntPtr lparam);

    public static IntPtr FindMainWindow(uint expectedProcessId)
    {
        IntPtr result = IntPtr.Zero;
        EnumWindows((hwnd, _) =>
        {
            uint processId;
            GetWindowThreadProcessId(hwnd, out processId);
            if (processId != expectedProcessId)
            {
                return true;
            }

            StringBuilder className = new StringBuilder(256);
            GetClassName(hwnd, className, className.Capacity);
            if (className.ToString() != "ZsuiMainWindow")
            {
                return true;
            }

            result = hwnd;
            return false;
        }, IntPtr.Zero);
        return result;
    }
}
'@

$process = Start-Process -FilePath $notepad -PassThru -WindowStyle Hidden
try {
    $hwnd = [IntPtr]::Zero
    for ($attempt = 0; $attempt -lt 50 -and $hwnd -eq [IntPtr]::Zero; $attempt++) {
        Start-Sleep -Milliseconds 100
        if ($process.HasExited) {
            throw "native accessibility probe exited before creating its main window"
        }
        $hwnd = [ZsuiWindowsTextAccessibilityProbeNative]::FindMainWindow([uint32]$process.Id)
    }
    if ($hwnd -eq [IntPtr]::Zero) {
        throw "native accessibility probe did not create a ZsuiMainWindow HWND"
    }

    # Focus the editor through the real Win32 message route used by the notepad smoke.
    $editorPoint = [IntPtr](360 -bor (220 -shl 16))
    [void][ZsuiWindowsTextAccessibilityProbeNative]::SendMessage(
        $hwnd,
        0x0201,
        [IntPtr]1,
        $editorPoint
    )
    [void][ZsuiWindowsTextAccessibilityProbeNative]::SendMessage(
        $hwnd,
        0x0202,
        [IntPtr]0,
        $editorPoint
    )

    $root = [System.Windows.Automation.AutomationElement]::FromHandle($hwnd)
    if ($null -eq $root) {
        throw "UI Automation returned no provider for the native HWND"
    }
    $condition = New-Object System.Windows.Automation.PropertyCondition(
        [System.Windows.Automation.AutomationElement]::ControlTypeProperty,
        [System.Windows.Automation.ControlType]::Edit
    )
    $element = $root.FindFirst(
        [System.Windows.Automation.TreeScope]::Descendants,
        $condition
    )
    if ($null -eq $element) {
        throw "UI Automation did not expose the focused self-drawn editor inside the retained semantic tree"
    }
    if ($element.Current.FrameworkId -ne "ZSUI") {
        throw "UI Automation framework id was '$($element.Current.FrameworkId)', expected 'ZSUI'"
    }
    if ($element.Current.ClassName -ne "ZsuiSemantictext_box") {
        throw "UI Automation class name was '$($element.Current.ClassName)', expected 'ZsuiSemantictext_box'"
    }
    if ($element.Current.AutomationId -ne "zsui-semantic-1") {
        throw "UI Automation id was '$($element.Current.AutomationId)', expected 'zsui-semantic-1'"
    }

    $valuePattern = $element.GetCurrentPattern(
        [System.Windows.Automation.ValuePattern]::Pattern
    )
    if ($null -eq $valuePattern) {
        throw "UI Automation ValuePattern was not available"
    }
    if ($valuePattern.Current.IsReadOnly) {
        throw "UI Automation unexpectedly reported the editor as read-only"
    }
    if (-not $valuePattern.Current.Value.Contains("ZSUI Notepad")) {
        throw "UI Automation ValuePattern did not expose the application-owned editor text"
    }

    $textPattern = $element.GetCurrentPattern(
        [System.Windows.Automation.TextPattern]::Pattern
    )
    if ($null -eq $textPattern) {
        throw "UI Automation TextPattern was not available"
    }
    $documentRange = $textPattern.DocumentRange
    if (-not $documentRange.GetText(-1).Contains("ZSUI Notepad")) {
        throw "UI Automation TextPattern did not expose the application-owned editor text"
    }
    $selectionRanges = $textPattern.GetSelection()
    if ($selectionRanges.Length -ne 1) {
        throw "UI Automation TextPattern did not expose exactly one native text selection"
    }
    $movedRange = $documentRange.Clone()
    $moved = $movedRange.MoveEndpointByUnit(
        [System.Windows.Automation.Text.TextPatternRangeEndpoint]::Start,
        [System.Windows.Automation.Text.TextUnit]::Character,
        1
    )
    if ($moved -ne 1 -or [string]::IsNullOrEmpty($movedRange.GetText(4))) {
        throw "UI Automation TextPattern range movement did not preserve readable text"
    }
    $movedRange.ScrollIntoView($true)
    if ($documentRange.GetBoundingRectangles().Length -lt 1) {
        throw "UI Automation TextPattern returned no native shaped text rectangles"
    }

    Write-Output "Windows native text accessibility passed: semantic fragment root -> ZSUI Edit/ValuePattern/TextPattern"
} finally {
    if (-not $process.HasExited) {
        Stop-Process -Id $process.Id -Force
    }
}