From e290254a18c5502e7c9ce0899a063f7947db731d Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Wed, 4 Nov 2026 23:02:20 -0500 Subject: [PATCH] fix(ui): give console and MCP users a hint they can follow on outlined sheets --- OpenNest.Console/Program.cs | 7 +++-- OpenNest.FrontEnd.Tests/McpPlateToolsTests.cs | 20 ++++++++++++++ OpenNest.Mcp/Tools/NestingTools.cs | 27 +++++++++++++------ 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/OpenNest.Console/Program.cs b/OpenNest.Console/Program.cs index 4724beb8..e8aa03e7 100644 --- a/OpenNest.Console/Program.cs +++ b/OpenNest.Console/Program.cs @@ -24,6 +24,9 @@ return NestConsole.Run(args); static class NestConsole { + private const string OutlinedSheetMessage = + "Error: Fill strategies need a rectangular sheet. Use --autonest --engine Irregular."; + public static int Run(string[] args) { using var cancellation = new CancellationTokenSource(); @@ -209,9 +212,9 @@ static class NestConsole ApplyTemplate(plate, options); ApplyOverrides(plate, options); - if (!options.AutoNest && PlateFillService.Refusal(plate) is { } refusal) + if (!options.AutoNest && PlateFillService.Refusal(plate) != null) { - Console.Error.WriteLine($"Error: {refusal} Use --autonest."); + Console.Error.WriteLine(OutlinedSheetMessage); return 2; } diff --git a/OpenNest.FrontEnd.Tests/McpPlateToolsTests.cs b/OpenNest.FrontEnd.Tests/McpPlateToolsTests.cs index c31b7321..ae3c0699 100644 --- a/OpenNest.FrontEnd.Tests/McpPlateToolsTests.cs +++ b/OpenNest.FrontEnd.Tests/McpPlateToolsTests.cs @@ -37,6 +37,26 @@ public class McpPlateToolsTests : IDisposable Assert.Null(sessionOnly.GetPlate(1)); } + [Fact] + public void FillOnOutlinedSheetNamesTheMcpToolsThatCanNestIt() + { + var session = new NestSession { Nest = new Nest("fixture") }; + session.Nest.Drawings.Add(new Drawing("square", new Program())); + var outline = new SheetOutline(new[] + { + new Vector(0, 0), new Vector(96, 0), new Vector(96, 24), + new Vector(48, 24), new Vector(48, 48), new Vector(0, 48), + }); + var plate = new Plate(10, 10) { Outline = outline }; + session.Nest.Plates.Add(plate); + + Assert.Equal( + "Error: Fill strategies need a rectangular sheet. Use autonest_plate or autonest_job " + + "with engine Irregular. Nothing committed.", + new NestingTools(session).FillPlate(0, "square")); + Assert.Empty(plate.Parts); + } + private static string Delete(SetupTools tools, int index) => tools.DeletePlate(index); [Fact] diff --git a/OpenNest.Mcp/Tools/NestingTools.cs b/OpenNest.Mcp/Tools/NestingTools.cs index 4560b8c8..0a06af7b 100644 --- a/OpenNest.Mcp/Tools/NestingTools.cs +++ b/OpenNest.Mcp/Tools/NestingTools.cs @@ -68,8 +68,8 @@ namespace OpenNest.Mcp.Tools var strategy = ResolveStrategy(engine); if (strategy == null) return EngineError(engine); - if (PlateFillService.Refusal(plate) is { } refusal) - return $"Error: {refusal} Nothing committed."; + if (OutlinedSheetRefusal(plate) is { } refusal) + return refusal; var countBefore = plate.Parts.Count; var item = new NestItem { Drawing = drawing, Quantity = quantity }; @@ -125,8 +125,8 @@ namespace OpenNest.Mcp.Tools var strategy = ResolveStrategy(engine); if (strategy == null) return EngineError(engine); - if (PlateFillService.Refusal(plate) is { } refusal) - return $"Error: {refusal} Nothing committed."; + if (OutlinedSheetRefusal(plate) is { } refusal) + return refusal; var countBefore = plate.Parts.Count; var item = new NestItem { Drawing = drawing, Quantity = quantity }; @@ -179,8 +179,8 @@ namespace OpenNest.Mcp.Tools var strategy = ResolveStrategy(engine); if (strategy == null) return EngineError(engine); - if (PlateFillService.Refusal(plate) is { } refusal) - return $"Error: {refusal} Nothing committed."; + if (OutlinedSheetRefusal(plate) is { } refusal) + return refusal; var finder = RemnantFinder.FromPlate(plate); var remnants = finder.FindRemnants(); @@ -248,8 +248,8 @@ namespace OpenNest.Mcp.Tools var strategy = ResolveStrategy(engine); if (strategy == null) return EngineError(engine); - if (PlateFillService.Refusal(plate) is { } refusal) - return $"Error: {refusal} Nothing committed."; + if (OutlinedSheetRefusal(plate) is { } refusal) + return refusal; var parsed = ParseItems(drawingNames, quantities); if (parsed.error != null) @@ -614,6 +614,17 @@ namespace OpenNest.Mcp.Tools /// private static string ResolveStrategy(string engine) => EngineNameResolver.Classify(engine).FillStrategy; + internal const string OutlinedSheetMessage = + "Error: Fill strategies need a rectangular sheet. Use autonest_plate or autonest_job " + + "with engine Irregular. Nothing committed."; + + /// + /// The fill tools' refusal for an outlined sheet, naming the MCP tools that can nest it; + /// null when the plate is rectangular. + /// + private static string OutlinedSheetRefusal(Plate plate) => + PlateFillService.Refusal(plate) == null ? null : OutlinedSheetMessage; + private static string EngineError(string engine) { var classified = EngineNameResolver.Classify(engine);