using CommunityToolkit.Mvvm.Input;
using Microsoft.UI.Xaml.Automation.Peers;
using Microsoft.UI.Xaml.Automation.Provider;
using XFEEDToolBox.Controls;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Navigation;
using XFEEDToolBox.Views;
using XFEEDToolBox.Services;
namespace XFEEDToolBox;
public partial class App
{
private static async Task RunInterfaceOnlyProbeAsync(string path)
{
var directory=Path.GetDirectoryName(Path.GetFullPath(path))!;Directory.CreateDirectory(directory);object? result=null;string? failure=null;
if(Environment.GetCommandLineArgs().Contains("--preview-layout-only"))
{
try{result=await RunPreviewLayoutProbeAsync(directory);}catch(Exception error){failure=error.ToString();}
await File.WriteAllTextAsync(path,System.Text.Json.JsonSerializer.Serialize(new{result,failure,uiError=ViewModel.ErrorMessage,gameFlightCommandsInvoked=false},new System.Text.Json.JsonSerializerOptions{WriteIndented=true}));MainWindow.Close();return;
}
try
{
MainWindow.AppWindow.Move(new(-30000,-30000));MainWindow.AppWindow.Resize(new(1900,1160));MainWindow.AppWindow.Show(false);
var modes=Environment.GetCommandLineArgs();
if(modes.Contains("--engineering-grade-only"))result=await RunEngineeringGradeProbeAsync(directory);
else if(modes.Contains("--system-atlas-only"))result=await RunSystemAtlasProbeAsync(directory);
else if(modes.Contains("--system-map-composition-only"))result=await RunSystemCompositionProbeAsync(directory);
else if(modes.Contains("--market-only"))result=await RunMarketPageProbeAsync(directory);
else if(modes.Contains("--market-cache-settings-only"))result=await RunMarketCacheSettingsProbeAsync(directory);
else if(modes.Contains("--activity-only"))result=await RunActivityLogProbeAsync(directory);
else if(modes.Contains("--system-map-only"))result=await RunLocalSystemMapProbeAsync(directory);
else if(modes.Contains("--trade-planner-only"))result=await RunTradePlannerProbeAsync(directory);
else if(modes.Contains("--probe-hologram-detail"))result=await RunHologramDetailProbeAsync(directory);
else if(modes.Contains("--refit-appearance-only"))result=await RunRefitAppearanceProbeAsync(directory);
else if(modes.Contains("--checklist-only"))result=await RunFloatingChecklistProbeAsync(directory);
else if(modes.Contains("--places-only"))result=await RunPlacesProbeAsync(directory);
else if(modes.Contains("--probe-navigation-overlap"))result=await RunGalaxyUi0416ProbeAsync(directory);
else if(modes.Contains("--probe-galaxy-nebula"))result=await RunGalaxyNebulaProbeAsync(directory);
else result=await RunInterfaceRefinementProbeAsync(directory);
}
catch(Exception error){failure=error.ToString();}
await File.WriteAllTextAsync(path,System.Text.Json.JsonSerializer.Serialize(new{result,failure,uiError=ViewModel.ErrorMessage,gameFlightCommandsInvoked=false,gameRestartInvoked=false},new System.Text.Json.JsonSerializerOptions{WriteIndented=true}));MainWindow.Close();
}
private static async Task<object> RunFloatingChecklistProbeAsync(string directory)
{
static void Require(bool value,string message){if(!value)throw new InvalidOperationException(message);}
var original=ViewModel.UpgradeNeeds.Select(row=>row.Record).ToArray();
try
{
ViewModel.UpgradeNeeds.Apply([new("modifiedembeddedfirmware","Modified Embedded Firmware",35,0),new("nickel","Nickel",16,68),new("vanadium","Vanadium",5,15)]);
Require(ViewModel.UpgradeMissingMaterialCount==1,"The badge must count one missing material type, not 35 units or three total types.");
using var popup=new FloatingUpgradeList(WinRT.Interop.WindowNative.GetWindowHandle(MainWindow),0,0,hidden:true);
popup.MoveTo(-30000,-30000);popup.Show();await Task.Delay(150);
Require(popup.IsToolPopup&&popup.Panel.IsLoaded,"Checklist must remain a captionless native tool popup.");
Require(Descendants(popup.Surface).OfType<FrameworkElement>().Count(grip=>Microsoft.UI.Xaml.Automation.AutomationProperties.GetName(grip)=="拖动调整清单大小")==8,"All four edges and four corners need resize handles.");
var initial=popup.Bounds;
popup.TogglePin();Require(popup.IsPinned&&popup.NativeTopmost,"Pin must set native topmost.");
popup.ResizeBy(FloatingUpgradeList.ResizeEdge.Right|FloatingUpgradeList.ResizeEdge.Bottom,150,120);await Task.Delay(80);
var enlarged=popup.Bounds;
Require(enlarged.X==initial.X&&enlarged.Y==initial.Y&&enlarged.Width==initial.Width+150&&enlarged.Height==initial.Height+120,"Right/bottom resize failed.");
var scale=popup.Surface.XamlRoot.RasterizationScale;
Require(Math.Abs(popup.Surface.ActualWidth*scale-enlarged.Width)<2&&Math.Abs(popup.Surface.ActualHeight*scale-enlarged.Height)<2,"XAML content did not follow the native window size.");
foreach(var edges in new[]{FloatingUpgradeList.ResizeEdge.Left,FloatingUpgradeList.ResizeEdge.Right,FloatingUpgradeList.ResizeEdge.Top,FloatingUpgradeList.ResizeEdge.Bottom,FloatingUpgradeList.ResizeEdge.Left|FloatingUpgradeList.ResizeEdge.Top,FloatingUpgradeList.ResizeEdge.Right|FloatingUpgradeList.ResizeEdge.Top,FloatingUpgradeList.ResizeEdge.Left|FloatingUpgradeList.ResizeEdge.Bottom,FloatingUpgradeList.ResizeEdge.Right|FloatingUpgradeList.ResizeEdge.Bottom})
{
var before=popup.Bounds;popup.ResizeBy(edges,edges.HasFlag(FloatingUpgradeList.ResizeEdge.Left)?-16:16,edges.HasFlag(FloatingUpgradeList.ResizeEdge.Top)?-12:12);await Task.Delay(25);var after=popup.Bounds;
Require(after.Width==before.Width+((edges&(FloatingUpgradeList.ResizeEdge.Left|FloatingUpgradeList.ResizeEdge.Right))!=0?16:0)&&after.Height==before.Height+((edges&(FloatingUpgradeList.ResizeEdge.Top|FloatingUpgradeList.ResizeEdge.Bottom))!=0?12:0),"Resize failed for "+edges);
Require(edges.HasFlag(FloatingUpgradeList.ResizeEdge.Left)?after.X+after.Width==before.X+before.Width:after.X==before.X,"Opposite horizontal edge moved for "+edges);
Require(edges.HasFlag(FloatingUpgradeList.ResizeEdge.Top)?after.Y+after.Height==before.Y+before.Height:after.Y==before.Y,"Opposite vertical edge moved for "+edges);
}
await CaptureRefinementElementAsync(popup.Surface,Path.Combine(directory,"checklist-enlarged.png"));
popup.ResizeBy(FloatingUpgradeList.ResizeEdge.Right|FloatingUpgradeList.ResizeEdge.Bottom,-100000,-100000);await Task.Delay(100);
var minimum=popup.Bounds;scale=popup.Surface.XamlRoot.RasterizationScale;
Require(Math.Abs(minimum.Width/scale-FloatingUpgradeList.MinimumWidth)<2&&Math.Abs(minimum.Height/scale-FloatingUpgradeList.MinimumHeight)<2,"Minimum readable size was not enforced.");
var scroller=(ScrollViewer)popup.Panel.FindName("UpgradePreviewScroller");Require(scroller.ActualHeight>30,"Minimum-size checklist has no material viewport.");
var bottom=new Windows.Foundation.Point(0,popup.Panel.ActualHeight);var windowBottom=popup.Panel.TransformToVisual(popup.Surface).TransformPoint(bottom);
Require(windowBottom.Y<=popup.Surface.ActualHeight,"Details button overflowed the resized window.");
Require(popup.IsPinned&&popup.NativeTopmost&&popup.IsToolPopup,"Resizing must retain topmost and popup styling.");
await CaptureRefinementElementAsync(popup.Surface,Path.Combine(directory,"checklist-minimum.png"));
popup.TogglePin();Require(!popup.IsPinned&&!popup.NativeTopmost,"Unpin must still work after resize.");
ViewModel.UpgradeNeeds.Apply([new("nickel","Nickel",16,68)]);Require(ViewModel.UpgradeMissingMaterialCount==0&&!ViewModel.HasUpgradeMaterialShortages,"Complete materials must hide the badge.");
return new{missingTypesBadge=1,allEightResizeDirections=true,minimumSizeEnforced=true,contentResizes=true,pinningRetained=true,captionlessPopupRetained=true,initial=new{initial.Width,initial.Height},enlarged=new{enlarged.Width,enlarged.Height},minimum=new{minimum.Width,minimum.Height},gameCommandsInvoked=false};
}
finally{ViewModel.UpgradeNeeds.Apply(original);}
}
private static async Task<object> RunInterfaceRefinementProbeAsync(string directory)
{
static void Require(bool value,string message){if(!value)throw new InvalidOperationException(message);}
static void Invoke(Button button)=>((IInvokeProvider)new ButtonAutomationPeer(button).GetPattern(PatternInterface.Invoke)).Invoke();
async Task<Page> Open(Type type){AppShellPage.Current!.Navigate(type);await Task.Delay(180);return AppShellPage.Current.CurrentPage!;}
var home=(MainPage)await Open(typeof(MainPage));
Require(!Descendants(home).OfType<TextBlock>().Any(t=>t.Text=="本机档案已读取"),"Removed dashboard badge is still visible.");
var dashboard=(StackPanel)home.FindName("DashboardContent");
Require(dashboard.Children.Last() is Border{Child:ShipStatusPanel},"Ship properties must be the final, full-width dashboard card.");
Require(!Descendants(home).OfType<TextBlock>().Any(t=>t.Text=="数据接入"),"Removed data-access card remains visible.");
await Task.Delay(500);
var shipProperties=(ShipStatusPanel)home.FindName("HomeShipStatus");
Require(((SelectorBar)shipProperties.FindName("StatusTabs")).Items.Count==4,"Ship specifications and power tab is missing.");
shipProperties.StartBringIntoView(new BringIntoViewOptions{AnimationDesired=false});await Task.Delay(150);
await CaptureRefinementElementAsync(shipProperties,Path.Combine(directory,"ship-properties.png"));
var host=(StackPanel)home.FindName("DashboardContent");var fixture=new StackPanel{Spacing=12};host.Children.Insert(0,fixture);
var idle=new StackPanel();idle.Children.Add(new TextBlock{Text="异步操作检查"});
var button=new Button{Content=idle};fixture.Children.Add(button);var cancelled=0;
var action=new AsyncRelayCommand(async token=>{try{await Task.Delay(Timeout.Infinite,token);}catch(OperationCanceledException){cancelled++;}});
AsyncAction.SetCommand(button,action);await Task.Delay(50);Invoke(button);await Task.Delay(50);
Require(action.IsRunning&&button.Content as string=="取消"&&button.IsEnabled,"Running action must remain cancellable on the original button.");
await CaptureRefinementElementAsync(button,Path.Combine(directory,"cancel-button.png"));Invoke(button);await action.ExecutionTask!.WaitAsync(TimeSpan.FromSeconds(3));await Task.Delay(40);
Require(cancelled==1&&!action.IsRunning&&ReferenceEquals(button.Content,idle),"Cancellation did not restore original content.");
var finish=new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);var bar=new AppBarButton{Label="载入附近星系",Icon=new SymbolIcon(Symbol.Download)};fixture.Children.Add(bar);
var explicitAction=new AsyncRelayCommand(()=>finish.Task);AsyncAction.SetCommand(bar,explicitAction);AsyncAction.SetCancelCommand(bar,new RelayCommand(()=>finish.TrySetResult()));await Task.Delay(30);Invoke(bar);await Task.Delay(30);
Require(bar.Label=="取消"&&bar.IsEnabled,"Command bar must offer cancellation on its original button.");
Invoke(bar);await explicitAction.ExecutionTask!.WaitAsync(TimeSpan.FromSeconds(3));await Task.Delay(30);Require(bar.Label=="载入附近星系","Command bar label did not restore.");
var deferred=new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);var deferredAction=new AsyncRelayCommand(()=>deferred.Task);
AsyncAction.SetCommand(button,deferredAction);Invoke(button);await Task.Delay(30);fixture.Children.Remove(button);deferred.SetResult();await deferredAction.ExecutionTask!;
fixture.Children.Add(button);await Task.Delay(40);Require(ReferenceEquals(button.Content,idle),"Unloaded action retained its cancel state on return.");
fixture.Children.Clear();var gallery=new StackPanel{Orientation=Orientation.Horizontal,Spacing=16};fixture.Children.Add(gallery);var opened=0;var failures=new List<string>();
void AddIcon(UserControl control,string label)
{
var image=(Image)control.Content;image.ImageOpened+=(_,_)=>opened++;image.ImageFailed+=(_,error)=>failures.Add(label+": "+error.ErrorMessage);
var panel=new StackPanel{Spacing=8,Width=64};panel.Children.Add(control);panel.Children.Add(new TextBlock{Text=label});gallery.Children.Add(panel);
}
foreach(var category in new[]{"Raw","Manufactured","Encoded"})AddIcon(new MaterialIcon{Category=category},category);
for(var grade=1;grade<=5;grade++)AddIcon(new MaterialGradeIcon{Grade=grade},$"G{grade}");
var deadline=DateTimeOffset.UtcNow.AddSeconds(4);while(opened<8&&failures.Count==0&&DateTimeOffset.UtcNow<deadline)await Task.Delay(50);
Require(opened==8&&failures.Count==0,"Imported game vectors did not load: "+string.Join("; ",failures));
await CaptureRefinementElementAsync(gallery,Path.Combine(directory,"game-material-icons.png"));host.Children.Remove(fixture);
fixture=new StackPanel();host.Children.Insert(0,fixture);
var map=new GalaxyMapView{Width=800,Height=500,CanPlanRoute=true,Centre=new(0,0,0),Systems=[new("界面恒星",new(5,2,3),42,"K")]};fixture.Children.Add(map);
await Task.Delay(120);map.RenderNowForProbe();var star=map.Systems[0];map.FocusSystem(star);await Task.Delay(350);map.RenderNowForProbe();
var centreBefore=map.ProjectForProbe(star.Position);map.ApplyDragDelta(40,20,false);map.RenderNowForProbe();var centreAfter=map.ProjectForProbe(star.Position);
Require(Math.Abs(centreBefore.X-centreAfter.X)<.1&&Math.Abs(centreBefore.Y-centreAfter.Y)<.1,"Selected star must remain the rotation pivot.");
var planned=0;map.TargetChosen+=(_,_)=>{planned++;map.Planning=true;};map.BeginPointer(centreAfter,false);await Task.Delay(270);
Require(map.ShowsHold&&map.HoldProgress is >10 and <90,"Long press must animate progress around the selected star.");
await CaptureRefinementElementAsync(map,Path.Combine(directory,"map-hold-progress.png"));await Task.Delay(430);map.EndPointer(centreAfter);
Require(planned==1&&map.ShowsPlanning,"Long press must submit once and display planning until confirmation.");await Task.Delay(80);map.Planning=false;
Require(!map.ShowsPlanning&&!map.ShowsHold,"Confirmed planning must clear the pending ring.");map.ClearSelection();Require(map.Selection is null,"Selection must be cancellable.");
map.Systems=BenchmarkSystems(25000);map.Centre=new(0,0,0);map.RenderNowForProbe();var sceneBuilds=map.SceneRebuilds;var vectorNodes=map.VectorNodesCreated;var cpu=new double[90];
for(var i=0;i<cpu.Length;i++){var at=System.Diagnostics.Stopwatch.GetTimestamp();map.ApplyDragDelta(.35,.12,false);map.RenderNowForProbe();cpu[i]=System.Diagnostics.Stopwatch.GetElapsedTime(at).TotalMilliseconds;}
Array.Sort(cpu);Require(map.MapGraphicsBackend.StartsWith("Direct3D")&&map.SceneRebuilds==sceneBuilds&&map.VectorNodesCreated==vectorNodes,"Map rendering must batch stars without rebuilding nodes while dragging.");
var restFrames=map.FrameCount;await Task.Delay(80);Require(map.FrameCount==restFrames,"Idle map must not redraw.");
var mapPerformance=new{systems=25000,map.MapGraphicsBackend,meanCpuMilliseconds=cpu.Average(),p95CpuMilliseconds=cpu[(int)(cpu.Length*.95)],map.VisibleStars,map.GraphicsDrawCalls,stableScene=true,idleDoesNotRedraw=true};host.Children.Remove(fixture);
var editor=(RefitPage)await Open(typeof(RefitPage));ViewModel.RebuildRefitCommand.Execute(null);await Task.Delay(100);
var slot=ViewModel.RefitVisibleSlots.First(s=>s.Module?.Engineering?.Quality is not null);ViewModel.RefitSlot=slot;ViewModel.RefitGrade=5;
var slots=(GridView)editor.FindName("SlotList");slots.ScrollIntoView(slot);await Task.Delay(100);
var container=slots.ContainerFromItem(slot);var property=ViewModel.RefitProperties.Single(p=>p.OriginalName=="Integrity");var blueprint=ViewModel.RefitBlueprint;var effect=ViewModel.RefitEffect?.Key;
var p=ViewModel.CurrentPlayer;var old=slot.Module!;var changed=old with{Health=.123,AmmoInClip=9,AmmoInHopper=234,Engineering=old.Engineering! with{Quality=.81}};
var snapshot=p with{Ships=p.Ships.Select(s=>s.ShipId==ViewModel.RefitDocument!.ShipId?s with{Modules=s.Modules.Select(m=>m.Slot==slot.Slot?changed:m).ToArray()}:s).ToArray()};
ViewModel.RefreshRefitProbePlayer(snapshot);await Task.Delay(100);
var ring=(ProgressRing)editor.FindName("CurrentEngineeringProgress");
Require(ViewModel.RefitDocument!.Slots.First(s=>s.Slot==slot.Slot).Target?.Grade==5&&!Descendants(editor).OfType<Button>().Any(b=>b.Content as string=="保存此模块的工程目标"),"Engineering targets must save automatically without a save-target button.");
Require(ReferenceEquals(slot,ViewModel.RefitSlot)&&ReferenceEquals(container,slots.ContainerFromItem(slot))&&ReferenceEquals(property,ViewModel.RefitProperties.Single(r=>r.OriginalName=="Integrity")),"Journal update recreated selected slot or property row.");
Require(ring.Value==81&&ViewModel.RefitGrade==5&&ViewModel.RefitBlueprint==blueprint&&ViewModel.RefitEffect?.Key==effect,$"Journal update reset target controls or failed to update progress: ring={ring.Value}, current={ViewModel.RefitCurrentProgress}, grade={ViewModel.RefitGrade}, blueprint={ViewModel.RefitBlueprint}/{blueprint}, effect={ViewModel.RefitEffect?.Key}/{effect}.");
var moduleData=(ScrollViewer)editor.FindName("ModuleDataView");
moduleData.ChangeView(null,moduleData.ScrollableHeight,null,true);await Task.Delay(80);
await CaptureRefinementElementAsync((Border)editor.FindName("InspectorPanel"),Path.Combine(directory,"module-data-updated.png"));
Require(Descendants(editor).OfType<BilingualText>().Any(t=>t.Text==changed.HealthText),$"Live property value did not reach its bound control: model={property.Value}, expected={changed.HealthText}.");
moduleData.ChangeView(null,0,null,true);await Task.Delay(50);
Require(!slots.CanReorderItems&&!slots.CanDragItems&&!Descendants(editor).OfType<PivotItem>().Any(i=>i.Header as string=="替换模块"),"Old reorder or module replacement UI remains.");
await CapturePageAsync(editor,Path.Combine(directory,"ship-editor-live.png"));
var inspectorTabs=(SelectorBar)editor.FindName("InspectorTabs");
Require(inspectorTabs.Items[0].Text=="模块数据"&&inspectorTabs.Items[1].Text=="改装","Inspector tab labels do not match the game.");
var inspector=(Border)editor.FindName("InspectorPanel");
Require(((Grid)inspector.Child).Children.First()==inspectorTabs,"Inspector switching must be above the module title.");
inspectorTabs.SelectedItem=inspectorTabs.Items[1];await Task.Delay(50);
Require(((FrameworkElement)editor.FindName("ModuleEngineeringView")).Visibility==Visibility.Visible&&((FrameworkElement)editor.FindName("ModuleDataView")).Visibility==Visibility.Collapsed,"Engineering tab did not switch content.");
await CaptureRefinementElementAsync(inspector,Path.Combine(directory,"module-engineering.png"));
inspectorTabs.SelectedItem=inspectorTabs.Items[0];await Task.Delay(50);
var changeButton=Descendants((DependencyObject)container).OfType<Button>().Single(b=>ReferenceEquals(b.Tag,slot));Invoke(changeButton);await Task.Delay(180);
Require(AppShellPage.Current!.CurrentPage is RefitModulePickerPage&&ViewModel.RefitChoosingType&&ViewModel.RefitModuleTypes.Count>0,"Card edit did not open compatible types page.");
var picker=(RefitModulePickerPage)AppShellPage.Current.CurrentPage!;await Task.Delay(350);await CapturePageAsync(picker,Path.Combine(directory,"module-types.png"));
var type=ViewModel.RefitModuleTypes.First(t=>ViewModel.RefitAvailableModules.Any(c=>XFEEDToolBox.Core.Outfitting.RefitModuleBrowser.TypeKey(c.Definition)==t.Key&&!c.Owned&&c.Module.Item!=old.Item));ViewModel.RefitSelectedModuleType=type;await Task.Delay(120);
Require(ViewModel.RefitChoosingModel&&ViewModel.RefitTypeModels.Count>0&&ViewModel.RefitTypeModels.All(c=>XFEEDToolBox.Core.Outfitting.RefitModuleBrowser.TypeKey(c.Definition)==type.Key),"Type selection did not reveal only its compatible models.");
var choice=ViewModel.RefitTypeModels.First(c=>!c.Owned&&c.Module.Item!=old.Item);ViewModel.RefitInstallModule=choice;
await CapturePageAsync(picker,Path.Combine(directory,"module-models.png"));Invoke((Button)picker.FindName("InstallButton"));await Task.Delay(180);
Require(AppShellPage.Current.CurrentPage is RefitPage&&ViewModel.RefitSlot?.Module?.Item==choice.Module.Item,"Model selection did not install and return to editor.");ViewModel.RebuildRefitCommand.Execute(null);
if(Environment.GetCommandLineArgs().Contains("--properties-only"))
return new{homePropertiesAtBottom=true,dataAccessRemoved=true,topModuleTabsSwitch=true,slotAndPropertyIdentityPreserved=true,targetsPreserved=true,
powerCapacity=ViewModel.ShipPowerCapacityText,retractedPower=ViewModel.ShipPowerRetractedText,deployedPower=ViewModel.ShipPowerDeployedText,realGameActionsInvoked=false};
var navigation=await Open(typeof(NavigationPage));
var fullMap=(GalaxyMapView)navigation.FindName("Map");var mapRegion=(Grid)navigation.FindName("MapRegion");var sidebar=(HorizontalPanel)navigation.FindName("SidebarExpander");
Require(Math.Abs(fullMap.ActualWidth-mapRegion.ActualWidth)<1&&!Descendants(navigation).OfType<Button>().Any(b=>b.Content as string=="规划航线"),"Map must fill its page without a route-plan button.");
var mapWidth=fullMap.ActualWidth;sidebar.IsExpanded=false;await Task.Delay(80);Require(Math.Abs(fullMap.ActualWidth-mapWidth)<1,"Sidebar collapse must leave the map full width.");sidebar.IsExpanded=true;
var overlay=(Border)navigation.FindName("SidebarOverlay");Require(overlay.Background is Microsoft.UI.Xaml.Media.AcrylicBrush tint&&Math.Abs(tint.Opacity-.78)<.00001&&Math.Abs(tint.TintOpacity-.32)<.00001,"Map overlay must retain its translucent acrylic finish.");
ViewModel.GalaxyPlaceKindIndex=2;((HorizontalPanel)navigation.FindName("PlacesExpander")).IsExpanded=true;await Task.Delay(100);
var engineerInput=(ListView)navigation.FindName("EngineerLocationInput");Require(engineerInput.Items.Count>=38&&Descendants(engineerInput).OfType<PersonPicture>().Any(p=>p.ProfilePicture is not null),"Engineer location picker must include portraits and locations.");
await CapturePageAsync(navigation,Path.Combine(directory,"map-full-page-engineers.png"));
var states=new[]{NavigationLegState.Completed,NavigationLegState.Running,NavigationLegState.Waiting,NavigationLegState.Warning,NavigationLegState.Error};
ViewModel.NavigationLegs.Apply(states.Select((state,index)=>new NavigationLegPreview(index+1,index+1,$"界面检查 {index+1}","K",20,true,state==NavigationLegState.Completed,index==1,null){ExecutionState=state,Message=state is NavigationLegState.Error or NavigationLegState.Warning?"状态图标检查":""}));ViewModel.HasNoNavigationLegs=false;
await Task.Delay(160);var route=(ListView)navigation.FindName("RouteList");
foreach(var leg in ViewModel.NavigationLegs.ToArray())
{
route.ScrollIntoView(leg);await Task.Delay(50);var row=route.ContainerFromItem(leg);
Require(row is not null,"Route row was not realised.");
Require(leg.IsRunning?Descendants((DependencyObject)row!).OfType<ProgressRing>().Any(r=>r.IsActive&&r.Visibility==Visibility.Visible):Descendants((DependencyObject)row!).OfType<FontIcon>().Any(i=>i.Visibility==Visibility.Visible),"Per-hop status icon did not render: "+leg.State);
}
route.ScrollIntoView(ViewModel.NavigationLegs[0]);await Task.Delay(60);
await CapturePageAsync(navigation,Path.Combine(directory,"route-status-icons.png"));
using(var popup=new FloatingUpgradeList(WinRT.Interop.WindowNative.GetWindowHandle(MainWindow),0,0,hidden:true))
{
popup.MoveTo(-30000,-30000);popup.Show();await Task.Delay(180);
Require(popup.IsToolPopup&&popup.Panel.IsLoaded&&popup.Surface.ActualWidth>0,"Floating checklist must be a loaded native tool popup without caption chrome.");
var before=popup.Position;popup.MoveTo(before.X+24,before.Y+16);Require(popup.Position==(before.X+24,before.Y+16),"Floating checklist position did not move.");
Invoke((Button)popup.Panel.FindName("PinListButton"));await Task.Delay(40);Require(popup.IsPinned&&popup.NativeTopmost,"Pin did not apply native always-on-top state.");
await CaptureRefinementElementAsync(popup.Surface,Path.Combine(directory,"floating-checklist.png"));
Invoke((Button)popup.Panel.FindName("PinListButton"));await Task.Delay(40);Require(!popup.IsPinned&&!popup.NativeTopmost,"Unpin did not restore normal z-order.");
}
return new{originalButtonCancels=true,commandBarCancels=true,unloadedContentRestored=true,gameMaterialIconsLoaded=opened,slotAndPropertyIdentityPreserved=true,targetsPreserved=true,targetsAutoSaved=true,compatibleTypeAndModelPages=true,perHopStates=states.Select(s=>s.ToString()).ToArray(),nativePopupMovesAndPins=true,fullPageAcrylicMap=true,engineerLocations=engineerInput.Items.Count,longPressProgressAndPlanning=true,selectionRotationPivot=true,mapPerformance,realGameActionsInvoked=false};
}
}
using CommunityToolkit.Mvvm.Input;
using Microsoft.UI.Xaml.Automation.Peers;
using Microsoft.UI.Xaml.Automation.Provider;
using XFEEDToolBox.Controls;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Navigation;
using XFEEDToolBox.Views;
using XFEEDToolBox.Services;
namespace XFEEDToolBox;
public partial class App
{
private static async Task RunInterfaceOnlyProbeAsync(string path)
{
var directory=Path.GetDirectoryName(Path.GetFullPath(path))!;Directory.CreateDirectory(directory);object? result=null;string? failure=null;
if(Environment.GetCommandLineArgs().Contains("--preview-layout-only"))
{
try{result=await RunPreviewLayoutProbeAsync(directory);}catch(Exception error){failure=error.ToString();}
await File.WriteAllTextAsync(path,System.Text.Json.JsonSerializer.Serialize(new{result,failure,uiError=ViewModel.ErrorMessage,gameFlightCommandsInvoked=false},new System.Text.Json.JsonSerializerOptions{WriteIndented=true}));MainWindow.Close();return;
}
try
{
MainWindow.AppWindow.Move(new(-30000,-30000));MainWindow.AppWindow.Resize(new(1900,1160));MainWindow.AppWindow.Show(false);
var modes=Environment.GetCommandLineArgs();
if(modes.Contains("--engineering-grade-only"))result=await RunEngineeringGradeProbeAsync(directory);
else if(modes.Contains("--system-atlas-only"))result=await RunSystemAtlasProbeAsync(directory);
else if(modes.Contains("--system-map-composition-only"))result=await RunSystemCompositionProbeAsync(directory);
else if(modes.Contains("--market-only"))result=await RunMarketPageProbeAsync(directory);
else if(modes.Contains("--market-cache-settings-only"))result=await RunMarketCacheSettingsProbeAsync(directory);
else if(modes.Contains("--activity-only"))result=await RunActivityLogProbeAsync(directory);
else if(modes.Contains("--system-map-only"))result=await RunLocalSystemMapProbeAsync(directory);
else if(modes.Contains("--trade-planner-only"))result=await RunTradePlannerProbeAsync(directory);
else if(modes.Contains("--probe-hologram-detail"))result=await RunHologramDetailProbeAsync(directory);
else if(modes.Contains("--refit-appearance-only"))result=await RunRefitAppearanceProbeAsync(directory);
else if(modes.Contains("--checklist-only"))result=await RunFloatingChecklistProbeAsync(directory);
else if(modes.Contains("--places-only"))result=await RunPlacesProbeAsync(directory);
else if(modes.Contains("--probe-navigation-overlap"))result=await RunGalaxyUi0416ProbeAsync(directory);
else if(modes.Contains("--probe-galaxy-nebula"))result=await RunGalaxyNebulaProbeAsync(directory);
else result=await RunInterfaceRefinementProbeAsync(directory);
}
catch(Exception error){failure=error.ToString();}
await File.WriteAllTextAsync(path,System.Text.Json.JsonSerializer.Serialize(new{result,failure,uiError=ViewModel.ErrorMessage,gameFlightCommandsInvoked=false,gameRestartInvoked=false},new System.Text.Json.JsonSerializerOptions{WriteIndented=true}));MainWindow.Close();
}
private static async Task<object> RunFloatingChecklistProbeAsync(string directory)
{
static void Require(bool value,string message){if(!value)throw new InvalidOperationException(message);}
var original=ViewModel.UpgradeNeeds.Select(row=>row.Record).ToArray();
try
{
ViewModel.UpgradeNeeds.Apply([new("modifiedembeddedfirmware","Modified Embedded Firmware",35,0),new("nickel","Nickel",16,68),new("vanadium","Vanadium",5,15)]);
Require(ViewModel.UpgradeMissingMaterialCount==1,"The badge must count one missing material type, not 35 units or three total types.");
using var popup=new FloatingUpgradeList(WinRT.Interop.WindowNative.GetWindowHandle(MainWindow),0,0,hidden:true);
popup.MoveTo(-30000,-30000);popup.Show();await Task.Delay(150);
Require(popup.IsToolPopup&&popup.Panel.IsLoaded,"Checklist must remain a captionless native tool popup.");
Require(Descendants(popup.Surface).OfType<FrameworkElement>().Count(grip=>Microsoft.UI.Xaml.Automation.AutomationProperties.GetName(grip)=="拖动调整清单大小")==8,"All four edges and four corners need resize handles.");
var initial=popup.Bounds;
popup.TogglePin();Require(popup.IsPinned&&popup.NativeTopmost,"Pin must set native topmost.");
popup.ResizeBy(FloatingUpgradeList.ResizeEdge.Right|FloatingUpgradeList.ResizeEdge.Bottom,150,120);await Task.Delay(80);
var enlarged=popup.Bounds;
Require(enlarged.X==initial.X&&enlarged.Y==initial.Y&&enlarged.Width==initial.Width+150&&enlarged.Height==initial.Height+120,"Right/bottom resize failed.");
var scale=popup.Surface.XamlRoot.RasterizationScale;
Require(Math.Abs(popup.Surface.ActualWidth*scale-enlarged.Width)<2&&Math.Abs(popup.Surface.ActualHeight*scale-enlarged.Height)<2,"XAML content did not follow the native window size.");
foreach(var edges in new[]{FloatingUpgradeList.ResizeEdge.Left,FloatingUpgradeList.ResizeEdge.Right,FloatingUpgradeList.ResizeEdge.Top,FloatingUpgradeList.ResizeEdge.Bottom,FloatingUpgradeList.ResizeEdge.Left|FloatingUpgradeList.ResizeEdge.Top,FloatingUpgradeList.ResizeEdge.Right|FloatingUpgradeList.ResizeEdge.Top,FloatingUpgradeList.ResizeEdge.Left|FloatingUpgradeList.ResizeEdge.Bottom,FloatingUpgradeList.ResizeEdge.Right|FloatingUpgradeList.ResizeEdge.Bottom})
{
var before=popup.Bounds;popup.ResizeBy(edges,edges.HasFlag(FloatingUpgradeList.ResizeEdge.Left)?-16:16,edges.HasFlag(FloatingUpgradeList.ResizeEdge.Top)?-12:12);await Task.Delay(25);var after=popup.Bounds;
Require(after.Width==before.Width+((edges&(FloatingUpgradeList.ResizeEdge.Left|FloatingUpgradeList.ResizeEdge.Right))!=0?16:0)&&after.Height==before.Height+((edges&(FloatingUpgradeList.ResizeEdge.Top|FloatingUpgradeList.ResizeEdge.Bottom))!=0?12:0),"Resize failed for "+edges);
Require(edges.HasFlag(FloatingUpgradeList.ResizeEdge.Left)?after.X+after.Width==before.X+before.Width:after.X==before.X,"Opposite horizontal edge moved for "+edges);
Require(edges.HasFlag(FloatingUpgradeList.ResizeEdge.Top)?after.Y+after.Height==before.Y+before.Height:after.Y==before.Y,"Opposite vertical edge moved for "+edges);
}
await CaptureRefinementElementAsync(popup.Surface,Path.Combine(directory,"checklist-enlarged.png"));
popup.ResizeBy(FloatingUpgradeList.ResizeEdge.Right|FloatingUpgradeList.ResizeEdge.Bottom,-100000,-100000);await Task.Delay(100);
var minimum=popup.Bounds;scale=popup.Surface.XamlRoot.RasterizationScale;
Require(Math.Abs(minimum.Width/scale-FloatingUpgradeList.MinimumWidth)<2&&Math.Abs(minimum.Height/scale-FloatingUpgradeList.MinimumHeight)<2,"Minimum readable size was not enforced.");
var scroller=(ScrollViewer)popup.Panel.FindName("UpgradePreviewScroller");Require(scroller.ActualHeight>30,"Minimum-size checklist has no material viewport.");
var bottom=new Windows.Foundation.Point(0,popup.Panel.ActualHeight);var windowBottom=popup.Panel.TransformToVisual(popup.Surface).TransformPoint(bottom);
Require(windowBottom.Y<=popup.Surface.ActualHeight,"Details button overflowed the resized window.");
Require(popup.IsPinned&&popup.NativeTopmost&&popup.IsToolPopup,"Resizing must retain topmost and popup styling.");
await CaptureRefinementElementAsync(popup.Surface,Path.Combine(directory,"checklist-minimum.png"));
popup.TogglePin();Require(!popup.IsPinned&&!popup.NativeTopmost,"Unpin must still work after resize.");
ViewModel.UpgradeNeeds.Apply([new("nickel","Nickel",16,68)]);Require(ViewModel.UpgradeMissingMaterialCount==0&&!ViewModel.HasUpgradeMaterialShortages,"Complete materials must hide the badge.");
return new{missingTypesBadge=1,allEightResizeDirections=true,minimumSizeEnforced=true,contentResizes=true,pinningRetained=true,captionlessPopupRetained=true,initial=new{initial.Width,initial.Height},enlarged=new{enlarged.Width,enlarged.Height},minimum=new{minimum.Width,minimum.Height},gameCommandsInvoked=false};
}
finally{ViewModel.UpgradeNeeds.Apply(original);}
}
private static async Task<object> RunInterfaceRefinementProbeAsync(string directory)
{
static void Require(bool value,string message){if(!value)throw new InvalidOperationException(message);}
static void Invoke(Button button)=>((IInvokeProvider)new ButtonAutomationPeer(button).GetPattern(PatternInterface.Invoke)).Invoke();
async Task<Page> Open(Type type){AppShellPage.Current!.Navigate(type);await Task.Delay(180);return AppShellPage.Current.CurrentPage!;}
var home=(MainPage)await Open(typeof(MainPage));
Require(!Descendants(home).OfType<TextBlock>().Any(t=>t.Text=="本机档案已读取"),"Removed dashboard badge is still visible.");
var dashboard=(StackPanel)home.FindName("DashboardContent");
Require(dashboard.Children.Last() is Border{Child:ShipStatusPanel},"Ship properties must be the final, full-width dashboard card.");
Require(!Descendants(home).OfType<TextBlock>().Any(t=>t.Text=="数据接入"),"Removed data-access card remains visible.");
await Task.Delay(500);
var shipProperties=(ShipStatusPanel)home.FindName("HomeShipStatus");
Require(((SelectorBar)shipProperties.FindName("StatusTabs")).Items.Count==4,"Ship specifications and power tab is missing.");
shipProperties.StartBringIntoView(new BringIntoViewOptions{AnimationDesired=false});await Task.Delay(150);
await CaptureRefinementElementAsync(shipProperties,Path.Combine(directory,"ship-properties.png"));
var host=(StackPanel)home.FindName("DashboardContent");var fixture=new StackPanel{Spacing=12};host.Children.Insert(0,fixture);
var idle=new StackPanel();idle.Children.Add(new TextBlock{Text="异步操作检查"});
var button=new Button{Content=idle};fixture.Children.Add(button);var cancelled=0;
var action=new AsyncRelayCommand(async token=>{try{await Task.Delay(Timeout.Infinite,token);}catch(OperationCanceledException){cancelled++;}});
AsyncAction.SetCommand(button,action);await Task.Delay(50);Invoke(button);await Task.Delay(50);
Require(action.IsRunning&&button.Content as string=="取消"&&button.IsEnabled,"Running action must remain cancellable on the original button.");
await CaptureRefinementElementAsync(button,Path.Combine(directory,"cancel-button.png"));Invoke(button);await action.ExecutionTask!.WaitAsync(TimeSpan.FromSeconds(3));await Task.Delay(40);
Require(cancelled==1&&!action.IsRunning&&ReferenceEquals(button.Content,idle),"Cancellation did not restore original content.");
var finish=new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);var bar=new AppBarButton{Label="载入附近星系",Icon=new SymbolIcon(Symbol.Download)};fixture.Children.Add(bar);
var explicitAction=new AsyncRelayCommand(()=>finish.Task);AsyncAction.SetCommand(bar,explicitAction);AsyncAction.SetCancelCommand(bar,new RelayCommand(()=>finish.TrySetResult()));await Task.Delay(30);Invoke(bar);await Task.Delay(30);
Require(bar.Label=="取消"&&bar.IsEnabled,"Command bar must offer cancellation on its original button.");
Invoke(bar);await explicitAction.ExecutionTask!.WaitAsync(TimeSpan.FromSeconds(3));await Task.Delay(30);Require(bar.Label=="载入附近星系","Command bar label did not restore.");
var deferred=new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);var deferredAction=new AsyncRelayCommand(()=>deferred.Task);
AsyncAction.SetCommand(button,deferredAction);Invoke(button);await Task.Delay(30);fixture.Children.Remove(button);deferred.SetResult();await deferredAction.ExecutionTask!;
fixture.Children.Add(button);await Task.Delay(40);Require(ReferenceEquals(button.Content,idle),"Unloaded action retained its cancel state on return.");
fixture.Children.Clear();var gallery=new StackPanel{Orientation=Orientation.Horizontal,Spacing=16};fixture.Children.Add(gallery);var opened=0;var failures=new List<string>();
void AddIcon(UserControl control,string label)
{
var image=(Image)control.Content;image.ImageOpened+=(_,_)=>opened++;image.ImageFailed+=(_,error)=>failures.Add(label+": "+error.ErrorMessage);
var panel=new StackPanel{Spacing=8,Width=64};panel.Children.Add(control);panel.Children.Add(new TextBlock{Text=label});gallery.Children.Add(panel);
}
foreach(var category in new[]{"Raw","Manufactured","Encoded"})AddIcon(new MaterialIcon{Category=category},category);
for(var grade=1;grade<=5;grade++)AddIcon(new MaterialGradeIcon{Grade=grade},$"G{grade}");
var deadline=DateTimeOffset.UtcNow.AddSeconds(4);while(opened<8&&failures.Count==0&&DateTimeOffset.UtcNow<deadline)await Task.Delay(50);
Require(opened==8&&failures.Count==0,"Imported game vectors did not load: "+string.Join("; ",failures));
await CaptureRefinementElementAsync(gallery,Path.Combine(directory,"game-material-icons.png"));host.Children.Remove(fixture);
fixture=new StackPanel();host.Children.Insert(0,fixture);
var map=new GalaxyMapView{Width=800,Height=500,CanPlanRoute=true,Centre=new(0,0,0),Systems=[new("界面恒星",new(5,2,3),42,"K")]};fixture.Children.Add(map);
await Task.Delay(120);map.RenderNowForProbe();var star=map.Systems[0];map.FocusSystem(star);await Task.Delay(350);map.RenderNowForProbe();
var centreBefore=map.ProjectForProbe(star.Position);map.ApplyDragDelta(40,20,false);map.RenderNowForProbe();var centreAfter=map.ProjectForProbe(star.Position);
Require(Math.Abs(centreBefore.X-centreAfter.X)<.1&&Math.Abs(centreBefore.Y-centreAfter.Y)<.1,"Selected star must remain the rotation pivot.");
var planned=0;map.TargetChosen+=(_,_)=>{planned++;map.Planning=true;};map.BeginPointer(centreAfter,false);await Task.Delay(270);
Require(map.ShowsHold&&map.HoldProgress is >10 and <90,"Long press must animate progress around the selected star.");
await CaptureRefinementElementAsync(map,Path.Combine(directory,"map-hold-progress.png"));await Task.Delay(430);map.EndPointer(centreAfter);
Require(planned==1&&map.ShowsPlanning,"Long press must submit once and display planning until confirmation.");await Task.Delay(80);map.Planning=false;
Require(!map.ShowsPlanning&&!map.ShowsHold,"Confirmed planning must clear the pending ring.");map.ClearSelection();Require(map.Selection is null,"Selection must be cancellable.");
map.Systems=BenchmarkSystems(25000);map.Centre=new(0,0,0);map.RenderNowForProbe();var sceneBuilds=map.SceneRebuilds;var vectorNodes=map.VectorNodesCreated;var cpu=new double[90];
for(var i=0;i<cpu.Length;i++){var at=System.Diagnostics.Stopwatch.GetTimestamp();map.ApplyDragDelta(.35,.12,false);map.RenderNowForProbe();cpu[i]=System.Diagnostics.Stopwatch.GetElapsedTime(at).TotalMilliseconds;}
Array.Sort(cpu);Require(map.MapGraphicsBackend.StartsWith("Direct3D")&&map.SceneRebuilds==sceneBuilds&&map.VectorNodesCreated==vectorNodes,"Map rendering must batch stars without rebuilding nodes while dragging.");
var restFrames=map.FrameCount;await Task.Delay(80);Require(map.FrameCount==restFrames,"Idle map must not redraw.");
var mapPerformance=new{systems=25000,map.MapGraphicsBackend,meanCpuMilliseconds=cpu.Average(),p95CpuMilliseconds=cpu[(int)(cpu.Length*.95)],map.VisibleStars,map.GraphicsDrawCalls,stableScene=true,idleDoesNotRedraw=true};host.Children.Remove(fixture);
var editor=(RefitPage)await Open(typeof(RefitPage));ViewModel.RebuildRefitCommand.Execute(null);await Task.Delay(100);
var slot=ViewModel.RefitVisibleSlots.First(s=>s.Module?.Engineering?.Quality is not null);ViewModel.RefitSlot=slot;ViewModel.RefitGrade=5;
var slots=(GridView)editor.FindName("SlotList");slots.ScrollIntoView(slot);await Task.Delay(100);
var container=slots.ContainerFromItem(slot);var property=ViewModel.RefitProperties.Single(p=>p.OriginalName=="Integrity");var blueprint=ViewModel.RefitBlueprint;var effect=ViewModel.RefitEffect?.Key;
var p=ViewModel.CurrentPlayer;var old=slot.Module!;var changed=old with{Health=.123,AmmoInClip=9,AmmoInHopper=234,Engineering=old.Engineering! with{Quality=.81}};
var snapshot=p with{Ships=p.Ships.Select(s=>s.ShipId==ViewModel.RefitDocument!.ShipId?s with{Modules=s.Modules.Select(m=>m.Slot==slot.Slot?changed:m).ToArray()}:s).ToArray()};
ViewModel.RefreshRefitProbePlayer(snapshot);await Task.Delay(100);
var ring=(ProgressRing)editor.FindName("CurrentEngineeringProgress");
Require(ViewModel.RefitDocument!.Slots.First(s=>s.Slot==slot.Slot).Target?.Grade==5&&!Descendants(editor).OfType<Button>().Any(b=>b.Content as string=="保存此模块的工程目标"),"Engineering targets must save automatically without a save-target button.");
Require(ReferenceEquals(slot,ViewModel.RefitSlot)&&ReferenceEquals(container,slots.ContainerFromItem(slot))&&ReferenceEquals(property,ViewModel.RefitProperties.Single(r=>r.OriginalName=="Integrity")),"Journal update recreated selected slot or property row.");
Require(ring.Value==81&&ViewModel.RefitGrade==5&&ViewModel.RefitBlueprint==blueprint&&ViewModel.RefitEffect?.Key==effect,$"Journal update reset target controls or failed to update progress: ring={ring.Value}, current={ViewModel.RefitCurrentProgress}, grade={ViewModel.RefitGrade}, blueprint={ViewModel.RefitBlueprint}/{blueprint}, effect={ViewModel.RefitEffect?.Key}/{effect}.");
var moduleData=(ScrollViewer)editor.FindName("ModuleDataView");
moduleData.ChangeView(null,moduleData.ScrollableHeight,null,true);await Task.Delay(80);
await CaptureRefinementElementAsync((Border)editor.FindName("InspectorPanel"),Path.Combine(directory,"module-data-updated.png"));
Require(Descendants(editor).OfType<BilingualText>().Any(t=>t.Text==changed.HealthText),$"Live property value did not reach its bound control: model={property.Value}, expected={changed.HealthText}.");
moduleData.ChangeView(null,0,null,true);await Task.Delay(50);
Require(!slots.CanReorderItems&&!slots.CanDragItems&&!Descendants(editor).OfType<PivotItem>().Any(i=>i.Header as string=="替换模块"),"Old reorder or module replacement UI remains.");
await CapturePageAsync(editor,Path.Combine(directory,"ship-editor-live.png"));
var inspectorTabs=(SelectorBar)editor.FindName("InspectorTabs");
Require(inspectorTabs.Items[0].Text=="模块数据"&&inspectorTabs.Items[1].Text=="改装","Inspector tab labels do not match the game.");
var inspector=(Border)editor.FindName("InspectorPanel");
Require(((Grid)inspector.Child).Children.First()==inspectorTabs,"Inspector switching must be above the module title.");
inspectorTabs.SelectedItem=inspectorTabs.Items[1];await Task.Delay(50);
Require(((FrameworkElement)editor.FindName("ModuleEngineeringView")).Visibility==Visibility.Visible&&((FrameworkElement)editor.FindName("ModuleDataView")).Visibility==Visibility.Collapsed,"Engineering tab did not switch content.");
await CaptureRefinementElementAsync(inspector,Path.Combine(directory,"module-engineering.png"));
inspectorTabs.SelectedItem=inspectorTabs.Items[0];await Task.Delay(50);
var changeButton=Descendants((DependencyObject)container).OfType<Button>().Single(b=>ReferenceEquals(b.Tag,slot));Invoke(changeButton);await Task.Delay(180);
Require(AppShellPage.Current!.CurrentPage is RefitModulePickerPage&&ViewModel.RefitChoosingType&&ViewModel.RefitModuleTypes.Count>0,"Card edit did not open compatible types page.");
var picker=(RefitModulePickerPage)AppShellPage.Current.CurrentPage!;await Task.Delay(350);await CapturePageAsync(picker,Path.Combine(directory,"module-types.png"));
var type=ViewModel.RefitModuleTypes.First(t=>ViewModel.RefitAvailableModules.Any(c=>XFEEDToolBox.Core.Outfitting.RefitModuleBrowser.TypeKey(c.Definition)==t.Key&&!c.Owned&&c.Module.Item!=old.Item));ViewModel.RefitSelectedModuleType=type;await Task.Delay(120);
Require(ViewModel.RefitChoosingModel&&ViewModel.RefitTypeModels.Count>0&&ViewModel.RefitTypeModels.All(c=>XFEEDToolBox.Core.Outfitting.RefitModuleBrowser.TypeKey(c.Definition)==type.Key),"Type selection did not reveal only its compatible models.");
var choice=ViewModel.RefitTypeModels.First(c=>!c.Owned&&c.Module.Item!=old.Item);ViewModel.RefitInstallModule=choice;
await CapturePageAsync(picker,Path.Combine(directory,"module-models.png"));Invoke((Button)picker.FindName("InstallButton"));await Task.Delay(180);
Require(AppShellPage.Current.CurrentPage is RefitPage&&ViewModel.RefitSlot?.Module?.Item==choice.Module.Item,"Model selection did not install and return to editor.");ViewModel.RebuildRefitCommand.Execute(null);
if(Environment.GetCommandLineArgs().Contains("--properties-only"))
return new{homePropertiesAtBottom=true,dataAccessRemoved=true,topModuleTabsSwitch=true,slotAndPropertyIdentityPreserved=true,targetsPreserved=true,
powerCapacity=ViewModel.ShipPowerCapacityText,retractedPower=ViewModel.ShipPowerRetractedText,deployedPower=ViewModel.ShipPowerDeployedText,realGameActionsInvoked=false};
var navigation=await Open(typeof(NavigationPage));
var fullMap=(GalaxyMapView)navigation.FindName("Map");var mapRegion=(Grid)navigation.FindName("MapRegion");var sidebar=(HorizontalPanel)navigation.FindName("SidebarExpander");
Require(Math.Abs(fullMap.ActualWidth-mapRegion.ActualWidth)<1&&!Descendants(navigation).OfType<Button>().Any(b=>b.Content as string=="规划航线"),"Map must fill its page without a route-plan button.");
var mapWidth=fullMap.ActualWidth;sidebar.IsExpanded=false;await Task.Delay(80);Require(Math.Abs(fullMap.ActualWidth-mapWidth)<1,"Sidebar collapse must leave the map full width.");sidebar.IsExpanded=true;
var overlay=(Border)navigation.FindName("SidebarOverlay");Require(overlay.Background is Microsoft.UI.Xaml.Media.AcrylicBrush tint&&Math.Abs(tint.Opacity-.78)<.00001&&Math.Abs(tint.TintOpacity-.32)<.00001,"Map overlay must retain its translucent acrylic finish.");
ViewModel.GalaxyPlaceKindIndex=2;((HorizontalPanel)navigation.FindName("PlacesExpander")).IsExpanded=true;await Task.Delay(100);
var engineerInput=(ListView)navigation.FindName("EngineerLocationInput");Require(engineerInput.Items.Count>=38&&Descendants(engineerInput).OfType<PersonPicture>().Any(p=>p.ProfilePicture is not null),"Engineer location picker must include portraits and locations.");
await CapturePageAsync(navigation,Path.Combine(directory,"map-full-page-engineers.png"));
var states=new[]{NavigationLegState.Completed,NavigationLegState.Running,NavigationLegState.Waiting,NavigationLegState.Warning,NavigationLegState.Error};
ViewModel.NavigationLegs.Apply(states.Select((state,index)=>new NavigationLegPreview(index+1,index+1,$"界面检查 {index+1}","K",20,true,state==NavigationLegState.Completed,index==1,null){ExecutionState=state,Message=state is NavigationLegState.Error or NavigationLegState.Warning?"状态图标检查":""}));ViewModel.HasNoNavigationLegs=false;
await Task.Delay(160);var route=(ListView)navigation.FindName("RouteList");
foreach(var leg in ViewModel.NavigationLegs.ToArray())
{
route.ScrollIntoView(leg);await Task.Delay(50);var row=route.ContainerFromItem(leg);
Require(row is not null,"Route row was not realised.");
Require(leg.IsRunning?Descendants((DependencyObject)row!).OfType<ProgressRing>().Any(r=>r.IsActive&&r.Visibility==Visibility.Visible):Descendants((DependencyObject)row!).OfType<FontIcon>().Any(i=>i.Visibility==Visibility.Visible),"Per-hop status icon did not render: "+leg.State);
}
route.ScrollIntoView(ViewModel.NavigationLegs[0]);await Task.Delay(60);
await CapturePageAsync(navigation,Path.Combine(directory,"route-status-icons.png"));
using(var popup=new FloatingUpgradeList(WinRT.Interop.WindowNative.GetWindowHandle(MainWindow),0,0,hidden:true))
{
popup.MoveTo(-30000,-30000);popup.Show();await Task.Delay(180);
Require(popup.IsToolPopup&&popup.Panel.IsLoaded&&popup.Surface.ActualWidth>0,"Floating checklist must be a loaded native tool popup without caption chrome.");
var before=popup.Position;popup.MoveTo(before.X+24,before.Y+16);Require(popup.Position==(before.X+24,before.Y+16),"Floating checklist position did not move.");
Invoke((Button)popup.Panel.FindName("PinListButton"));await Task.Delay(40);Require(popup.IsPinned&&popup.NativeTopmost,"Pin did not apply native always-on-top state.");
await CaptureRefinementElementAsync(popup.Surface,Path.Combine(directory,"floating-checklist.png"));
Invoke((Button)popup.Panel.FindName("PinListButton"));await Task.Delay(40);Require(!popup.IsPinned&&!popup.NativeTopmost,"Unpin did not restore normal z-order.");
}
return new{originalButtonCancels=true,commandBarCancels=true,unloadedContentRestored=true,gameMaterialIconsLoaded=opened,slotAndPropertyIdentityPreserved=true,targetsPreserved=true,targetsAutoSaved=true,compatibleTypeAndModelPages=true,perHopStates=states.Select(s=>s.ToString()).ToArray(),nativePopupMovesAndPins=true,fullPageAcrylicMap=true,engineerLocations=engineerInput.Items.Count,longPressProgressAndPlanning=true,selectionRotationPivot=true,mapPerformance,realGameActionsInvoked=false};
}
}