# .NET 8 Migration Plan > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** Convert OpenNest from .NET Framework 4.8 to .NET 8, replacing netDxf with ACadSharp. **Architecture:** Three projects (OpenNest.Core, OpenNest.Engine, OpenNest) converted from legacy csproj to SDK-style targeting `net8.0-windows`. The netDxf NuGet dependency is replaced with ACadSharp 3.1.32 in the WinForms project. The 3 IO files (DxfImporter, DxfExporter, Extensions) are rewritten against the ACadSharp API. **Tech Stack:** .NET 8, WinForms, ACadSharp 3.1.32, System.Drawing.Common, CSMath --- ## Reference Material - ACadSharp usage examples: `C:\Users\aisaacs\Desktop\Projects\Geometry\Geometry.Dxf\Extensions.cs` and `DxfImport.cs` - ACadSharp key types: - Reading: `ACadSharp.IO.DxfReader` → `.Read()` returns `CadDocument` - Writing: `ACadSharp.IO.DxfWriter` - Entities: `ACadSharp.Entities.{Line, Arc, Circle, Polyline, LwPolyline, Spline, Ellipse}` - Vectors: `CSMath.XY`, `CSMath.XYZ` - Layers: `ACadSharp.Tables.Layer` - Colors: `ACadSharp.Color` (ACI index-based) - Document: `ACadSharp.CadDocument` - netDxf → ACadSharp type mapping: - `netDxf.Vector2` → `CSMath.XY` - `netDxf.Vector3` → `CSMath.XYZ` - `netDxf.DxfDocument` → `ACadSharp.CadDocument` - `netDxf.DxfDocument.Load(path)` → `new DxfReader(path).Read()` - `netDxf.DxfDocument.Save(stream)` → `new DxfWriter(stream, doc).Write()` - `netDxf.Tables.Layer` → `ACadSharp.Tables.Layer` - `netDxf.AciColor` → `ACadSharp.Color` - `netDxf.Linetype` → `ACadSharp.Tables.LineType` - `doc.Entities.Lines` → `doc.Entities.OfType()` - `doc.Entities.Arcs` → `doc.Entities.OfType()` - `spline.PolygonalVertexes(n)` → manual control point interpolation - `polyline.Vertexes` → `polyline.Vertices` - `ellipse.ToPolyline2D(n)` → manual parametric calculation --- ### Task 1: Convert OpenNest.Core to SDK-style .NET 8 **Files:** - Replace: `OpenNest.Core/OpenNest.Core.csproj` - Delete: `OpenNest.Core/Properties/AssemblyInfo.cs` **Step 1: Replace the csproj with SDK-style** Replace `OpenNest.Core/OpenNest.Core.csproj` with: ```xml net8.0-windows OpenNest OpenNest.Core false ``` Notes: - `net8.0-windows` needed because `System.Drawing.Common` requires Windows on .NET 8 - `GenerateAssemblyInfo` false to keep existing AssemblyInfo.cs (we'll remove it in step 2 and re-enable) - SDK-style auto-includes all `.cs` files, so no `` items needed - `System.Drawing.Common` NuGet replaces the Framework's built-in `System.Drawing` **Step 2: Delete AssemblyInfo.cs and enable auto-generated assembly info** Delete `OpenNest.Core/Properties/AssemblyInfo.cs`. Then update the csproj to remove `GenerateAssemblyInfo`: ```xml net8.0-windows OpenNest OpenNest.Core ``` **Step 3: Build to verify** Run: `dotnet build OpenNest.Core/OpenNest.Core.csproj` Expected: Build succeeds with 0 errors. **Step 4: Commit** ``` feat: convert OpenNest.Core to .NET 8 SDK-style project ``` --- ### Task 2: Convert OpenNest.Engine to SDK-style .NET 8 **Files:** - Replace: `OpenNest.Engine/OpenNest.Engine.csproj` - Delete: `OpenNest.Engine/Properties/AssemblyInfo.cs` **Step 1: Replace the csproj with SDK-style** ```xml net8.0-windows OpenNest OpenNest.Engine ``` **Step 2: Delete AssemblyInfo.cs** Delete `OpenNest.Engine/Properties/AssemblyInfo.cs`. **Step 3: Build to verify** Run: `dotnet build OpenNest.Engine/OpenNest.Engine.csproj` Expected: Build succeeds. **Step 4: Commit** ``` feat: convert OpenNest.Engine to .NET 8 SDK-style project ``` --- ### Task 3: Convert OpenNest (WinForms) to SDK-style .NET 8 **Files:** - Replace: `OpenNest/OpenNest.csproj` - Delete: `OpenNest/Properties/AssemblyInfo.cs` - Delete: `OpenNest/packages.config` - Keep: `OpenNest/Properties/Settings.settings`, `Settings.Designer.cs`, `Resources.resx`, `Resources.Designer.cs` - Keep: `OpenNest/app.config` **Step 1: Replace the csproj with SDK-style** ```xml WinExe net8.0-windows OpenNest OpenNest true false DpiUnaware True Resources.resx True True Settings.settings True ResXFileCodeGenerator Resources.Designer.cs SettingsSingleFileGenerator Settings.Designer.cs ``` Notes: - `UseWindowsForms` enables WinForms support on .NET 8 - `ApplicationHighDpiMode` replaces the manual `SetProcessDPIAware()` P/Invoke - ACadSharp replaces netDxf package reference - `GenerateAssemblyInfo` false initially — we'll clean up AssemblyInfo and re-enable after **Step 2: Delete AssemblyInfo.cs and packages.config** Delete `OpenNest/Properties/AssemblyInfo.cs` and `OpenNest/packages.config`. Then remove `GenerateAssemblyInfo` from the csproj. **Step 3: Build to check what breaks** Run: `dotnet build OpenNest/OpenNest.csproj` Expected: Build fails — the 3 IO files reference `netDxf` types. This is expected and will be fixed in Tasks 4-6. **Step 4: Commit (with build errors expected)** ``` feat: convert OpenNest WinForms project to .NET 8 SDK-style Build errors expected in IO files — netDxf references not yet migrated to ACadSharp. ``` --- ### Task 4: Rewrite Extensions.cs for ACadSharp **Files:** - Modify: `OpenNest/IO/Extensions.cs` - Reference: `C:\Users\aisaacs\Desktop\Projects\Geometry\Geometry.Dxf\Extensions.cs` **Step 1: Rewrite Extensions.cs** Replace the full file. Key changes: - `netDxf.Vector2` → `CSMath.XY` - `netDxf.Vector3` → `CSMath.XYZ` - `netDxf.Entities.*` → `ACadSharp.Entities.*` - `netDxf.Tables.Layer` → `ACadSharp.Tables.Layer` - `layer.Color.ToColor()` → `System.Drawing.Color.FromArgb(layer.Color.GetColorIndex()...)` or equivalent - `spline.PolygonalVertexes(n)` → manual control point iteration - `polyline.Vertexes` → `polyline.Vertices` - `ellipse.ToPolyline2D(n)` → manual parametric calculation - Polyline2D closed check: use `Flags` property - `arc.StartAngle`/`arc.EndAngle` — ACadSharp stores arc angles in **radians** already (netDxf used degrees) ```csharp using System.Collections.Generic; using System.Linq; using ACadSharp.Entities; using CSMath; using OpenNest.Geometry; using OpenNest.Math; namespace OpenNest.IO { internal static class Extensions { public static Vector ToOpenNest(this XY v) { return new Vector(v.X, v.Y); } public static Vector ToOpenNest(this XYZ v) { return new Vector(v.X, v.Y); } public static Arc ToOpenNest(this ACadSharp.Entities.Arc arc) { // ACadSharp stores angles in radians already return new Arc( arc.Center.X, arc.Center.Y, arc.Radius, arc.StartAngle, arc.EndAngle) { Layer = arc.Layer.ToOpenNest() }; } public static Circle ToOpenNest(this ACadSharp.Entities.Circle circle) { return new Circle( circle.Center.X, circle.Center.Y, circle.Radius) { Layer = circle.Layer.ToOpenNest() }; } public static Line ToOpenNest(this ACadSharp.Entities.Line line) { return new Line( line.StartPoint.X, line.StartPoint.Y, line.EndPoint.X, line.EndPoint.Y) { Layer = line.Layer.ToOpenNest() }; } public static List ToOpenNest(this Spline spline, int precision = 200) { var lines = new List(); var controlPoints = spline.ControlPoints .Select(p => new Vector(p.X, p.Y)).ToList(); if (controlPoints.Count < 2) return lines; var lastPoint = controlPoints[0]; for (int i = 1; i < controlPoints.Count; i++) { var nextPoint = controlPoints[i]; lines.Add(new Line(lastPoint, nextPoint) { Layer = spline.Layer.ToOpenNest() }); lastPoint = nextPoint; } if (spline.IsClosed) lines.Add(new Line(lastPoint, controlPoints[0]) { Layer = spline.Layer.ToOpenNest() }); return lines; } public static List ToOpenNest(this Polyline polyline) { var lines = new List(); var vertices = polyline.Vertices.ToList(); if (vertices.Count == 0) return lines; var lastPoint = vertices[0].Location.ToOpenNest(); for (int i = 1; i < vertices.Count; i++) { var nextPoint = vertices[i].Location.ToOpenNest(); lines.Add(new Line(lastPoint, nextPoint) { Layer = polyline.Layer.ToOpenNest() }); lastPoint = nextPoint; } if ((polyline.Flags & PolylineFlags.ClosedPolylineOrClosedPolygonMeshInM) != 0) lines.Add(new Line(lastPoint, vertices[0].Location.ToOpenNest()) { Layer = polyline.Layer.ToOpenNest() }); return lines; } public static List ToOpenNest(this LwPolyline polyline) { var lines = new List(); var vertices = polyline.Vertices.ToList(); if (vertices.Count == 0) return lines; var lastPoint = new Vector(vertices[0].Location.X, vertices[0].Location.Y); for (int i = 1; i < vertices.Count; i++) { var nextPoint = new Vector(vertices[i].Location.X, vertices[i].Location.Y); lines.Add(new Line(lastPoint, nextPoint) { Layer = polyline.Layer.ToOpenNest() }); lastPoint = nextPoint; } if ((polyline.Flags & LwPolylineFlags.Closed) != 0) lines.Add(new Line(lastPoint, new Vector(vertices[0].Location.X, vertices[0].Location.Y)) { Layer = polyline.Layer.ToOpenNest() }); return lines; } public static List ToOpenNest(this Ellipse ellipse, int precision = 200) { var lines = new List(); var points = new List(); var startParam = ellipse.StartParameter; var endParam = ellipse.EndParameter; var paramRange = endParam - startParam; var radius = System.Math.Sqrt( ellipse.EndPoint.X * ellipse.EndPoint.X + ellipse.EndPoint.Y * ellipse.EndPoint.Y); for (int i = 0; i <= precision; i++) { var t = i / (double)precision; var angle = startParam + paramRange * t; var x = ellipse.Center.X + radius * System.Math.Cos(angle); var y = ellipse.Center.Y + radius * System.Math.Sin(angle); points.Add(new Vector(x, y)); } for (int i = 1; i < points.Count; i++) { lines.Add(new Line(points[i - 1], points[i]) { Layer = ellipse.Layer.ToOpenNest() }); } if (points.Count > 1 && System.Math.Abs(paramRange - 2 * System.Math.PI) < 0.0001) { lines.Add(new Line(points[points.Count - 1], points[0]) { Layer = ellipse.Layer.ToOpenNest() }); } return lines; } public static Layer ToOpenNest(this ACadSharp.Tables.Layer layer) { return new Layer(layer.Name) { Color = System.Drawing.Color.FromArgb( layer.Color.R, layer.Color.G, layer.Color.B), IsVisible = layer.IsOn }; } public static XY ToAcad(this Vector v) { return new XY(v.X, v.Y); } public static XYZ ToAcadXYZ(this Vector v) { return new XYZ(v.X, v.Y, 0); } } } ``` **Step 2: Commit** ``` refactor: rewrite IO Extensions for ACadSharp ``` --- ### Task 5: Rewrite DxfImporter.cs for ACadSharp **Files:** - Modify: `OpenNest/IO/DxfImporter.cs` **Step 1: Rewrite DxfImporter.cs** Key changes: - `DxfDocument.Load(path)` → `new DxfReader(path).Read()` returning `CadDocument` - `doc.Entities.Lines` → `doc.Entities.OfType()` - Same pattern for Arcs, Circles, Splines, etc. - `doc.Entities.Polylines2D` → `doc.Entities.OfType()` + `doc.Entities.OfType()` - `doc.Entities.Polylines3D` → handled via `Polyline` type ```csharp using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using ACadSharp; using ACadSharp.IO; using OpenNest.Geometry; namespace OpenNest.IO { public class DxfImporter { public int SplinePrecision { get; set; } public DxfImporter() { } private List GetGeometry(CadDocument doc) { var entities = new List(); var lines = new List(); var arcs = new List(); foreach (var entity in doc.Entities) { switch (entity) { case ACadSharp.Entities.Spline spline: lines.AddRange(spline.ToOpenNest(SplinePrecision)); break; case ACadSharp.Entities.LwPolyline lwPolyline: lines.AddRange(lwPolyline.ToOpenNest()); break; case ACadSharp.Entities.Polyline polyline: lines.AddRange(polyline.ToOpenNest()); break; case ACadSharp.Entities.Ellipse ellipse: lines.AddRange(ellipse.ToOpenNest(SplinePrecision)); break; case ACadSharp.Entities.Line line: lines.Add(line.ToOpenNest()); break; case ACadSharp.Entities.Arc arc: arcs.Add(arc.ToOpenNest()); break; case ACadSharp.Entities.Circle circle: entities.Add(circle.ToOpenNest()); break; } } Helper.Optimize(lines); Helper.Optimize(arcs); entities.AddRange(lines); entities.AddRange(arcs); return entities; } public bool GetGeometry(Stream stream, out List geometry) { bool success = false; try { using var reader = new DxfReader(stream); var doc = reader.Read(); geometry = GetGeometry(doc); success = true; } catch (Exception ex) { Debug.WriteLine(ex.Message); geometry = new List(); } return success; } public bool GetGeometry(string path, out List geometry) { bool success = false; try { using var reader = new DxfReader(path); var doc = reader.Read(); geometry = GetGeometry(doc); success = true; } catch (Exception ex) { Debug.WriteLine(ex.Message); geometry = new List(); } return success; } } } ``` **Step 2: Commit** ``` refactor: rewrite DxfImporter for ACadSharp ``` --- ### Task 6: Rewrite DxfExporter.cs for ACadSharp **Files:** - Modify: `OpenNest/IO/DxfExporter.cs` **Step 1: Rewrite DxfExporter.cs** Key changes: - `new DxfDocument()` → `new CadDocument()` - `doc.Entities.Add(entity)` → `doc.Entities.Add(entity)` (same pattern) - `doc.Save(stream)` → `using var writer = new DxfWriter(stream); writer.Write(doc);` - `new netDxf.Entities.Line(pt1, pt2)` → `new ACadSharp.Entities.Line { StartPoint = xyz1, EndPoint = xyz2 }` - `new netDxf.Vector2(x, y)` → `new XYZ(x, y, 0)` (ACadSharp Lines use XYZ) - `AciColor.Red` → `new Color(1)` (ACI color index 1 = red) - `AciColor.Blue` → `new Color(5)` (ACI color index 5 = blue) - `AciColor.Cyan` → `new Color(4)` (ACI color index 4 = cyan) - `Linetype.Dashed` → look up or create dashed linetype - `Vector2 curpos` → `XYZ curpos` (track as 3D) - Arc angles: netDxf used degrees, ACadSharp uses radians — remove degree conversions ```csharp using System; using System.Diagnostics; using System.IO; using ACadSharp; using ACadSharp.Entities; using ACadSharp.IO; using ACadSharp.Tables; using CSMath; using OpenNest.CNC; using OpenNest.Math; namespace OpenNest.IO { using AcadLine = ACadSharp.Entities.Line; using AcadArc = ACadSharp.Entities.Arc; using AcadCircle = ACadSharp.Entities.Circle; public class DxfExporter { private CadDocument doc; private XYZ curpos; private Mode mode; private readonly Layer cutLayer; private readonly Layer rapidLayer; private readonly Layer plateLayer; public DxfExporter() { doc = new CadDocument(); cutLayer = new Layer("Cut"); cutLayer.Color = new Color(1); // Red rapidLayer = new Layer("Rapid"); rapidLayer.Color = new Color(5); // Blue plateLayer = new Layer("Plate"); plateLayer.Color = new Color(4); // Cyan } public void ExportProgram(Program program, Stream stream) { doc = new CadDocument(); EnsureLayers(); AddProgram(program); using var writer = new DxfWriter(stream); writer.Write(doc); } public bool ExportProgram(Program program, string path) { Stream stream = null; bool success = false; try { stream = File.Create(path); ExportProgram(program, stream); success = true; } catch { Debug.Fail("DxfExporter.ExportProgram failed to write program to file: " + path); } finally { if (stream != null) stream.Close(); } return success; } public void ExportPlate(Plate plate, Stream stream) { doc = new CadDocument(); EnsureLayers(); AddPlateOutline(plate); foreach (var part in plate.Parts) { var endpt = part.Location.ToAcadXYZ(); var line = new AcadLine(); line.StartPoint = curpos; line.EndPoint = endpt; line.Layer = rapidLayer; doc.Entities.Add(line); curpos = endpt; AddProgram(part.Program); } using var writer = new DxfWriter(stream); writer.Write(doc); } public bool ExportPlate(Plate plate, string path) { Stream stream = null; bool success = false; try { stream = File.Create(path); ExportPlate(plate, stream); success = true; } catch { Debug.Fail("DxfExporter.ExportPlate failed to write plate to file: " + path); } finally { if (stream != null) stream.Close(); } return success; } private void EnsureLayers() { if (!doc.Layers.Contains(cutLayer.Name)) doc.Layers.Add(cutLayer); if (!doc.Layers.Contains(rapidLayer.Name)) doc.Layers.Add(rapidLayer); if (!doc.Layers.Contains(plateLayer.Name)) doc.Layers.Add(plateLayer); } private void AddPlateOutline(Plate plate) { XYZ pt1, pt2, pt3, pt4; switch (plate.Quadrant) { case 1: pt1 = new XYZ(0, 0, 0); pt2 = new XYZ(0, plate.Size.Height, 0); pt3 = new XYZ(plate.Size.Width, plate.Size.Height, 0); pt4 = new XYZ(plate.Size.Width, 0, 0); break; case 2: pt1 = new XYZ(0, 0, 0); pt2 = new XYZ(0, plate.Size.Height, 0); pt3 = new XYZ(-plate.Size.Width, plate.Size.Height, 0); pt4 = new XYZ(-plate.Size.Width, 0, 0); break; case 3: pt1 = new XYZ(0, 0, 0); pt2 = new XYZ(0, -plate.Size.Height, 0); pt3 = new XYZ(-plate.Size.Width, -plate.Size.Height, 0); pt4 = new XYZ(-plate.Size.Width, 0, 0); break; case 4: pt1 = new XYZ(0, 0, 0); pt2 = new XYZ(0, -plate.Size.Height, 0); pt3 = new XYZ(plate.Size.Width, -plate.Size.Height, 0); pt4 = new XYZ(plate.Size.Width, 0, 0); break; default: return; } AddLine(pt1, pt2, plateLayer); AddLine(pt2, pt3, plateLayer); AddLine(pt3, pt4, plateLayer); AddLine(pt4, pt1, plateLayer); // Inner margin lines var m1 = new XYZ(pt1.X + plate.EdgeSpacing.Left, pt1.Y + plate.EdgeSpacing.Bottom, 0); var m2 = new XYZ(m1.X, pt2.Y - plate.EdgeSpacing.Top, 0); var m3 = new XYZ(pt3.X - plate.EdgeSpacing.Right, m2.Y, 0); var m4 = new XYZ(m3.X, m1.Y, 0); AddLine(m1, m2, plateLayer); AddLine(m2, m3, plateLayer); AddLine(m3, m4, plateLayer); AddLine(m4, m1, plateLayer); } private void AddLine(XYZ start, XYZ end, Layer layer) { var line = new AcadLine(); line.StartPoint = start; line.EndPoint = end; line.Layer = layer; doc.Entities.Add(line); } private void AddProgram(Program program) { mode = program.Mode; for (int i = 0; i < program.Length; ++i) { var code = program[i]; switch (code.Type) { case CodeType.ArcMove: var arc = (ArcMove)code; AddArcMove(arc); break; case CodeType.LinearMove: var line = (LinearMove)code; AddLinearMove(line); break; case CodeType.RapidMove: var rapid = (RapidMove)code; AddRapidMove(rapid); break; case CodeType.SubProgramCall: var tmpmode = mode; var subpgm = (CNC.SubProgramCall)code; AddProgram(subpgm.Program); mode = tmpmode; break; } } } private void AddLinearMove(LinearMove line) { var pt = line.EndPoint.ToAcadXYZ(); if (mode == Mode.Incremental) pt = new XYZ(pt.X + curpos.X, pt.Y + curpos.Y, 0); AddLine(curpos, pt, cutLayer); curpos = pt; } private void AddRapidMove(RapidMove rapid) { var pt = rapid.EndPoint.ToAcadXYZ(); if (mode == Mode.Incremental) pt = new XYZ(pt.X + curpos.X, pt.Y + curpos.Y, 0); AddLine(curpos, pt, rapidLayer); curpos = pt; } private void AddArcMove(ArcMove arc) { var center = arc.CenterPoint.ToAcadXYZ(); var endpt = arc.EndPoint.ToAcadXYZ(); if (mode == Mode.Incremental) { endpt = new XYZ(endpt.X + curpos.X, endpt.Y + curpos.Y, 0); center = new XYZ(center.X + curpos.X, center.Y + curpos.Y, 0); } // ACadSharp uses radians — no conversion needed var startAngle = System.Math.Atan2( curpos.Y - center.Y, curpos.X - center.X); var endAngle = System.Math.Atan2( endpt.Y - center.Y, endpt.X - center.X); if (arc.Rotation == OpenNest.RotationType.CW) Generic.Swap(ref startAngle, ref endAngle); var dx = endpt.X - center.X; var dy = endpt.Y - center.Y; var radius = System.Math.Sqrt(dx * dx + dy * dy); if (startAngle.IsEqualTo(endAngle)) { var circle = new AcadCircle(); circle.Center = center; circle.Radius = radius; circle.Layer = cutLayer; doc.Entities.Add(circle); } else { var arc2 = new AcadArc(); arc2.Center = center; arc2.Radius = radius; arc2.StartAngle = startAngle; arc2.EndAngle = endAngle; arc2.Layer = cutLayer; doc.Entities.Add(arc2); } curpos = endpt; } } } ``` **Step 2: Commit** ``` refactor: rewrite DxfExporter for ACadSharp ``` --- ### Task 7: Update MainApp.cs for .NET 8 **Files:** - Modify: `OpenNest/MainApp.cs` **Step 1: Simplify MainApp.cs** .NET 8 WinForms handles DPI awareness via the `ApplicationHighDpiMode` project property. Remove the manual `SetProcessDPIAware()` P/Invoke. ```csharp using System; using System.Windows.Forms; using OpenNest.Forms; namespace OpenNest { internal static class MainApp { [STAThread] private static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new MainForm()); } } } ``` **Step 2: Commit** ``` refactor: simplify MainApp for .NET 8 DPI handling ``` --- ### Task 8: Update solution file and clean up **Files:** - Modify: `OpenNest.sln` - Delete: `packages/` directory (if present on disk) - Verify: `.gitignore` already ignores `packages/` **Step 1: Update solution file** Replace `OpenNest.sln` with a clean version. The project GUIDs stay the same but the solution format is updated: ``` Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.0.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenNest", "OpenNest\OpenNest.csproj", "{1F1E40E0-5C53-474F-A258-69C9C3FAC15A}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenNest.Core", "OpenNest.Core\OpenNest.Core.csproj", "{5A5FDE8D-F8DB-440E-866C-C4807E1686CF}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenNest.Engine", "OpenNest.Engine\OpenNest.Engine.csproj", "{0083B9CC-54AD-4085-A30D-56BC6834B71A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {1F1E40E0-5C53-474F-A258-69C9C3FAC15A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1F1E40E0-5C53-474F-A258-69C9C3FAC15A}.Debug|Any CPU.Build.0 = Debug|Any CPU {1F1E40E0-5C53-474F-A258-69C9C3FAC15A}.Release|Any CPU.ActiveCfg = Release|Any CPU {1F1E40E0-5C53-474F-A258-69C9C3FAC15A}.Release|Any CPU.Build.0 = Release|Any CPU {5A5FDE8D-F8DB-440E-866C-C4807E1686CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5A5FDE8D-F8DB-440E-866C-C4807E1686CF}.Debug|Any CPU.Build.0 = Debug|Any CPU {5A5FDE8D-F8DB-440E-866C-C4807E1686CF}.Release|Any CPU.ActiveCfg = Release|Any CPU {5A5FDE8D-F8DB-440E-866C-C4807E1686CF}.Release|Any CPU.Build.0 = Release|Any CPU {0083B9CC-54AD-4085-A30D-56BC6834B71A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0083B9CC-54AD-4085-A30D-56BC6834B71A}.Debug|Any CPU.Build.0 = Debug|Any CPU {0083B9CC-54AD-4085-A30D-56BC6834B71A}.Release|Any CPU.ActiveCfg = Release|Any CPU {0083B9CC-54AD-4085-A30D-56BC6834B71A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal ``` **Step 2: Full solution build** Run: `dotnet build OpenNest.sln` Expected: Build succeeds with 0 errors. **Step 3: Fix any remaining build errors** If there are build errors, fix them. Common issues: - `System.IO.Compression` — now built into .NET 8, no reference needed - Missing `using` statements for new namespaces - API differences in `System.Drawing.Common` on .NET 8 - `LayoutViewGL.cs` exists on disk but was never in the old csproj — SDK-style will auto-include it. Exclude or delete it if it causes errors. **Step 4: Commit** ``` feat: complete .NET 8 migration — update solution and clean up ``` --- ### Task 9: Update CLAUDE.md **Files:** - Modify: `CLAUDE.md` **Step 1: Update build instructions and dependency info** Update the Build section to reflect `dotnet build` instead of `msbuild`, remove references to `packages/` folder and `nuget restore`, update netDxf references to ACadSharp, and note .NET 8 target. **Step 2: Commit** ``` docs: update CLAUDE.md for .NET 8 migration ```