<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Codeart — by Allan Koech]]></title><description><![CDATA[A software engineer's notes on building real systems — C++, Rust, Qt/QML, and the open-source tools that come out of the work.]]></description><link>https://www.codeart.co.ke</link><image><url>https://substackcdn.com/image/fetch/$s_!yEUh!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3da6f509-88fe-4874-9781-59a9b4b5257b_256x256.png</url><title>Codeart — by Allan Koech</title><link>https://www.codeart.co.ke</link></image><generator>Substack</generator><lastBuildDate>Thu, 13 Aug 2026 21:43:20 GMT</lastBuildDate><atom:link href="https://www.codeart.co.ke/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Allan Koech]]></copyright><language><![CDATA[en-gb]]></language><webMaster><![CDATA[allankoech@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[allankoech@substack.com]]></itunes:email><itunes:name><![CDATA[Allan Koech]]></itunes:name></itunes:owner><itunes:author><![CDATA[Allan Koech]]></itunes:author><googleplay:owner><![CDATA[allankoech@substack.com]]></googleplay:owner><googleplay:email><![CDATA[allankoech@substack.com]]></googleplay:email><googleplay:author><![CDATA[Allan Koech]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Microsoft .NET 10 Updates for Autodesk 3ds Max and Maya]]></title><description><![CDATA[Microsoft has announced that support for .NET 8 and .NET 9 will end on November 10, 2026. After this date, Microsoft will no longer provide servicing updates, security updates, or technical support for these versions.]]></description><link>https://www.codeart.co.ke/p/microsoft-net-10-updates-for-3dsmax-maya</link><guid isPermaLink="false">https://www.codeart.co.ke/p/microsoft-net-10-updates-for-3dsmax-maya</guid><dc:creator><![CDATA[Allan Koech]]></dc:creator><pubDate>Wed, 12 Aug 2026 10:48:25 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/b4d0ec9b-4e47-45bb-8cdb-f6cf3d8d2857_1774x887.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Microsoft has announced that support for <strong>.NET 8 and .NET 9 will end on November 10, 2026</strong>. After this date, Microsoft will no longer provide servicing updates, security updates, or technical support for these versions.</p><p>Many Autodesk products include components or dependencies that rely on Microsoft .NET technologies. As part of this commitment to providing highly trustworthy and performant software, Autodesk product teams are planning updates to Autodesk products, where applicable and feasible, to align with Microsoft&#8217;s .NET product support lifecycle.</p><p><strong>3ds Max 2026, Maya 2025, and Maya 2026</strong> have already rolled out security updates that include an upgrade from .NET 8 to .NET 10. Refer to the respective product release notes for more information:</p><ul><li><p><a href="https://help.autodesk.com/view/3DSMAX/2026/ENU/?guid=3dsMax_ReleaseNotes_3dsmax_2026_3_4_releasenotes_html">3ds Max 2026 Release Notes</a></p></li></ul><ul><li><p><a href="https://help.autodesk.com/view/MAYAUL/2026/ENU/?guid=MAYA_RELEASENOTES_2026_3_3_RELEASE_NOTES_HTML">Maya 2026 Release Notes</a></p></li></ul><ul><li><p><a href="https://help.autodesk.com/view/MAYAUL/2025/ENU/?guid=MAYA_RELEASENOTES_2025_3_3_RELEASE_NOTES_HTML">Maya 2025 Release Notes</a></p></li></ul><p><em><strong>Note:</strong> <strong>3ds Max 2024/2025</strong> and <strong>Maya 2024</strong> use .NET Framework 4.8 variants and therefore do not require this .NET 10 upgrade. Microsoft continues to support .NET Framework 4.x versions, including 4.8 and 4.8.1, in accordance with the lifecycle of the Windows operating system on which they are installed. .NET Framework will also continue to be included with Windows. Read more about supported .NET Frameworks <a href="https://dotnet.microsoft.com/en-us/platform/support/policy/dotnet-framework">here</a>.</em></p><p><em><strong>Also note:</strong> 3ds Max 2027 and Maya 2027 ship with .NET 10 by default, so no changes are required for these versions.</em></p><p>As a result of these upgrades, we recommend ensuring that all related Autodesk products are also updated to avoid potential compatibility issues.</p><blockquote><p>For additional background, see our previous blog post <a href="https://blog.autodesk.io/autodesk-desktop-products-2025-2026-net-10-updates/">here</a>.</p></blockquote><h2><strong>What do you need to do?</strong></h2><p>There are <strong>no API changes</strong> in any of these products as part of this update. The only change is that the underlying .NET runtime is moving from .NET 8 to .NET 10.</p><p>Existing plug-ins and add-ins should continue to work unless they are affected by breaking changes introduced in .NET 10. In such cases, they may need to be recompiled and retargeted to <strong>.NET 10</strong>.</p><p>Developers are advised to test their plug-ins and add-ins for compatibility with the updated product releases.</p><p>If you encounter any compatibility issues, please report them through <strong><a href="https://aps.autodesk.com/get-help">ADN Support</a></strong>.</p><h2><strong>Related links</strong></h2><ul><li><p><a href="https://www.autodesk.com/support/technical/article/caas/sfdcarticles/sfdcarticles/Autodesk-Products-What-customers-need-to-know-about-Microsoft-NET-10-updates.html">Autodesk Products: What customers need to know about Microsoft .NET 10 updates</a></p></li></ul><ul><li><p><a href="https://www.autodesk.com/support/technical/article/caas/sfdcarticles/sfdcarticles/3ds-Max-Requirements-for-products-affected-by-the-Microsoft-NET-10-transition.html">3ds Max: Requirements for products affected by the Microsoft .NET 10 transition</a></p></li></ul><ul><li><p><a href="https://www.autodesk.com/support/technical/article/caas/sfdcarticles/sfdcarticles/Maya--Requirements-for-products-affected-by-the-Microsoft--NET-10-transition.html">Maya: Requirements for products affected by the Microsoft .NET 10 transition</a></p></li></ul><ul><li><p><a href="https://learn.microsoft.com/en-us/dotnet/core/compatibility/10">Breaking changes in .NET 10</a></p></li></ul><div><hr></div><p>Also posted here: <a href="https://blog.autodesk.io/microsoft-net-10-updates-for-autodesk-3ds-max-and-maya/">https://blog.autodesk.io/microsoft-net-10-updates-for-autodesk-3ds-max-and-maya/</a> </p><div><hr></div><p></p>]]></content:encoded></item><item><title><![CDATA[Porting Explode Geometry to Python: Life After MaxPlus]]></title><description><![CDATA[For years, one of the friendlier ways to learn the 3ds Max API in Python was the Explode Geometry sample.]]></description><link>https://www.codeart.co.ke/p/porting-explode-geometry-to-pymxs</link><guid isPermaLink="false">https://www.codeart.co.ke/p/porting-explode-geometry-to-pymxs</guid><dc:creator><![CDATA[Allan Koech]]></dc:creator><pubDate>Wed, 05 Aug 2026 08:31:29 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!ariK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7b63c4f-8365-419d-ab93-836fca27c079_832x409.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><span>For years, one of the friendlier ways to learn the 3ds Max API in Python was the </span><strong><span>Explode Geometry</span></strong><span> sample. It took a selected object, shattered it into one new object per face, and let you optionally </span><em><span>shell</span></em><span>, </span><em><span>collapse</span></em><span>, </span><em><span>re-pivot</span></em><span>, or </span><em><span>delete </span></em><span>the result. It was a great teaching example because it touched almost everything a real tool needs: a </span><em><span>Qt dialog</span></em><span>, </span><em><span>selection handling, mesh/poly traversal, modifier stacks, and node management</span></em><span>.</span></p><p><span>There was just one problem. The Python version was built on </span><em><strong><span>MaxPlus</span></strong></em><span>, and MaxPlus is gone (</span><a href="https://help.autodesk.com/view/MAXDEV/2022/ENU/?guid=MAXDEV_Python_what_s_new_in_3ds_max_python_api_html"><span>deprecated and removed from 3ds Max 2022</span></a><span>).</span></p><p><span>This post walks through how we brought the sample back, what changed when we moved it onto the modern pymxs + PySide6 stack, and a few things we improved along the way.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ariK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7b63c4f-8365-419d-ab93-836fca27c079_832x409.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ariK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7b63c4f-8365-419d-ab93-836fca27c079_832x409.png 424w, https://substackcdn.com/image/fetch/$s_!ariK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7b63c4f-8365-419d-ab93-836fca27c079_832x409.png 848w, https://substackcdn.com/image/fetch/$s_!ariK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7b63c4f-8365-419d-ab93-836fca27c079_832x409.png 1272w, https://substackcdn.com/image/fetch/$s_!ariK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7b63c4f-8365-419d-ab93-836fca27c079_832x409.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ariK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7b63c4f-8365-419d-ab93-836fca27c079_832x409.png" width="832" height="409" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d7b63c4f-8365-419d-ab93-836fca27c079_832x409.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:409,&quot;width&quot;:832,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!ariK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7b63c4f-8365-419d-ab93-836fca27c079_832x409.png 424w, https://substackcdn.com/image/fetch/$s_!ariK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7b63c4f-8365-419d-ab93-836fca27c079_832x409.png 848w, https://substackcdn.com/image/fetch/$s_!ariK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7b63c4f-8365-419d-ab93-836fca27c079_832x409.png 1272w, https://substackcdn.com/image/fetch/$s_!ariK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7b63c4f-8365-419d-ab93-836fca27c079_832x409.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em><span>The Explode Geometry dialog (screenshots will be refreshed for the new PySide6 UI).</span></em></p><div class="pullquote"><p>Also published  here: <a href="https://blog.autodesk.io/porting-explode-geometry-to-python-life-after-maxplus/">https://blog.autodesk.io/porting-explode-geometry-to-python-life-after-maxplus/</a></p></div><h2><span>A quick history lesson</span></h2><p><strong><span>MaxPlus </span></strong><span>was the first official Python binding for 3ds Max, introduced back in 2014. It exposed a hand-curated, object-oriented wrapper over the C++ SDK,</span><em><span> classes like MaxPlus.TriObject</span></em><span>, </span><em><span>MaxPlus.Factory</span></em><span>, and </span><em><span>MaxPlus.NotificationManager</span></em><span>.</span></p><p><span>It was useful, but it was also a maintenance burden:</span><em><span> every class and method had to be wrapped by hand</span></em><span>, so coverage was always partial and always lagging the SDK. Meanwhile, </span><strong><span>pymxs,</span></strong><span> which exposes the full MAXScript runtime to Python, kept growing into the more complete, better-supported option.</span></p><p><span>So MaxPlus was </span><strong><span>deprecated in 3ds Max 2021 and removed entirely starting with 3ds Max 2022</span></strong><span>. Any sample that imports MaxPlus stops loading; thus the classic Explode Geometry sample sat abandoned because the port wasn&#8217;t trivial:  MaxPlus and pymxs think about the scene very differently.</span></p><p><span>The original sample is still in the repo as </span><em><span>explode_geometry_classic.py</span></em><span> for historical reference. It&#8217;s a good before/after companion to everything below, but note it only runs on 3ds Max 2016&#8211;2021.</span></p><h2><span>The goals of the port</span></h2><p><span>We set out to do more than merely make it run again:</span></p><ol><li><p><strong><span>Drop every MaxPlus dependency</span></strong><span> and target 3ds Max 2022+ (best on 2026/2027).</span></p></li><li><p><strong><span>Move the UI to PySide6</span></strong><span>, the Qt6 binding shipped with modern 3ds Max.</span></p></li><li><p><strong><span>Keep feature parity</span></strong><span> with the .NET sample&#8217;s options &#8212; shell, edit-mesh, collapse, centre-pivot, delete-original, and the TriMesh/MNMesh choice.</span></p></li><li><p><strong><span>Improve the experience</span></strong><span> while we were in there: a real progress bar, an undoable operation, a debug toggle, and timing feedback.</span></p></li></ol><h3><span>Wiring the window into 3ds Max</span></h3><p><span>MaxPlus had its own helpers for parenting a Qt widget to the Max main window and making it dockable:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;0c9527fe-a8b5-425b-b306-a594b6feb16a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># Classic (MaxPlus) 
form.setParent(MaxPlus.GetQMaxWindow()) 
MaxPlus.MakeQWidgetDockable(form, 4) </code></pre></div><p><span>The modern equivalent uses the </span><strong><span>qtmax </span></strong><span>module that ships with 3ds Max, plus a standard Qt window flag:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;780045f8-cf0b-4940-bae9-d67095cac0b4&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># New (qtmax + PySide6) 
form.setParent(qtmax.GetQMaxMainWindow()) 
form.setWindowFlags(QtCore.Qt.WindowType.Tool) 
form.show() </code></pre></div><p><em><span>qtmax.GetQMaxMainWindow()</span></em><span> returns the real </span><em><span>QMainWindow</span></em><span> for Max, so the dialog behaves like a native tool window &#8212; it stays on top of Max and follows it as expected.</span></p><h3><span>Listening for selection changes</span></h3><p><span>The classic sample registered a selection callback through MaxPlus&#8217; notification manager and tore it down by index:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;084e2e9b-d5e4-444c-90f8-a88ee8164b06&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext"># Classic 
MaxPlus.NotificationManager.Register( 
 MaxPlus.NotificationCodes.SelectionsetChanged, self.updateSelectionLabel) 
... 
MaxPlus.NotificationManager.Unregister(MaxPlus.NotificationManager.Handlers[-1]) </code></pre></div><p><span>With pymxs, we go straight to the MAXScript callback system, which is both more capable and easier to clean up because we can tag callbacks with a named ID:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;3013d8c1-515e-4bd1-b340-8699336aef92&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># New 
rt.callbacks.addScript(rt.Name(&#8217;selectionSetChanged&#8217;), 
 self.updateSelectionLabel, 
 id=rt.Name(&#8217;explodeGeom&#8217;)) 
... 
rt.callbacks.removeScripts(id=rt.Name(&#8217;explodeGeom&#8217;)) </code></pre></div><p><span>Naming the callback group allows us to remove everything under </span><em><span>explodeGeom </span></em><span>when the Qt dialog closes.</span></p><h3><span>The TriMesh path: from low-level mesh to one call</span></h3><p><span>The classic sample built each face by hand against the low-level mesh API, casting to a </span><em><span>TriObject</span></em><span>, grabbing the </span><em><span>Mesh</span></em><span>, setting vertex and face counts, copying verts, and invalidating the geometry cache:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;e1f278c0-23ac-47d1-a0b3-a1dae9b37c9c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># Classic &#8212; manual mesh construction 
new_face = MaxPlus.Factory.CreateNewTriObject() 
n = MaxPlus.Factory.CreateNode(new_face) 
mesh = new_face.GetMesh() 
mesh.SetNumFaces(1) 
mesh.SetNumVerts(3) 
mesh.GetFace(0).SetVerts(0, 1, 2) 
mesh.GetFace(0).SetEdgeVisFlags(1, 1, 1) 
mesh.GetFace(0).SetSmGroup(2) 
for i in range(0, 3): 
 pt = tri_mesh.GetVertex(face.GetVert(i)) 
 mesh.SetVert(i, pt) 
mesh.InvalidateGeomCache() </code></pre></div><p><span>With pymxs we lean on the MAXScript </span><em><span>mesh()</span></em><span> constructor, which takes vertices and faces directly. The whole per-face body is collapsed to a few lines:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;96ecb5cb-da0d-41c8-ac46-3c13e9a6180b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># New &#8212; high-level mesh constructor 
rt.convertToMesh(node) 
num_faces = rt.getNumFaces(node) 
for face_idx in range(1, num_faces + 1): 
 face = rt.getFace(node, face_idx) 
 v1 = rt.getVert(node, int(face.x)) 
 v2 = rt.getVert(node, int(face.y)) 
 v3 = rt.getVert(node, int(face.z)) 
 
 new_node = rt.mesh(vertices=rt.Array(v1, v2, v3), 
 faces=rt.Array(rt.Point3(1, 2, 3))) 
 rt.update(new_node) 
 new_node.wireColor = _random_wire_color() 
 applySettings(new_node, addShell, shell_amount, 
 addEditMesh, collapseNode, centerPivot) 
</code></pre></div><p><span>No manual cache invalidation, and the new node lands in the scene automatically. As a small visual nicety, we also assign each exploded face a random wire colour so you can actually see the individual pieces.</span></p><h3><span>The MNMesh path: detach instead of rebuild</span></h3><p><span>The classic </span><em><span>MNMesh </span></em><span>path was the most painful code in the old sample. To make each polygon, it created a </span><em><span>PolyObject</span></em><span>, reached into the </span><em><span>MNMesh</span></em><span>, set vertex/edge/face counts, positioned every vertex, built a visibility list, called </span><em><span>MakePoly</span></em><span>, then </span><em><span>FillInMesh,</span></em><span> roughly 20 lines of low-level construction per face.</span></p><p><span>The pymxs version sidesteps all of it. Instead of </span><em><span>rebuilding</span></em><span> each face as a new poly, we let </span><em><span>polyop.detachFaces</span></em><span> do the work and hand us back a node directly:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;56d8bbb1-7ceb-44d1-b5bd-e3282ec8480a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># New &#8212; let polyop detach each face into its own node 
rt.convertToPoly(node) 
num_faces = rt.polyop.getNumFaces(node) 
for face_idx in range(num_faces, 0, -1): 
 rt.polyop.detachFaces(node, rt.Array(face_idx), asNode=True) 
 new_node = rt.objects[-1] 
 new_node.wireColor = _random_wire_color() 
 applySettings(new_node, addShell, shell_amount, 
 addEditMesh, collapseNode, centerPivot) 
</code></pre></div><p><span>A couple of details worth noting:</span></p><ul><li><p><em><strong><span>We iterate backwards (num_faces &#8230; 1)</span></strong></em><span>. Detaching a face renumbers the remaining faces, so walking from the end keeps the indices we haven&#8217;t processed yet stable.</span></p></li><li><p><em><strong><span>asNode=True</span></strong></em><span> makes detachFaces create a brand-new node for the detached face, which we then grab as </span><em><span>rt.objects[-1]</span></em><span>.</span></p></li></ul><p><span>This is dramatically simpler, and it leans on Max&#8217;s own, well-tested geometry code instead of our hand-rolled mesh building.</span></p><p><em><span>MNMesh mode: each polygon is detached into its own node.</span></em></p><h3><span>Applying the modifiers</span></h3><p><span>The </span><em><span>applySettings </span></em><span>helper is conceptually identical between versions &#8212; the difference is just how you instantiate a modifier. MaxPlus used the factory and a parameter block:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;4f5aebdf-3044-43b6-83ec-2c68eed966a4&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># Classic 
mod = MaxPlus.Factory.CreateObjectModifier(MaxPlus.ClassIds.Shell) 
mod.ParameterBlock.outerAmount.Value = shell_amount 
n.AddModifier(mod) </code></pre></div><p><span>In pymxs, modifiers are just runtime classes you call like constructors, and their parameters are plain attributes:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;54ef6ccc-c462-4983-ab8f-32788978e9d6&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># New 
mod = rt.Shell() 
mod.outerAmount = shell_amount 
rt.addModifier(n, mod) </code></pre></div><p><span>The same pattern covers </span><em><span>Edit_Mesh</span></em><span>, </span><em><span>maxOps.collapseNode</span></em><span>, and </span><em><span>centerPivot</span></em><span> </span><em><span>(it reads almost as the MAXScript).</span></em></p><h3><span>What we added beyond parity</span></h3><p><span>Since we are rewriting it anyway, why not make the tool better? Here are some quality of life improvements we baked in:</span></p><p><em><strong><span>Undo as a single operation.</span></strong></em><span> Exploding a model creates a </span><em><span>lot</span></em><span> of nodes. Wrapping the whole run in a pymxs.undo block means a single Ctrl+Z reverts the entire explode instead of unwinding it one face at a time:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;86a5da88-cf4f-4d50-b60d-7ab6794441ee&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">with pymxs.undo(True, &#8220;Explode Geometry&#8221;): 
 ... # all node creation/deletion happens here </code></pre></div><p><em><strong><span>A real progress panel.</span></strong></em><span> Exploding a dense mesh can take a while, so the dialog now shows a progress bar plus the current node name and an </span><em><span>N of M</span></em><span> counter. This matches the .NET dialog with a progress bar. The conversion functions take an on_progress callback and pump </span><em><span>QApplication.processEvents()</span></em><span> so the UI stays responsive:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;ca83ca17-fabf-4039-9602-ede063bfc866&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def _prog(cur, tot): 
 self.progress_bar.setValue(int(cur * 100 / tot) if tot else 0) 
 QtWidgets.QApplication.processEvents() </code></pre></div><p><em><strong><span>A debug toggle.</span></strong></em><span> Rather than littering the code with print calls, we route everything through Python&#8217;s logging module to the MAXScript Listener. A </span><em><span>&#8220;Show Debug Messages&#8221;</span></em><span> checkbox flips the logger between WARNING and DEBUG. </span></p><blockquote><p>Note: Verbose logging affects performance, especially around hot paths and loops when there are many nodes/faces to explode. The completion summary is always printed regardless of the toggle.</p></blockquote><p><em><strong>Timing feedback.</strong></em> We wrap the run with time.perf_counter() and report how long the explode took &#8212; handy when you&#8217;re comparing the TriMesh and MNMesh paths on the same model.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;bd156941-3f24-41e3-bf14-9eedc8381591&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">pymxs.print_(&#8221;[ExplodeGeometry] Explode completed in {:.2f} seconds.\n&#8221;.format(elapsed)) </code></pre></div><h2><span>Python 2 to Python 3, while we&#8217;re here</span></h2><p><span>The classic sample was Python 2 </span><em><span>(note the print msg statements and the Qt4-era PySide/QtGui imports)</span></em><span>. The port is clean Python 3 on PySide6: </span><em><span>QtWidgets </span></em><span>instead of QtGui, </span><em><span>pymxs.print_()</span></em><span> for Listener output, and modern signal connections like </span><em><span>checkStateChanged</span></em><span>.</span></p><h2><span>Shipping it as an AppBundle</span></h2><p><span>Running a script from the editor is fine for development, but the sample also ships as a proper </span><strong><span>Automatic Loader AppBundle</span></strong><span> under Bundle2/, so it can show up as a menu entry right next to the .NET version. The package&#8217;s </span><a href="https://github.com/ADN-DevTech/3dsMax-Explode-Geometry/tree/master/Bundle2/Contents/python"><span>Contents/python/</span></a><span> folder carries </span><em><span>explode_geometry.py</span></em><span>, and a </span><a href="https://github.com/ADN-DevTech/3dsMax-Explode-Geometry/blob/master/Bundle2/Contents/macroscripts/ADNExplodeGeometryPy.mcr"><span>small macroscript</span></a><span> launches it:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;4d2c52f0-c01c-480e-a619-d9e7e6cc67a8&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">macroScript ADNExplodeGeomPyMS 
  category:"ADN Samples"
  tooltip:"Explode Geometry (Python)" 
  buttonText:"Explode Geometry (Python)"
( 
 local scriptDir = getFilenamePath (getSourceFileName()) 
 local pyFile = scriptDir + "..\python\explode_geometry.py"
 python.ExecuteFile pyFile 
) 
</code></pre></div><h3><span>How do we run the AppBundle?</span></h3><p><span>You have two ways to get 3ds Max to load the bundle:</span></p><ul><li><p><strong><span>Copy it into an auto-discovered plug-in folder.</span></strong><span> 3ds Max automatically scans the ApplicationPlugins directories on startup. Drop the Bundle2 folder </span><em><span>(renamed to something like ADN-ExplodeGeometry.bundle)</span></em><span> into one of:</span></p><ul><li><p><em><span>%PROGRAMDATA%\Autodesk\ApplicationPlugins</span></em><span> (all users), or</span></p></li><li><p><em><span>%APPDATA%\Autodesk\ApplicationPlugins</span></em><span> (current user).</span></p></li></ul></li><li><p><strong><span>Point an environment variable at it.</span></strong><span> Set </span><strong><span>ADSK_APPLICATION_PLUGINS</span></strong><span> to the folder containing your bundle before launching Max &#8212; handy when you&#8217;re iterating and don&#8217;t want to copy files around.</span></p></li></ul><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;shell&quot;,&quot;nodeId&quot;:&quot;eb011b7b-e7b8-4960-b900-85d5f0212395&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-shell">#Set ENV var (check terminal specifics for PowerShell/CMD/etc)
set ADSK_APPLICATION_PLUGINS=D:/path/to/bundle/parent/dir

# Launch mamax in the same shell
3dsmax.exe </code></pre></div><div class="pullquote"><p>It&#8217;s not advisable to copy plugins to the default Max installation directories (&lt;max&gt;/Plugins, &lt;max&gt;/scripts, etc). Use the external plugins directory shown above for a cleaner way to add and remove plugins. All plugins shipped via the Autodesk AppStore are installed in the external plugin directories; rest assured, Max scans these directories at startup and will discover your plugins.</p></div><p><span>See the 3ds Max developer docs on the </span><strong><a href="https://help.autodesk.com/view/MAXDEV/2026/ENU/?guid=packagexml_format"><span>Plug-in Package (.bundle) format</span></a></strong><span> and </span><em><span>PackageContents.xml</span></em><span> for the full directory layout and the auto-loader search paths.</span></p><p><span>Once the bundle loads cleanly, you&#8217;ll find the tool on the main menu under </span><em><strong><span>App Store &#8594; Explode Geometry (Python)</span></strong></em><span>, sitting alongside the original </span><strong><span>Explode Geometry</span></strong><span> (.NET) entry. Select your objects, launch it, and you'll see the same dialogue you&#8217;d see in the editor.</span></p><blockquote><p>Not ethat the Python menu entry is part of the GitHub sample, but won&#8217;t show if you install the plugin from the AppStore.</p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Wk7Y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28eb7250-3b8f-4c52-94af-2183cc4abe8e_1475x520.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Wk7Y!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28eb7250-3b8f-4c52-94af-2183cc4abe8e_1475x520.png 424w, https://substackcdn.com/image/fetch/$s_!Wk7Y!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28eb7250-3b8f-4c52-94af-2183cc4abe8e_1475x520.png 848w, https://substackcdn.com/image/fetch/$s_!Wk7Y!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28eb7250-3b8f-4c52-94af-2183cc4abe8e_1475x520.png 1272w, https://substackcdn.com/image/fetch/$s_!Wk7Y!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28eb7250-3b8f-4c52-94af-2183cc4abe8e_1475x520.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Wk7Y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28eb7250-3b8f-4c52-94af-2183cc4abe8e_1475x520.png" width="1456" height="513" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/28eb7250-3b8f-4c52-94af-2183cc4abe8e_1475x520.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:513,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:52017,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.codeart.co.ke/i/208668741?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28eb7250-3b8f-4c52-94af-2183cc4abe8e_1475x520.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Wk7Y!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28eb7250-3b8f-4c52-94af-2183cc4abe8e_1475x520.png 424w, https://substackcdn.com/image/fetch/$s_!Wk7Y!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28eb7250-3b8f-4c52-94af-2183cc4abe8e_1475x520.png 848w, https://substackcdn.com/image/fetch/$s_!Wk7Y!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28eb7250-3b8f-4c52-94af-2183cc4abe8e_1475x520.png 1272w, https://substackcdn.com/image/fetch/$s_!Wk7Y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28eb7250-3b8f-4c52-94af-2183cc4abe8e_1475x520.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>One nuance worth knowing: the Python menu entry is only registered on </span><strong><span>3ds Max 2027 and up</span></strong><span>. The post-start-up menu script gates it on the Max version:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;fc07d7cb-97e4-4a30-a1df-0d9b8c82fd9d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">-- Only add the Python menu entry for 3ds Max 2027+ (maxVersion 29000), 
-- where Python support in the menu system is available. 
if (maxVersion())[1] &gt;= 29000 then 
( 
 newSubMenu.CreateAction "85045470-A03C-45EC-A13F-538346AF077A" 647394 "ADNExplodeGeomPyMS`ADN Samples" 
) 
</code></pre></div><p><span>The .NET action is added on all supported versions; the Python one is for 2027+ only.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!z9vb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fd5f7fd-4863-4f1b-a09c-bc11c8b770b6_270x356.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!z9vb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fd5f7fd-4863-4f1b-a09c-bc11c8b770b6_270x356.png 424w, https://substackcdn.com/image/fetch/$s_!z9vb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fd5f7fd-4863-4f1b-a09c-bc11c8b770b6_270x356.png 848w, https://substackcdn.com/image/fetch/$s_!z9vb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fd5f7fd-4863-4f1b-a09c-bc11c8b770b6_270x356.png 1272w, https://substackcdn.com/image/fetch/$s_!z9vb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fd5f7fd-4863-4f1b-a09c-bc11c8b770b6_270x356.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!z9vb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fd5f7fd-4863-4f1b-a09c-bc11c8b770b6_270x356.png" width="270" height="356" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0fd5f7fd-4863-4f1b-a09c-bc11c8b770b6_270x356.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:356,&quot;width&quot;:270,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:38664,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.codeart.co.ke/i/208668741?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fd5f7fd-4863-4f1b-a09c-bc11c8b770b6_270x356.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!z9vb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fd5f7fd-4863-4f1b-a09c-bc11c8b770b6_270x356.png 424w, https://substackcdn.com/image/fetch/$s_!z9vb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fd5f7fd-4863-4f1b-a09c-bc11c8b770b6_270x356.png 848w, https://substackcdn.com/image/fetch/$s_!z9vb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fd5f7fd-4863-4f1b-a09c-bc11c8b770b6_270x356.png 1272w, https://substackcdn.com/image/fetch/$s_!z9vb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fd5f7fd-4863-4f1b-a09c-bc11c8b770b6_270x356.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h2><span>A note on Qt versions</span></h2><p><span>The sample is written against </span><strong><span>PySide6 (Qt 6)</span></strong><span>, which is what ships with 3ds Max </span><strong><span>2026 and 2027</span></strong><span> &#8212; so on those releases it runs as-is.</span></p><p><span>On </span><strong><span>3ds Max 2022&#8211;2025</span></strong><span>, the bundled Qt is the </span><strong><span>PySide2 (Qt 5)</span></strong><span> generation, so the imports and a few API details won&#8217;t match out of the box. If you want to run it there, you&#8217;ll need to adjust things like:</span></p><ul><li><p><span>from PySide6 import ... &#8594; from PySide2 import ...</span></p></li><li><p><span>Scoped enums back to flat ones &#8212; e.g. QtCore.Qt.WindowType.Tool &#8594; QtCore.Qt.Tool.</span></p></li><li><p><span>Signal differences &#8212; e.g. the Qt6 checkStateChanged signal versus Qt5&#8217;s stateChanged.</span></p></li><li><p><span>QDialog/QMessageBox.exec() versus the older exec_().</span></p></li></ul><p><span>None of these is hard, but they&#8217;re version-specific. Consider it a good exercise in learning where PySide2 and PySide6 diverge and a reminder always to check which Qt your target Max ships before writing UI code.</span></p><h2><span>Lessons learned</span></h2><p><span>A few takeaways for anyone facing a similar MaxPlus migration:</span></p><ul><li><p><strong><span>pymxs is usually less code, not more.</span></strong><span> The instinct is to fear that dropping a &#8220;high-level&#8221; wrapper means more boilerplate. In practice, the opposite was true &#8212; rt.mesh(...) and polyop.detachFaces(..., asNode=True) replaced dozens of lines of manual construction.</span></p></li><li><p><strong><span>Prefer Max&#8217;s own operations over rebuilding geometry.</span></strong><span> The biggest simplification came from detaching faces instead of reconstructing them. When the SDK already does the thing you want, use it.</span></p></li><li><p><strong><span>Watch index invalidation.</span></strong><span> Anything that adds or removes faces/nodes mid-loop can shift the indices under you. Iterating in reverse is a cheap fix.</span></p></li><li><p><strong><span>qtmax is your friend for UI hosting.</span></strong><span> GetQMaxMainWindow() plus a Tool window flag gives you a well-behaved dialog without MaxPlus&#8217; dockable helpers.</span></p></li></ul><h2><span>Try it out</span></h2><p><span>The ported sample lives in the </span><a href="https://github.com/ADN-DevTech/3dsMax-Explode-Geometry"><span>Explode Geometry repository</span></a><span> under Python/explode_geometry.py. There are two ways to run it:</span></p><ul><li><p><strong><span>Straight from the editor</span></strong><span> &#8212; open Python/explode_geometry.py in the 3ds Max scripting editor and execute it. Quickest for poking at the code.</span></p></li><li><p><strong><span>As an AppBundle</span></strong><span> &#8212; load the Bundle2/ package as described above and launch it from </span><strong><span>App Store &#8594; Explode Geometry (Python)</span></strong><span> (2027+).</span></p></li></ul><p><span>Either way: select an object (or several), pick </span><em><span>TriMesh </span></em><span>or </span><em><span>MNMesh</span></em><span>, choose your options, and hit </span><strong><span>Explode</span></strong><span>. Then enjoy a single Ctrl+Z to put it all back together. It runs as-is on 3ds Max 2026/2027; for 2022&#8211;2025 you&#8217;ll need the PySide2 tweaks noted above.</span></p><div><hr></div><p><em><span>The original .NET plug-in was written by Kevin Vandecar, from an idea by Louis Marcoux. The first Python version was developed by Drew Avis. This pymxs port modernises that work for 3ds Max 2022 and beyond.</span></em></p>]]></content:encoded></item><item><title><![CDATA[Setting Up 3ds Max 2027 SDK Development with Visual Studio 2026 ]]></title><description><![CDATA[Visual Studio 2026 landed in late 2025, and if you write plugins for 3ds Max, the first question is the obvious one: can I move my development environment to the new release, or do I need to stay put? The short answer is that you can, with one important caveat about the compiler toolset. This post walks through what changed, what the 3ds Max 2027 SDK requires, and how to get a clean, working build environment on the new IDE.]]></description><link>https://www.codeart.co.ke/p/setting-up-3ds-max-2027-sdk-development</link><guid isPermaLink="false">https://www.codeart.co.ke/p/setting-up-3ds-max-2027-sdk-development</guid><dc:creator><![CDATA[Allan Koech]]></dc:creator><pubDate>Fri, 31 Jul 2026 08:41:41 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/064b7738-5a66-4532-9422-b5c04d4293dd_1023x576.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Visual Studio 2026 landed in late 2025, and if you write plugins for 3ds Max, the first question is the obvious one: <em><strong>can I move my development environment to the new release, or do I need to stay put</strong></em>? The short answer is that you can, with one important caveat about the compiler toolset. This post walks through what changed, what the 3ds Max 2027 SDK requires, and how to get a clean, working build environment on the new IDE. </p><div><hr></div><p>Original posting here: <a href="https://blog.autodesk.io/setting-up-3ds-max-2027-sdk-development-with-visual-studio-2026/">https://blog.autodesk.io/setting-up-3ds-max-2027-sdk-development-with-visual-studio-2026/</a> </p><div><hr></div><h2>What does the 3ds Max 2027 SDK require? </h2><p>Per the official <em><a href="https://help.autodesk.com/view/MAXDEV/2027/ENU/?guid=sdk_requirements">SDK </a></em><a href="https://help.autodesk.com/view/MAXDEV/2027/ENU/?guid=sdk_requirements">Requirements page</a>, 3ds Max 2027 targets: </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!l_W2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6127590f-3f8d-4e26-9258-82722cc2879e_1889x698.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!l_W2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6127590f-3f8d-4e26-9258-82722cc2879e_1889x698.png 424w, https://substackcdn.com/image/fetch/$s_!l_W2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6127590f-3f8d-4e26-9258-82722cc2879e_1889x698.png 848w, https://substackcdn.com/image/fetch/$s_!l_W2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6127590f-3f8d-4e26-9258-82722cc2879e_1889x698.png 1272w, https://substackcdn.com/image/fetch/$s_!l_W2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6127590f-3f8d-4e26-9258-82722cc2879e_1889x698.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!l_W2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6127590f-3f8d-4e26-9258-82722cc2879e_1889x698.png" width="1456" height="538" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6127590f-3f8d-4e26-9258-82722cc2879e_1889x698.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:538,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:81178,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.codeart.co.ke/i/204415830?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6127590f-3f8d-4e26-9258-82722cc2879e_1889x698.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!l_W2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6127590f-3f8d-4e26-9258-82722cc2879e_1889x698.png 424w, https://substackcdn.com/image/fetch/$s_!l_W2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6127590f-3f8d-4e26-9258-82722cc2879e_1889x698.png 848w, https://substackcdn.com/image/fetch/$s_!l_W2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6127590f-3f8d-4e26-9258-82722cc2879e_1889x698.png 1272w, https://substackcdn.com/image/fetch/$s_!l_W2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6127590f-3f8d-4e26-9258-82722cc2879e_1889x698.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The SDK Requirements page lists the IDE version as <strong>VS2022 17.14.13</strong>, but what your build must match is the <strong>MSVC v14.38 (17.8) toolset</strong> within the <em>v143 family</em> &#8212; see the next section for why that difference matters. Any edition of Visual Studio <em>(Community, Professional, or Enterprise)</em> can build 3ds Max plugins; the edition doesn&#8217;t matter, the toolset version does. </p><p>The SDK Requirements page names <em>Visual Studio 2022 17.14.13</em> as the IDE baseline, but the build itself is pinned to a specific MSVC v143 build-tools version <em><strong>&#8212; v14.38 (17.8)</strong></em>, which Microsoft now marks as Out of Support. &#8220;v143&#8221; is not a single compiler; it&#8217;s a family that spans many MSVC minor versions <em>(14.30 through 14.44 and beyond)</em>, and 3ds Max 2027 is ABI-locked to the v14.38 set. The platform-toolset name stays v143 across Visual Studio 2022 and Visual Studio 2026 for backward compatibility, but the minor version underneath is what has to match. </p><p>This is why Visual Studio 2026 can host 3ds Max 2027 development: the newer IDE is able to install and target the older, out-of-support v14.38 build tools side by side with its own. 3ds Max and its plugins are extremely sensitive to ABI (Application Binary Interface) matching; a plugin must be built with a toolchain binary compatible with the one Autodesk used to build 3ds Max itself. Installing and selecting the v14.38 toolset is what keeps you inside that compatible window while still letting you run the newer IDE. </p><blockquote><p>NOTE: The new IDE is fine; the exact build-tools version is what must match. Build against the v14.38 (17.8) v143 toolset, not whatever newer toolset Visual Studio 2026 installs by default. </p></blockquote><h2>What does Visual Studio 2026 ship with? </h2><p>A default Visual Studio 2026 installation will not build a 3ds Max 2027 SDK project. Out of the box, VS2026 ships: </p><ul><li><p>the MSVC <strong>v14.44</strong> build tools (the current v143 compiler), and </p></li><li><p>the Windows 11 SDK 10.0.26100.x. </p></li></ul><p>3ds Max 2027, by contrast, is built against the MSVC v14.38 (17.8) v143 build tools and the Windows 10 <strong>SDK 10.0.19041.0</strong>. Both VS2026&#8217;s defaults are newer than what the SDK expects, and that&#8217;s enough to break the build. To make matters more confusing, the v14.38 (17.8) toolset is now marked Out of Support by Microsoft, so it&#8217;s tucked away in the installer&#8217;s Individual components list rather than offered by default. </p><p>So, you must add the exact pieces Autodesk validated, and they come from two different places: </p><ul><li><p><strong>MSVC v143 &#8212; VS 2022 C++ x64/x86 build tools (v14.38-17.8)</strong> &#8212; through the Visual Studio installer&#8217;s Individual components (under Out of Support). </p></li><li><p><strong>Windows 10 SDK (10.0.19041.0) </strong>&#8212; this one is not in the VS2026 installer at all. You download and install it separately, from the older-version downloads on the Windows SDK site. </p></li></ul><p>The newer IDE can host the older, ABI-compatible v14.38 toolchain side by side with its own v14.44. But &#8220;possible&#8221; still means &#8220;you have to install it deliberately and then point your project at it.&#8221; The next section is exactly that; install the right pieces, then pin the project to them. </p><h2>How to set up VS2026 for 3ds Max 2027 </h2><h3>Assumptions and prerequisites </h3><ul><li><p>A licensed copy of 3ds Max 2027 installed (you need a real install to test your plugin and to point to your debugger at 3dsmax.exe). </p></li><li><p>The 3ds Max 2027 SDK installed. </p></li><li><p>Qt 6.8.3 (the LGPL build) if your plugin uses Qt-based UI. Match this version exactly. </p></li><li><p>.NET 10 SDK if you build managed or mixed-mode (C++/CLI) assemblies. </p></li></ul><h3>Install the right Visual Studio workloads and components </h3><p>In the VS2026 installer, select the workloads: </p><ul><li><p>Desktop development with C++. </p></li><li><p>.NET desktop development, if you build any managed components. </p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Y1lI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70501544-5725-4dea-aae8-9b3b2d63eb0e_3200x1706.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Y1lI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70501544-5725-4dea-aae8-9b3b2d63eb0e_3200x1706.png 424w, https://substackcdn.com/image/fetch/$s_!Y1lI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70501544-5725-4dea-aae8-9b3b2d63eb0e_3200x1706.png 848w, https://substackcdn.com/image/fetch/$s_!Y1lI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70501544-5725-4dea-aae8-9b3b2d63eb0e_3200x1706.png 1272w, https://substackcdn.com/image/fetch/$s_!Y1lI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70501544-5725-4dea-aae8-9b3b2d63eb0e_3200x1706.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Y1lI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70501544-5725-4dea-aae8-9b3b2d63eb0e_3200x1706.png" width="3200" height="1706" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/70501544-5725-4dea-aae8-9b3b2d63eb0e_3200x1706.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1706,&quot;width&quot;:3200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:347601,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.codeart.co.ke/i/204415830?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F832a6b7f-ab1d-4b5a-9720-173dcf9a42c0_3200x1800.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Y1lI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70501544-5725-4dea-aae8-9b3b2d63eb0e_3200x1706.png 424w, https://substackcdn.com/image/fetch/$s_!Y1lI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70501544-5725-4dea-aae8-9b3b2d63eb0e_3200x1706.png 848w, https://substackcdn.com/image/fetch/$s_!Y1lI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70501544-5725-4dea-aae8-9b3b2d63eb0e_3200x1706.png 1272w, https://substackcdn.com/image/fetch/$s_!Y1lI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70501544-5725-4dea-aae8-9b3b2d63eb0e_3200x1706.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The workload alone is not enough, you need two more pieces, and they&#8217;re installed differently: </p><ol><li><p><strong>The v14.38 build tools &#8212; </strong><em><strong>from the Visual Studio installer</strong></em>. Open the installer, switch to the Individual components tab, and use the search box to type v14.38. The matching entry &#8212; MSVC v143 - VS 2022 C++ x64/x86 build tools (v14.38-17.8) &#8212; appears under the Out of Support group; the installer will warn that it&#8217;s unsupported. That&#8217;s expected. Tick it and install it. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zEcB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F423aaf6f-86f4-49bf-ba47-57906e8fa792_3200x1731.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zEcB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F423aaf6f-86f4-49bf-ba47-57906e8fa792_3200x1731.png 424w, https://substackcdn.com/image/fetch/$s_!zEcB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F423aaf6f-86f4-49bf-ba47-57906e8fa792_3200x1731.png 848w, https://substackcdn.com/image/fetch/$s_!zEcB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F423aaf6f-86f4-49bf-ba47-57906e8fa792_3200x1731.png 1272w, https://substackcdn.com/image/fetch/$s_!zEcB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F423aaf6f-86f4-49bf-ba47-57906e8fa792_3200x1731.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zEcB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F423aaf6f-86f4-49bf-ba47-57906e8fa792_3200x1731.png" width="1456" height="788" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/423aaf6f-86f4-49bf-ba47-57906e8fa792_3200x1731.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:788,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:527852,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.codeart.co.ke/i/204415830?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F423aaf6f-86f4-49bf-ba47-57906e8fa792_3200x1731.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!zEcB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F423aaf6f-86f4-49bf-ba47-57906e8fa792_3200x1731.png 424w, https://substackcdn.com/image/fetch/$s_!zEcB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F423aaf6f-86f4-49bf-ba47-57906e8fa792_3200x1731.png 848w, https://substackcdn.com/image/fetch/$s_!zEcB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F423aaf6f-86f4-49bf-ba47-57906e8fa792_3200x1731.png 1272w, https://substackcdn.com/image/fetch/$s_!zEcB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F423aaf6f-86f4-49bf-ba47-57906e8fa792_3200x1731.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div></li><li><p><strong>Windows 10 SDK 10.0.19041.0 &#8212; </strong><em><strong>as a separate download</strong></em>. <span>This version is </span><em><span>not</span></em><span> offered in the VS2026 installer. Download it on its own from the </span><a href="https://go.microsoft.com/fwlink/?linkid=2311805"><span>Windows SDK downloads site</span></a><span> and run that installer. Once installed, it becomes selectable as a target SDK in your project.</span></p></li></ol><h3>Installing the 3ds Max plugin project wizard </h3><p>The SDK ships an AppWizard that adds a <strong>&#8220;3ds Max Plugin&#8221;</strong> template to Visual Studio&#8217;s New Project dialog, pre-filled with the include paths, libraries, and project structure a plugin needs. It was written for Visual Studio 2022, but the setup for VS2026 is essentially works, you just copy the wizard files into the VS2026 IDE directory instead of the VS2022 one. </p><p>The wizard lives in: </p><p><code>3ds Max 2027 SDK\maxsdk\howto\3dsmaxPluginWizard\ </code></p><p>Open the <strong>readme.txt</strong> in that folder and follow its steps, with one substitution: wherever it points you at the Visual Studio 2022 IDE directory, use the VS2026 IDE directory instead <em>(the path where VS2026 is installed, under its Common7\IDE location)</em>. Copy the wizard&#8217;s .vsz / .vsdir and template files to the locations the readme describes, restart Visual Studio, and the 3ds Max plugin template appears in the New Project dialog. </p><h3>Create, Build, load, verify </h3><p>Similar to VS2022 workflow, create projects utilizing the Plugin Wizard for 3ds Max 2027. Projects generated still default to the correct v143 toolset.  </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TUSE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab07dfe7-5252-4f6e-93e3-a317769f7d96_2225x1437.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TUSE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab07dfe7-5252-4f6e-93e3-a317769f7d96_2225x1437.png 424w, https://substackcdn.com/image/fetch/$s_!TUSE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab07dfe7-5252-4f6e-93e3-a317769f7d96_2225x1437.png 848w, https://substackcdn.com/image/fetch/$s_!TUSE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab07dfe7-5252-4f6e-93e3-a317769f7d96_2225x1437.png 1272w, https://substackcdn.com/image/fetch/$s_!TUSE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab07dfe7-5252-4f6e-93e3-a317769f7d96_2225x1437.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TUSE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab07dfe7-5252-4f6e-93e3-a317769f7d96_2225x1437.png" width="1456" height="940" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ab07dfe7-5252-4f6e-93e3-a317769f7d96_2225x1437.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:940,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:204251,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.codeart.co.ke/i/204415830?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab07dfe7-5252-4f6e-93e3-a317769f7d96_2225x1437.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!TUSE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab07dfe7-5252-4f6e-93e3-a317769f7d96_2225x1437.png 424w, https://substackcdn.com/image/fetch/$s_!TUSE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab07dfe7-5252-4f6e-93e3-a317769f7d96_2225x1437.png 848w, https://substackcdn.com/image/fetch/$s_!TUSE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab07dfe7-5252-4f6e-93e3-a317769f7d96_2225x1437.png 1272w, https://substackcdn.com/image/fetch/$s_!TUSE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab07dfe7-5252-4f6e-93e3-a317769f7d96_2225x1437.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Build and Run in Hybrid| x64, and confirm your plugin appears and loads cleanly. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!EH4B!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6d71e9b-d078-478f-af53-0b1cfa761eab_2492x677.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!EH4B!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6d71e9b-d078-478f-af53-0b1cfa761eab_2492x677.png 424w, https://substackcdn.com/image/fetch/$s_!EH4B!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6d71e9b-d078-478f-af53-0b1cfa761eab_2492x677.png 848w, https://substackcdn.com/image/fetch/$s_!EH4B!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6d71e9b-d078-478f-af53-0b1cfa761eab_2492x677.png 1272w, https://substackcdn.com/image/fetch/$s_!EH4B!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6d71e9b-d078-478f-af53-0b1cfa761eab_2492x677.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!EH4B!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6d71e9b-d078-478f-af53-0b1cfa761eab_2492x677.png" width="1456" height="396" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a6d71e9b-d078-478f-af53-0b1cfa761eab_2492x677.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:396,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:67429,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.codeart.co.ke/i/204415830?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6d71e9b-d078-478f-af53-0b1cfa761eab_2492x677.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!EH4B!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6d71e9b-d078-478f-af53-0b1cfa761eab_2492x677.png 424w, https://substackcdn.com/image/fetch/$s_!EH4B!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6d71e9b-d078-478f-af53-0b1cfa761eab_2492x677.png 848w, https://substackcdn.com/image/fetch/$s_!EH4B!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6d71e9b-d078-478f-af53-0b1cfa761eab_2492x677.png 1272w, https://substackcdn.com/image/fetch/$s_!EH4B!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6d71e9b-d078-478f-af53-0b1cfa761eab_2492x677.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>If all was configured correctly, you should build successfully and 3ds Max should start and load the plugin. </p><h3>Tips and tricks </h3><p><em>Start from the SDK samples. The 3ds Max 2027 SDK ships sample projects that are already configured correctly. Building one of them first verifies your environment before you po</em></p><div><hr></div><p>Always cross-check exact version numbers against the official 3ds Max 2027 SDK Requirements page before you commit your build configuration &#8212; point releases can shift toolsets and Windows SDK expectations.</p><p></p>]]></content:encoded></item><item><title><![CDATA[Singleton to Dependency Injection: A C++ Refactor]]></title><description><![CDATA[Why MantisBase is replacing the MantisBase::instance() singleton with dependency injection in v0.4.x; testability, honest signatures, and multiple instances per process.]]></description><link>https://www.codeart.co.ke/p/ripping-out-the-singleton</link><guid isPermaLink="false">https://www.codeart.co.ke/p/ripping-out-the-singleton</guid><dc:creator><![CDATA[Allan Koech]]></dc:creator><pubDate>Sun, 26 Jul 2026 17:58:22 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/1774abd6-bf49-46be-8546-f30751e19254_1672x941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Every codebase has a decision that felt obviously correct on day one and quietly turns into a debt you pay forever. For MantisBase, that decision was making the application object a singleton.</p><p>It made sense at the time. There is exactly one server, listening on exactly one port, owning one database pool, one router, and one logger. Why <em><strong>wouldn&#8217;t</strong></em> there be one global handle to reach it all? For most of <em>v0.1</em> through <em>v0.3</em>, that instinct served us well. In <em>v0.4.x</em> we are pulling the cord, replacing <em>MantisBase::instance()</em> with plain old dependency injection. This post is about why, and it&#8217;s worth starting with what a singleton actually <strong>is</strong> before explaining why we&#8217;re purging ours.</p><h2>A quick detour through design patterns</h2><p>If you&#8217;ve written software for any length of time you&#8217;ve absorbed design patterns whether or not you&#8217;ve read the book <em><a href="https://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612">Design Patterns: Elements of Reusable Object-Oriented Software</a></em><a href="https://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612"> (Gamma, Helm, Johnson &amp; Vlissides, 1994)</a>. Patterns are not language features or libraries; they&#8217;re names for shapes that keep recurring. Having a shared vocabulary is genuinely useful: <em>&#8220;wrap it in a decorator&#8221;</em> communicates a whole structure in three words.</p><p><em><strong>And then there&#8217;s Singleton.</strong></em></p><p>The Singleton pattern <em>&#8220;ensures a class has only one instance and provides a global point of access to it.&#8221;</em> Two promises in one sentence, and the second one is the problem. It&#8217;s the only creational pattern in the book that most of its own authors would now tell you to avoid. In a 2009 interview, Erich Gamma &#8212; one of the four &#8212; said plainly:</p><blockquote><p><em>&#8220;When discussing which patterns to drop, we found that we still love them all. (Not really, I&#8217;m in favor of dropping Singleton. Its use is almost always a design smell.)&#8221;</em></p><p>&#8212; Erich Gamma</p></blockquote><p>The reason he gives is the reason this post exists.</p><h2>How MantisBase became a singleton</h2><p>MantisBase is an embeddable backend, a BaaS you link into a C++ process and drive. The <em>MantisBase</em> class is the composition root: it owns every subsystem and hands them out.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;cpp&quot;,&quot;nodeId&quot;:&quot;8e679110-9320-49d7-af04-c1b495324b35&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-cpp">class MANTISBASE_API MantisBase
{
public:
    static MantisBase&amp; instance();
    static MantisBase&amp; create(const json&amp; config = json::object());

    [[nodiscard]] Database&amp; db() const;
    [[nodiscard]] Router&amp; router() const;
    [[nodiscard]] Logger&amp; logs() const;
    [[nodiscard]] RealtimeDB&amp; rt() const;

    // ...

private:
    MantisBase();                       // creation is private
    static MantisBase&amp; getInstanceImpl();
    std::atomic&lt;bool&gt; m_isCreated = false;
};</code></pre></div><p>The mechanics are the textbook version. The constructor is private, <em>`create()`</em> builds the one instance, and <em>`instance()`</em> hands it back:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;cpp&quot;,&quot;nodeId&quot;:&quot;4071887d-1e93-4861-9819-9ea9636d3e94&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-cpp">MantisBase &amp;MantisBase::instance() {
    auto &amp;app_instance = getInstanceImpl();
    if (!app_instance.isCreated())
        throw std::runtime_error("MantisBase not created yet");

    return app_instance;
}

MantisBase &amp;MantisBase::create(const json &amp;config) {
    auto &amp;app = getInstanceImpl();
    if (app.isCreated())
        throw std::runtime_error("MantisBase already created, "
                                 "use MantisBase::instance() instead.");

    // ...
}</code></pre></div><p>Even our own header documented the constraint as a feature:</p><div class="callout-block" data-callout="true"><p><em>MantisBase is a single-instance-per-process type (a managed singleton): the whole codebase reaches the active instance through <strong>instance()</strong>. Multiple concurrent instances in one process are not supported; <strong>create()</strong> throws if an instance already exists.</em></p></div><p>Once that door is open, the whole codebase walks through it. Reaching the <em>database</em> from a request handler, a <em>model</em>, an <em>auth check, or</em> a <em>file path</em> resolver&#8212;all of it collapses into one convenient line:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;cpp&quot;,&quot;nodeId&quot;:&quot;848c52a2-92f9-4881-a13b-4644c99f5434&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-cpp">// src/core/api_keys.cpp
auto sql = MantisBase::instance().db().session();

// src/core/files.cpp
return fs::path(MantisBase::instance().dataDir()) / "files";

// src/core/sse_mgr.cpp
auto &amp;router = MantisBase::instance().router();</code></pre></div><p>At last count, that pattern appeared across dozens of translation units, and that ubiquity is exactly the trap. Every one of those call sites is a dependency that doesn&#8217;t appear in any signature. Which brings us to why we&#8217;re ripping it out.</p><h2>Why we&#8217;re ripping it out</h2><h3>1. A singleton is a global variable, dressed nicely</h3><p>The case against globals is old and settled. Wulf and Shaw made it in 1973 in <em><strong>&#8220;Global Variable Considered Harmful&#8221;</strong></em>: non-local access makes it impossible to reason about a piece of code in isolation, because any part of the program can influence any other through the shared name. A singleton doesn&#8217;t repeal that argument; it just dresses it in a class with a <em>`static`</em> accessor. As Mi&#353;ko Hevery put it in his widely cited critique, <em><a href="https://testing.googleblog.com/2008/08/by-miko-hevery-so-you-join-new-project.html">Singletons are Pathological Liars</a></em>: the method signature lies to you. When a <em>`apiKeyLookup(id)`</em> secretly calls <em>`MantisBase::instance().db()`</em>, its true dependency list is nowhere in its declaration. You only discover it by reading the body or by watching it explode at runtime.</p><p>This is the Dependency Inversion Principle (<em>the &#8220;D&#8221; in Robert C. Martin&#8217;s SOLID</em>) inverted the <em>wrong</em> way: our high-level model and request code depended directly on a concrete global-access mechanism instead of on abstractions handed to them.</p><h3>2. The testing harness had to bend around it</h3><p>This is where the abstraction stopped being free. Because there can only ever be one instance per process, our entire test suite is built around a single global application that must be stood up once and shared by everything. Look at what the test environment has to do:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;cpp&quot;,&quot;nodeId&quot;:&quot;7d21f9a6-01ec-4d47-9ff4-d388b9bbd44f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-cpp">// tests/common/test_environment.h
inline MbTestEnv::MbTestEnv() {
    // ... assemble config ...
    mb::MantisBase::create(args);          // the one and only create()
}

inline void MbTestEnv::SetUp() {
    serverThread = std::thread([this]() {
        server_running.store(true);
        auto res = mb::MantisBase::instance().run();   // reach back for it
    });

    // sleep, poll /health, hope it came up...
}</code></pre></div><p>Every test then reaches through the same <em>global</em> to get at subsystems:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;cpp&quot;,&quot;nodeId&quot;:&quot;b1086961-e4fb-466f-acf2-4b4afcfe1a2e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-cpp">// tests/unit/test_database.cpp
EXPECT_TRUE(mb::MantisBase::instance().db().isConnected());

// tests/unit/test_models.cpp
mb::EntitySchema base{mb::MantisBase::instance(), "test", "base"};</code></pre></div><p>The consequences are exactly what Michael Feathers warns about in <em><strong>Working Effectively with Legacy Code (2004)</strong></em>. He defines a unit test as one that runs fast and in isolation, but singletons destroy isolation by construction. Global state persists between tests, so ordering matters, and you get to write defensive code like this just to survive a <em>`create() &gt; close() &gt; create()`</em> cycle within a single process:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;cpp&quot;,&quot;nodeId&quot;:&quot;c6c5c8b2-b040-4b0f-9b30-a3b1b8086c57&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-cpp">// src/mantisbase.cpp

// Start from a clean slate: on a create() -&gt; close() -&gt; create()
// cycle (e.g. across test runs in one process) the arg vector would
// otherwise accumulate the previous run&#8217;s arguments.

app.m_cmdArgs.clear();
app.m_cmdArgs.emplace_back("mantisbase");</code></pre></div><p>That comment is a remnant record of the pain, a state that should have died with an object instead outlives it, because the object is a <em>`static`</em> that never really goes away. Freeman and Pryce, in <em>Growing Object-Oriented Software, Guided by Tests (2009),</em> argue that hard-to-test code is usually a signal of a design problem, not a testing problem. The singleton was our signal, printed in every <em>`instance()`</em> call the tests had to make.</p><h3>3. We now genuinely need more than one instance per process</h3><p>The convenience argument for a singleton, <em>&#8220;there&#8217;s only ever one&#8221;</em>, turned out to be false as the product grew. We want to run <strong>multiple independent MantisBase instances in a single process</strong>: think isolated tenants, an embedding host driving several databases, or simply a test that spins up two servers and asserts they don&#8217;t leak into each other. We already have the test that describes the goal:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;cpp&quot;,&quot;nodeId&quot;:&quot;04a53cb3-5a36-48e5-a49a-22510f044ed8&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-cpp">// tests/integration/test_integration_multi_instance.cpp

TEST_F(MultiInstanceTest, IndependentEntitiesDontInterfere) {
    // create schema multi_inst_a and multi_inst_b, insert into each,
    // and assert each entity sees exactly its own row.
    EXPECT_EQ(body_a["data"]["items_count"], 1);
    EXPECT_EQ(body_b["data"]["items_count"], 1);
}</code></pre></div><p>You cannot express <em>&#8220;two independent apps that don&#8217;t interfere&#8221;</em> when <em>`instance()`</em> is hardwired to return the one true app and <em>`create()`</em> throws the second time you call it. The singleton isn&#8217;t just inconvenient here; it makes the requirement <em>unrepresentable</em>.</p><h2>What we&#8217;re replacing it with</h2><p>The fix is the boring, correct one that Martin Fowler catalogued back in 2004 in <em><a href="https://martinfowler.com/articles/injection.html">Inversion of Control Containers and the Dependency Injection pattern</a></em> constructor injection. Instead of reaching out to a global, each object is <strong>handed</strong> the app it belongs to, at construction, as its first parameter.</p><p>A locked-in design decision for <em>v0.4.x</em>: the app is the first constructor parameter, so nobody can forget to bind it.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;cpp&quot;,&quot;nodeId&quot;:&quot;08b18ec1-b48f-491e-8933-825f1f27544d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-cpp">// include/mantisbase/core/http.h

class MantisRequest {
    /// Owning application, injected by the Router when the request is
    /// wrapped. Request-path code reaches shared services (db, router,
    /// realtime, config) via app() instead of the global singleton.

    const MantisBase &amp;m_app;

public:
    explicit MantisRequest(const MantisBase&amp; mb, const drogon::HttpRequestPtr&amp; req);
    const MantisBase&amp; mApp() const { return m_app; }
};</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;cpp&quot;,&quot;nodeId&quot;:&quot;06263a09-a96b-4e11-9154-43cba3e08bd2&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-cpp">// include/mantisbase/core/models/entity.h
Entity(const MantisBase &amp;app, const nlohmann::json &amp;schema);
Entity(const MantisBase &amp;app, const std::string &amp;name, const std::string &amp;type);</code></pre></div><p><em>`Router`</em>, <em>`Database`</em>, <em>`Entity`</em>, <em>`EntitySchema`</em>, `<em>SSEMgr`</em>, <em>`WSMgr`</em> each now takes its owning `MantisBase&amp;` up front and stores it. The dependency that used to be invisible is now right there in the signature, allowing the type to tell the truth about what it needs.</p><p>The refactor isn&#8217;t finished. A handful of <em>`instance()`</em> calls in infrastructure code&#8212;the logger, file utilities, date helpers, the SSE manager, and one validator&#8212;are deliberately still standing, development in progress <em>(maybe done by the time you are reading this!)</em>.</p><div><hr></div><p>The lesson from v0.4.x isn&#8217;t that <em><strong>singletons are bad.</strong></em> It&#8217;s that <em><strong>convenience at the composition root has a way of becoming a constraint at the leaves.</strong></em> <em>`MantisBase::instance()`</em> saved us a constructor parameter thousands of times and charged interest on every one. Dependency injection just makes you pay the cost up front, in the open, where the type system can see it, and that turns out to be the cheaper deal.</p><div><hr></div><p><em>This work is part of the ongoing MantisBase v0.4.x line. If you want to follow along, check out the <a href="https://github.com/allankoechke/mantisbase/tree/v0.4.x">v0.4.x branch</a> and watch out for exciting features coming soon!</em></p><p></p>]]></content:encoded></item><item><title><![CDATA[MantisBase v0.3.6 Release]]></title><description><![CDATA[A Cleaner, More Consistent API]]></description><link>https://www.codeart.co.ke/p/mantisbase-v036-release</link><guid isPermaLink="false">https://www.codeart.co.ke/p/mantisbase-v036-release</guid><dc:creator><![CDATA[Allan Koech]]></dc:creator><pubDate>Wed, 01 Jul 2026 16:02:39 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/11c35355-bea5-4270-b806-4c165c4dfccd_1920x1080.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>MantisBase v0.3.6</em> is a housekeeping release, marking the start of systemic cleanup. We cleaned up the REST API so every route follows the same conventions, scoped authentication to the entity it belongs to, and gave admin management its own home. There are a few breaking changes, but migration is a quick find-and-replace in most cases.</p><h2>Why the change?</h2><p>As MantisBase grew, the API surface accumulated small inconsistencies. Auth took the target entity in the request body while everything else was path-based. File serving lived at <code>`/api/files/...`</code> while the rest of the API was versioned under <code>`/api/v1/`</code>. Admin operations were split awkwardly between the <em>auth </em>namespace and the <em>system </em>namespace.</p><p>v0.3.6 makes the whole surface predictable. Everything now lives under <em>`/api/v1/`</em> in a clear set of namespaces:</p><p></p><h2>Authentication is now per-entity</h2><p>Previously you told <em>MantisBase </em>which entity to authenticate against by putting it in the request body:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;d19ad1c1-68f6-4a70-bf8b-500787b493cc&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash"># Old (v0.3.5)
curl -X POST http://localhost:7070/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"entity": "users", "identity": "user@example.com", "password": "pw"}'</code></pre></div><p>Now the entity lives in the URL, which is more <em>RESTful </em>and lets each auth entity have its own clearly addressable endpoints:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;e760237b-5ed4-41c6-a5db-270b7f6ccbdb&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash"># New (v0.3.6)
curl -X POST http://localhost:7070/api/v1/auth/users/login \
  -H "Content-Type: application/json" \
  -d '{"identity": "user@example.com", "password": "pw"}'</code></pre></div><p>The same pattern applies to refresh and logout:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;95d3734c-a570-42a8-8931-3cbed7ab24d6&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">curl -X POST http://localhost:7070/api/v1/auth/users/refresh \
  -H "Authorization: Bearer &lt;token&gt;"

curl -X POST http://localhost:7070/api/v1/auth/users/logout \
  -H "Authorization: Bearer &lt;token&gt;"</code></pre></div><p>The <em>`&lt;entity&gt;`</em> must be a registered <em>`auth`</em>-type entity that isn&#8217;t a system table and has the API enabled &#8212; otherwise you&#8217;ll get a clean <em>`404 Route Not Found`</em>.</p><p>One more thing: the login response is now wrapped in MantisBase&#8217;s standard envelope, so it matches every other endpoint:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;json&quot;,&quot;nodeId&quot;:&quot;7873cb27-bf8f-475f-9058-d54b0b4d64ac&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-json">{
  "status": 200,
  "data": {
    "token": "eyJhbGci...",
    "user&#8221;: { "id": "123456"&#8221;, "email": "user@example.com", "name": "John Doe" }
  },
  "error": ""
}</code></pre></div><h2>Admins get their own namespace</h2><p>Admin authentication and account management now live entirely under <em>`/api/v1/sys/admins/`</em>, and admin CRUD is fully supported:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;7d8f3259-71a8-4064-a744-c9bd244c1040&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash"># Create the very first admin (only works when none exist yet)
curl -X POST http://localhost:7070/api/v1/sys/admins/setup \
  -H "Content-Type: application/json" \
  -d '{"email": "admin@example.com", "password": "adminpassword"}'

# Admin login
curl -X POST http://localhost:7070/api/v1/sys/admins/login \
  -H "Content-Type: application/json" \
  -d '{"identity": "admin@example.com", "password": "adminpassword"}'

# Manage admin accounts (requires admin auth)
curl http://localhost:7070/api/v1/sys/admins                   # list
curl http://localhost:7070/api/v1/sys/admins/:id               # get one
curl -X POST   http://localhost:7070/api/v1/sys/admins ...     # create
curl -X PATCH  http://localhost:7070/api/v1/sys/admins/:id ..  # update
curl -X DELETE http://localhost:7070/api/v1/sys/admins/:id     # delete</code></pre></div><p>The old <em>`POST /api/v1/auth/setup/admin`</em> endpoint is gone &#8212; use <em>`/api/v1/sys/admins/setup`</em> instead.</p><h2>Files and settings</h2><p>File serving is now versioned, matching the rest of the API:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;f0249b78-8e06-4580-b6f1-03343b6b8eae&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash"># Old:  http://localhost:7070/api/files/posts/photo.jpg
# New:  http://localhost:7070/api/v1/files/posts/photo.jpg

curl http://localhost:7070/api/v1/files/posts/photo.jpg</code></pre></div><p>And application settings are exposed under the system namespace:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;c8f1ee73-461d-4bb0-80b7-be689ceab874&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">curl http://localhost:7070/api/v1/sys/settings/config   # read
curl -X PATCH http://localhost:7070/api/v1/sys/settings/config \
  -H "Content-Type: application/json" -d '{ ... }'             # update</code></pre></div><h2>Under the hood: lighter on memory</h2><p>Beyond the visible API changes, we refactored how routes are registered. Instead of building a dedicated set of CRUD handlers for every single entity, MantisBase now uses centralized, generic handlers that resolve the target entity from the request path. Schema route handlers were similarly decoupled from the schema object. The result is the same behavior with a smaller memory footprint &#8212; <em>especially noticeable on deployments with many entities.</em></p><p>We also refreshed the bundled dependencies <em>(WolfSSL, nlohmann/json, jwt-cpp, spdlog, and the mb-admin dashboard)</em> and fixed a build-target clash around `z<code>td`.</code></p><h2>Something is broken, oh no!</h2><p>As a result of the API changes, we ended up breaking the admin dashboard. Currently, v0.3.6 <em>admin dashboard is unusable</em> and requires reworking to bring it up to bar. That&#8217;s already being worked on and we will push an update once resolved.</p><h2>Upgrading from v0.3.5</h2><p>Most apps only need three mechanical updates:</p><ol><li><p><strong>Auth calls</strong>: move the entity from the body into the path <br><code>(`/api/v1/auth/login` + `"entity": "users"` &#8594; `/api/v1/auth/users/login`).</code></p></li><li><p><strong>Admin calls:</strong> repoint admin auth/setup to <em>`/api/v1/sys/admins/...`</em>.</p></li><li><p><strong>File URLs:</strong> add the <em>1</em> segment (`/api/files/...` &#8594; `/api/v1/files/...`).</p></li></ol><p>If you parse the login response, unwrap it from the new <em>`{ status, data, error }`</em> envelope.</p><div><hr></div><p>That&#8217;s it &#8212; grab v0.3.6 from the <a href="https://github.com/allankoechke/mantisbase/releases/tag/v0.3.6">releases page</a>, and check the updated <a href="https://github.com/allankoechke/mantisbase/blob/master/doc/02.auth.md">Authentication API</a> and <a href="https://github.com/allankoechke/mantisbase/blob/master/doc/02.api.md">API Reference</a> docs for the full details.</p>]]></content:encoded></item><item><title><![CDATA[To Port or Not to Port: C++ to Rust, and the Trap of the 90% Demo]]></title><description><![CDATA[Ownership was never in the source code. That's the whole migration &#8212; and it's exactly the part the demo skips.]]></description><link>https://www.codeart.co.ke/p/to-port-or-not-to-port-c-to-rust</link><guid isPermaLink="false">https://www.codeart.co.ke/p/to-port-or-not-to-port-c-to-rust</guid><dc:creator><![CDATA[Allan Koech]]></dc:creator><pubDate>Sun, 28 Jun 2026 21:05:04 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/f7360ec9-5391-4901-b2e0-d0626ac198ce_1672x941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><span>There is a demo making the rounds in every engineering org right now. You point an LLM at a gnarly C++ file, and ninety seconds later you have Rust that compiles. It looks like the migration problem just got solved. Leadership sees it, a quarter gets funded, and a team starts pointing the model at the codebase file by file.</span></p><p><span>Then, somewhere around the third month, the project stalls. Not dramatically &#8212; there&#8217;s no postmortem, no cancellation email. It just quietly stops moving. The remaining work never gets easier, the Rust never gets </span><em><span>good</span></em><span>, and eventually the branch goes stale.</span></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.codeart.co.ke/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en-gb&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Codeart &#8212; by Allan Koech! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p><span>This isn&#8217;t a failure of nerve or tooling. It&#8217;s a structural property of what porting C++ to Rust actually </span><em><span>is</span></em><span> &#8212; and it&#8217;s worth understanding before you spend a quarter learning it the expensive way.</span></p><h2><strong><span>The seduction is real, and so is the wall</span></strong></h2><p><span>The reason the demo is so convincing is that the first 90% genuinely is easy now. Translating C++ control flow, arithmetic, and data structures into syntactically valid Rust is exactly the kind of pattern-matching LLMs are good at. The compiler even helps: if it builds, a lot of bugs are already gone.</span></p><p><span>The problem is what&#8217;s hiding in the last 10%, and it&#8217;s the same thing whether a human or a model is doing the work.</span></p><p><span>C++ does not encode ownership. When you write </span><code>T* p</code><span>, the language does not record whether </span><code>p</code><span> owns that memory, borrows it, shares it, or aliases something else that&#8217;s about to be freed. Those facts live in the programmer&#8217;s head, in comments, in naming conventions, and in tribal knowledge about </span><em><span>&#8220;who frees what.&#8221;</span></em><span> </span><strong><span>Rust&#8217;s entire value proposition is that these facts must be written down and checked.</span></strong><span> So a C++&#8594;Rust port is not a translation. It&#8217;s a </span><em><span>reconstruction of invariants that were never explicitly recorded.</span></em></p><div class="callout-block" data-callout="true"><p>So a C++&#8594;Rust port is not a translation. It&#8217;s a <em>reconstruction of invariants that were never explicitly recorded.</em></p></div><p><span>That&#8217;s why automatic transpilers are so instructive. Tools like </span><a href="https://arxiv.org/pdf/2511.20617"><span>c2rust</span></a><span> produce Rust that is </span><em><span>&#8220;almost always correct&#8221;</span></em><span> &#8212; and almost always </span><em><span>unsafe</span></em><span> and unidiomatic. They faithfully preserve C&#8217;s pointer semantics by wrapping everything in </span><code>unsafe</code><span>, raw pointers, and </span><code>libc</code><span> calls. The output compiles. It is also, in the words of the research, </span><em><span>&#8220;very different from the kind of Rust code usually written by humans, making it hard to read and maintain&#8221;</span></em><span> &#8212; and it sails straight past the linters that are supposed to catch the problems. You get Rust syntax with C semantics: all of the migration cost, none of the safety payoff.</span></p><p><span>LLMs improve on this &#8212; they can sometimes infer ownership and produce idiomatic code &#8212; but the failure mode is sneakier. As DARPA&#8217;s program manager for </span><a href="https://www.darpa.mil/research/programs/translating-all-c-to-rust"><span>TRACTOR </span></a><span>(the agency&#8217;s program to automate exactly this translation) put it: ask any model to translate C to safe idiomatic Rust and </span><em><span>&#8220;something comes out, and it&#8217;s often very good, but not always.&#8221;</span></em><span> When it&#8217;s not, the model tends to reach for </span><code>unsafe</code><span> to make the borrow checker stop complaining &#8212; which is precisely how you end up with code that </span><em><span>looks</span></em><span> migrated but carries the same latent bugs it always had, now harder to spot because everyone assumes </span><em><span>&#8220;it&#8217;s in Rust, so it&#8217;s safe.&#8221;</span></em></p><p><span>This is the mechanism behind the stall. The borrow checker doesn&#8217;t get tired and wave the last 10% through. Every place where the original C++ had a fuzzy ownership story becomes a hard stop that requires a human to </span><em><span>decide</span></em><span> what the ownership story should be &#8212; and frequently to redesign the data structures to express it. That&#8217;s design work, not typing work. LLMs removed the typing bottleneck, which was never the bottleneck.</span></p><h2><strong><span>The evidence cuts both ways &#8212; but cleanly</span></strong></h2><p><span>The honest read of the industry data is not </span><em><span>&#8220;Rust good&#8221;</span></em><span> or </span><em><span>&#8220;rewrites bad.&#8221;</span></em><span> It&#8217;s that </span><em><span>the shape of the project predicts the outcome</span></em><span> far better than the language choice does.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!se6i!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcbb153e-efa6-409f-ade6-2a8a504fb908_2536x654.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!se6i!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcbb153e-efa6-409f-ade6-2a8a504fb908_2536x654.png 424w, https://substackcdn.com/image/fetch/$s_!se6i!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcbb153e-efa6-409f-ade6-2a8a504fb908_2536x654.png 848w, https://substackcdn.com/image/fetch/$s_!se6i!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcbb153e-efa6-409f-ade6-2a8a504fb908_2536x654.png 1272w, https://substackcdn.com/image/fetch/$s_!se6i!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcbb153e-efa6-409f-ade6-2a8a504fb908_2536x654.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!se6i!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcbb153e-efa6-409f-ade6-2a8a504fb908_2536x654.png" width="1456" height="375" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dcbb153e-efa6-409f-ade6-2a8a504fb908_2536x654.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:375,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:158874,&quot;alt&quot;:&quot;porting strategy across the industry&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.codeart.co.ke/i/204011442?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcbb153e-efa6-409f-ade6-2a8a504fb908_2536x654.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="porting strategy across the industry" title="porting strategy across the industry" srcset="https://substackcdn.com/image/fetch/$s_!se6i!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcbb153e-efa6-409f-ade6-2a8a504fb908_2536x654.png 424w, https://substackcdn.com/image/fetch/$s_!se6i!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcbb153e-efa6-409f-ade6-2a8a504fb908_2536x654.png 848w, https://substackcdn.com/image/fetch/$s_!se6i!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcbb153e-efa6-409f-ade6-2a8a504fb908_2536x654.png 1272w, https://substackcdn.com/image/fetch/$s_!se6i!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcbb153e-efa6-409f-ade6-2a8a504fb908_2536x654.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>Look at what the wins have in common. Android never </span><em><span>ported</span></em><span> anything &#8212; Google&#8217;s entire strategy is that new code is Rust and old C++ is left in place; the safety gains come from the fact that </span><em><a href="https://security.googleblog.com/2025/11/rust-in-android-move-fast-fix-things.html"><span>&#8220;most bugs live in new code&#8221;</span></a></em><span>, so you don&#8217;t need to touch the old stuff to bend the curve. Pingora wasn&#8217;t a translation at all; it was a </span><em><span>clean-room reimplementation</span></em><span> against a known spec, where the team got to design the ownership model from scratch instead of recovering it from someone else&#8217;s pointers. Mozilla picked off self-contained, parallelism-hungry components one at a time and integrated each before starting the next.</span></p><p><span>Now look at curl. Daniel Stenberg&#8217;s team spent </span><strong><span>roughly five years</span></strong><span> (2020&#8211;2025) trying to let curl use the Rust </span><a href="https://daniel.haxx.se/blog/2024/12/21/dropping-hyper/"><span>hyper </span></a><span>library as an HTTP backend through an FFI glue layer &#8212; and ultimately ripped it out. The reasons are a near-perfect catalog of why these projects die:</span></p><ul><li><p><strong><span>The last 5% was the whole problem.</span></strong><span> </span><em><span>&#8220;The final few percent would turn out to be friction enough to now eventually make us admit defeat.&#8221;</span></em></p></li><li><p><strong><span>Nobody to do the hard part.</span></strong><span> It needed someone fluent in </span><em><span>both</span></em><span> C and Rust </span><em><span>and</span></em><span> the HTTP protocols to drive the integration across the FFI boundary &#8212; and almost no one wanted to.</span></p></li><li><p><strong><span>The FFI seam leaked.</span></strong><span> They had to </span><em><span>remove</span></em><span> HTTP/2 support because they&#8217;d misunderstood how the API integrated across the language boundary.</span></p></li><li><p><strong><span>No demand pulling it forward.</span></strong><span> curl users didn&#8217;t care that a backend was in Rust; Rust users didn&#8217;t want to maintain glue for a C project.</span></p></li></ul><p><span>Stenberg&#8217;s conclusion is the bluntest data point in the whole debate: </span><a href="https://thenewstack.io/curls-daniel-stenberg-on-securing-180000-lines-of-c-code/"><span>&#8220;We&#8217;re not going to rewrite curl in Rust. In any language &#8212; we&#8217;re not going to rewrite it at all.&#8221;</span></a><span> This from someone who openly acknowledges that </span><a href="https://daniel.haxx.se/blog/2021/03/09/half-of-curls-vulnerabilities-are-c-mistakes/"><span>roughly half of curl&#8217;s historical vulnerabilities are C memory mistakes</span></a><span>. He knows the safety case cold. He&#8217;s declining anyway, because the migration cost is real and the existing C is hardened by decades of fuzzing and CVE wrangling.</span></p><h2><strong><span>The hidden tax: comprehension debt</span></strong></h2><p><span>There&#8217;s a second-order cost that the demo never shows you, and it&#8217;s worse with LLM-driven ports than with hand ports.</span></p><p><span>When a model emits 4,000 lines of plausible Rust, </span><em><span>no one on your team owns the mental model of that code.</span></em><span> The 2025 research on LLM-generated code calls this </span><a href="https://codemanship.wordpress.com/2025/09/30/comprehension-debt-the-ticking-time-bomb-of-llm-generated-code/"><span>comprehension debt</span></a><span> and </span><em><span>&#8220;ownership debt&#8221;</span></em><span>: </span><em><span>&#8220;it compiles&#8221;</span></em><span> is not the same as </span><em><span>&#8220;someone understands why it&#8217;s correct and can change it safely.&#8221;</span></em><span> With a hand-written port, the engineer who fought the borrow checker comes out the other side </span><em><span>understanding the ownership model</span></em><span> &#8212; that understanding is half the point. With a generated port, you&#8217;ve skipped the understanding and kept only the artifact. The first time it needs a non-trivial change, you pay the comprehension cost you deferred, except now under deadline and without the person who&#8217;d have built the model.</span></p><p><span>This is why &#8220;the LLM did 90% in a week&#8221; is a misleading metric. The week of typing was never the expensive part. The expensive part is owning a correct mental model of a safety-critical system &#8212; and that&#8217;s exactly what gets skipped when the code arrives pre-written.</span></p><h2><strong><span>A decision framework that actually predicts outcomes</span></strong></h2><p><span>Stop asking </span><em><span>&#8220;should we port to Rust?&#8221;</span></em><span> It&#8217;s the wrong altitude. Ask these instead:</span></p><h3><strong><span>1. Is this new code or existing code?</span></strong></h3><p><span>If new, strongly favor Rust, and stop reading. The Android data is overwhelming: the cheapest safety win is to stop </span><em><span>adding</span></em><span> to the C++ pile. No migration risk, all the upside.</span></p><h3><strong><span>2. If existing &#8212; is the component self-contained, or is it threaded through the codebase?</span></strong></h3><p><span>A module with a narrow, well-defined interface (a parser, a codec, a proxy data path) is a Stylo/Pingora candidate. A module whose pointers reach into everything will trap you in </span><strong><span>FFI hell</span></strong><span>: every call across the C++/Rust seam is an </span><code>unsafe </code><span>boundary where Rust&#8217;s guarantees evaporate and new bugs breed. The curl/hyper failure was fundamentally an FFI-boundary failure.</span></p><h3><strong><span>3. Do you have a spec, or only the code?</span></strong></h3><p><span>If you can reimplement against a specification or a strong test suite (Pingora had the HTTP spec; you can have a differential test harness), favor a </span><em><span>clean-room rewrite</span></em><span> over a line-by-line port. You&#8217;ll design the ownership model instead of reverse-engineering it, and you can run old and new side by side to prove equivalence.</span></p><h3><strong><span>4. Will someone own the result?</span></strong></h3><p><span>If the answer is </span><em><span>&#8220;the LLM wrote it and we&#8217;ll figure it out later,&#8221;</span></em><span> you don&#8217;t have a migration plan, you have a future incident. Budget the human comprehension cost up front or don&#8217;t start.</span></p><h3><strong><span>5. Is the existing C++ actually your risk?</span></strong></h3><p><span>Battle-hardened, heavily-fuzzed C++ that hasn&#8217;t thrown a CVE in years is not where your memory-safety risk concentrates. New and frequently-changed code is. Spend the migration budget where the bugs actually are.</span></p><h2><strong><span>The verdict</span></strong></h2><p><strong><span>Use the LLM</span></strong><span>. It genuinely removed a real cost &#8212; the mechanical translation that used to make these projects unaffordable to even start. But understand precisely what it removed and what it didn&#8217;t. It removed the </span><em><span>typing</span></em><span>. It did not remove the </span><em><span>thinking</span></em><span>: recovering ownership invariants that C++ never recorded, designing data structures that satisfy the borrow checker, and proving the result is equivalent to the original.</span></p><p><strong><span>That thinking is the project</span></strong><span>. It was always the project. The reason ports stall at 90% is that the demo completes the 90% that was never hard, and then hands you the 10% that was the entire point &#8212; now disguised as &#8220;almost done.&#8221;</span></p><p><strong><span>Port new code by default</span></strong><span>. Port self-contained components against a spec. Leave hardened, stable C++ alone. And treat any plan whose timeline is built on </span><em><span>&#8220;the model already wrote most of it&#8221;</span></em><span> as a plan that hasn&#8217;t started yet.</span></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.codeart.co.ke/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en-gb&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Codeart &#8212; by Allan Koech! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[MantisBase is Moving to Rust]]></title><description><![CDATA[There is no shortage of "rewrite it in Rust" stories on the internet.]]></description><link>https://www.codeart.co.ke/p/mantisbase-is-moving-to-rust</link><guid isPermaLink="false">https://www.codeart.co.ke/p/mantisbase-is-moving-to-rust</guid><dc:creator><![CDATA[Allan K. Koech]]></dc:creator><pubDate>Mon, 18 May 2026 19:40:05 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/aaa6e839-a98a-4cd7-a7f7-9dd0bdbf4142_690x350.svg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IXjp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06fc8da7-12f5-498d-8934-d20d96346e38_690x350.svg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IXjp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06fc8da7-12f5-498d-8934-d20d96346e38_690x350.svg 424w, https://substackcdn.com/image/fetch/$s_!IXjp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06fc8da7-12f5-498d-8934-d20d96346e38_690x350.svg 848w, https://substackcdn.com/image/fetch/$s_!IXjp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06fc8da7-12f5-498d-8934-d20d96346e38_690x350.svg 1272w, https://substackcdn.com/image/fetch/$s_!IXjp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06fc8da7-12f5-498d-8934-d20d96346e38_690x350.svg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IXjp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06fc8da7-12f5-498d-8934-d20d96346e38_690x350.svg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/06fc8da7-12f5-498d-8934-d20d96346e38_690x350.svg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!IXjp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06fc8da7-12f5-498d-8934-d20d96346e38_690x350.svg 424w, https://substackcdn.com/image/fetch/$s_!IXjp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06fc8da7-12f5-498d-8934-d20d96346e38_690x350.svg 848w, https://substackcdn.com/image/fetch/$s_!IXjp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06fc8da7-12f5-498d-8934-d20d96346e38_690x350.svg 1272w, https://substackcdn.com/image/fetch/$s_!IXjp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06fc8da7-12f5-498d-8934-d20d96346e38_690x350.svg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>There is no shortage of <em>"rewrite it in Rust"</em> stories on the internet. Some are driven by hype, others by genuine engineering needs. This is not the former. What follows is a straightforward account of where MantisBase is headed, why, and what it means for you.</p><div><hr></div><h2>A Growing Ecosystem Worth Taking Seriously</h2><p>Before getting into the decision itself, it is worth acknowledging the state of Rust as a language and ecosystem. The <em>Rustacean</em> community has grown into one of the most active and technically rigorous developer communities in open source. The <a href="https://crates.io">crates.io</a> registry now hosts hundreds of thousands of packages, covering everything from async runtimes and HTTP frameworks to database drivers, JWT handling, and mailer integrations &#8212; all with first-class tooling and consistently high quality. Rust has also topped Stack Overflow's "most admired language" survey for several consecutive years, which speaks less to popularity and more to developer satisfaction once they have committed to it.</p><p>We have been watching this ecosystem closely. It has matured to a point where building a production-grade, single-binary backend on top of it is not only feasible &#8212; it is arguably the most sensible path forward for a project with MantisBase's goals.</p><div><hr></div><h2>What Is Already Happening</h2><p>Work on v0.4 has begun, and it is a full rewrite in Rust. The goal is to reach feature parity with the current C++ release at minimum, and to go further where the new foundation makes it practical to do so. If you want to track progress, the <a href="https://github.com/allankoechke/mantisbase">master branch on GitHub</a> already reflects the active rewrite &#8212; the repository is already building and running in Rust, with core functionality like entity schemas, REST routing, access rules, JWT authentication, admin dashboard serving, and webhook support already in place.</p><p>This is not a distant roadmap item. It is ongoing work, and it is further along than most announcements of this kind tend to let on.</p><div><hr></div><h2>Why the C++ Foundation Was Holding Us Back</h2><p>MantisBase started as a C++ project for good reasons: raw speed, embeddability, and a single-binary deployment model with no external runtime dependency. Those goals have not changed. What changed is our assessment of the cost of continuing to pursue them in C++.</p><p>The C++ codebase accumulated a set of challenges over time &#8212; not insurmountable, but expensive to address correctly. Memory management patterns that required careful audit, integration complexity when pulling in third-party libraries, and a build system that made certain kinds of cross-platform testing harder than it needed to be. Fixing these properly would have demanded a significant investment of time &#8212; time that should have gone toward improving the product.</p><p>The practical result was that several things we wanted to deliver kept getting pushed back. The documentation site needed modernisation. The admin dashboard needed a cleaner, more capable UI. The REST API had features we wanted to expand. Most notably, out-of-the-box SDK support for JavaScript and Python &#8212; something users have asked about consistently &#8212; was hard to prioritise while we were managing the overhead of the C++ foundation.</p><p>We reached a point where the honest assessment was: the language itself had become a constraint on the project's growth, not just a technical detail.</p><div><hr></div><h2>Why Rust, and Not Zig or Go</h2><p>Once the decision to move away from C++ was made, we evaluated the realistic alternatives that could preserve what made MantisBase useful in the first place: a single compiled binary, genuine performance, and low memory overhead. Rust, Zig, and Go were the serious contenders.</p><p>Go produces clean binaries and has an excellent standard library, but its garbage collector introduces latency characteristics that are a poor fit for a backend meant to be embedded inside other applications. Zig is technically compelling and offers tight control over memory, but its ecosystem and tooling are still maturing &#8212; pulling in third-party integrations would mean building more from scratch than we wanted.</p><p>Rust offered the right combination: compile-time memory safety without a garbage collector, a mature ecosystem through crates.io, and tooling (cargo, rustfmt, clippy, cargo test) that genuinely reduces friction rather than adding to it. The memory safety story was particularly relevant. Part of why the C++ codebase was difficult to maintain was precisely the category of issues that Rust eliminates at the compiler level &#8212; use-after-free, data races, invalid pointer dereferences. We could have audited and hardened the C++ code, but choosing Rust means we do not have to keep having that conversation on every future contribution.</p><p>Beyond safety, the crate ecosystem makes it straightforward to integrate capabilities that previously required significant custom work: mailer support via <a href="https://github.com/lettre/lettre">lettre</a>, async HTTP with <a href="https://github.com/tokio-rs/axum">axum</a>, database access via <a href="https://github.com/launchbargain/sqlx">sqlx</a>, and more. These are not afterthoughts &#8212; they are well-maintained, widely used libraries that slot in cleanly.</p><p>First-class test support via <code>cargo test</code> is also worth naming directly. Writing and running tests in C++ projects of this kind involves considerably more ceremony. In Rust, testing is a natural part of the workflow, which matters when you are trying to build confidence in a rewrite incrementally.</p><div><hr></div><h2>Language SDK Support Becomes Realistic</h2><p>One of the things we wanted to offer from early on was MantisBase SDKs for JavaScript and Python &#8212; not just REST clients, but deeper integration. In the C++ world, producing idiomatic bindings for both languages meant navigating complex FFI boundaries, separate build pipelines, and significant maintenance surface.</p><p>From Rust, this is substantially more tractable. The <a href="https://pyo3.rs">PyO3</a> project provides a mature path for exposing Rust libraries to Python, and <a href="https://neon-bindings.com">Neon</a> (or WASM targets) covers the Node.js side. We are not committing to a specific release timeline for these yet, but the rewrite removes the structural obstacle that made them impractical before.</p><div><hr></div><h2>What Happens to C++ Support</h2><p>The original C++ releases are not being abandoned. Binaries up through v0.3.5 remain available on the <a href="https://github.com/allankoechke/mantisbase/releases">GitHub releases page</a>, and that code is not going anywhere.</p><p>Looking further ahead, we are interested in the model that projects like <a href="https://slint.dev">Slint</a> have demonstrated. Slint is a UI toolkit written in Rust that exposes a fully supported C++ API &#8212; developers working in C++ interact with it as though it were a native C++ library, with no Rust toolchain required on their end. Several other projects in the embedded and systems space have taken similar approaches. We are examining what that looks like for MantisBase's embedding use case, and C++ API support remains part of the long-term plan.</p><div><hr></div><h2>What to Expect Next</h2><p>The v0.4 work is progressing in the open. You can follow along, open issues, or contribute via the <a href="https://github.com/allankoechke/mantisbase">GitHub repository</a>. The current Rust codebase is already functional enough to run and experiment with.</p><p>A more detailed devlog covering the architecture of the Rust port &#8212; including database abstraction, the new schema engine, and the HTTP layer &#8212; is coming soon. For updates, subscribe below or follow <a href="https://x.com/allankoechke">@allankoechke</a> on X.</p><div><hr></div><p><em>MantisBase is an open-source, single-binary Backend-as-a-Service. Visit <a href="https://mantisbase.com">mantisbase.com</a> for documentation and demos.</em></p>]]></content:encoded></item><item><title><![CDATA[What’s new in 3ds Max 2027?]]></title><description><![CDATA[Originally posted here:]]></description><link>https://www.codeart.co.ke/p/whats-new-in-3ds-max-2027</link><guid isPermaLink="false">https://www.codeart.co.ke/p/whats-new-in-3ds-max-2027</guid><dc:creator><![CDATA[Allan K. Koech]]></dc:creator><pubDate>Mon, 13 Apr 2026 07:25:40 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/4f517473-944d-4277-9db7-47c37427c948_1412x795.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gEzp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d49560-3af5-4f88-8d9a-7638a5343edc_1412x795.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gEzp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d49560-3af5-4f88-8d9a-7638a5343edc_1412x795.png 424w, https://substackcdn.com/image/fetch/$s_!gEzp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d49560-3af5-4f88-8d9a-7638a5343edc_1412x795.png 848w, https://substackcdn.com/image/fetch/$s_!gEzp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d49560-3af5-4f88-8d9a-7638a5343edc_1412x795.png 1272w, https://substackcdn.com/image/fetch/$s_!gEzp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d49560-3af5-4f88-8d9a-7638a5343edc_1412x795.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gEzp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d49560-3af5-4f88-8d9a-7638a5343edc_1412x795.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b6d49560-3af5-4f88-8d9a-7638a5343edc_1412x795.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!gEzp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d49560-3af5-4f88-8d9a-7638a5343edc_1412x795.png 424w, https://substackcdn.com/image/fetch/$s_!gEzp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d49560-3af5-4f88-8d9a-7638a5343edc_1412x795.png 848w, https://substackcdn.com/image/fetch/$s_!gEzp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d49560-3af5-4f88-8d9a-7638a5343edc_1412x795.png 1272w, https://substackcdn.com/image/fetch/$s_!gEzp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d49560-3af5-4f88-8d9a-7638a5343edc_1412x795.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><div><hr></div><p>Originally posted here:</p><blockquote><h4><a href="https://blog.autodesk.io/whats-new-in-3ds-max-2027/">Explore New Features in 3ds Max 2027 Release Updates</a></h4></blockquote><div><hr></div><p>A few weeks ago, 3ds Max 2027 was officially released, introducing new features and capabilities for both artists and developers. With this release, 3ds Max saw many upgrades including <em>Arnold to 5.9.0, USD to 0.14.0, Substance to 3.0.6</em>, performance improvements across tools, modifiers, and render workflows, and much more. Refer to the <a href="https://help.autodesk.com/view/3DSMAX/2027/ENU/?guid=GUID-7CC2F041-F797-4DB9-B2F9-326AAB994F37">product release page</a> for a detailed feature breakdown for 3ds Max 2027.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://blog.autodesk.io/wp-content/uploads/2026/04/GUID-05B7B772-F025-42E4-A43B-238EE14FDF74.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://blog.autodesk.io/wp-content/uploads/2026/04/GUID-05B7B772-F025-42E4-A43B-238EE14FDF74.gif 424w, https://blog.autodesk.io/wp-content/uploads/2026/04/GUID-05B7B772-F025-42E4-A43B-238EE14FDF74.gif 848w, https://blog.autodesk.io/wp-content/uploads/2026/04/GUID-05B7B772-F025-42E4-A43B-238EE14FDF74.gif 1272w, https://blog.autodesk.io/wp-content/uploads/2026/04/GUID-05B7B772-F025-42E4-A43B-238EE14FDF74.gif 1456w" sizes="100vw"><img src="https://blog.autodesk.io/wp-content/uploads/2026/04/GUID-05B7B772-F025-42E4-A43B-238EE14FDF74.gif" width="800" height="450" data-attrs="{&quot;src&quot;:&quot;https://blog.autodesk.io/wp-content/uploads/2026/04/GUID-05B7B772-F025-42E4-A43B-238EE14FDF74.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:450,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Smart Bevel for post-Boolean intersections&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Smart Bevel for post-Boolean intersections" title="Smart Bevel for post-Boolean intersections" srcset="https://blog.autodesk.io/wp-content/uploads/2026/04/GUID-05B7B772-F025-42E4-A43B-238EE14FDF74.gif 424w, https://blog.autodesk.io/wp-content/uploads/2026/04/GUID-05B7B772-F025-42E4-A43B-238EE14FDF74.gif 848w, https://blog.autodesk.io/wp-content/uploads/2026/04/GUID-05B7B772-F025-42E4-A43B-238EE14FDF74.gif 1272w, https://blog.autodesk.io/wp-content/uploads/2026/04/GUID-05B7B772-F025-42E4-A43B-238EE14FDF74.gif 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Smart Bevel for post-Boolean intersections released in 3ds Max 2027</figcaption></figure></div><h2>New Features, Improvements, and Updated Workflows</h2><p>3ds Max 2027 ships with several notable updates across its plugin ecosystem:&nbsp;</p><ul><li><p>Arnold for 3ds Max updated to <em>5.9.0</em> (Arnold <em>7.5.0.0</em>)</p></li><li><p>USD for 3ds Max updated to <em>0.14.0</em></p></li><li><p>Substance updated to 3.0.6</p></li><li><p>Autodesk Assistant (Technical Preview): An AI-powered tool that helps you quickly find information about features and workflows directly within the product &#8212; a welcome addition for developers and artists alike looking to streamline their pipeline research. Read more <a href="https://help.autodesk.com/view/3DSMAX/2027/ENU/?guid=GUID-92EF3530-9E04-46DF-B3F2-2BA150C6CEDF">here</a>.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DtIv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F281cee7a-03a4-4099-ae60-7cffff8e7412_1280x720.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DtIv!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F281cee7a-03a4-4099-ae60-7cffff8e7412_1280x720.gif 424w, https://substackcdn.com/image/fetch/$s_!DtIv!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F281cee7a-03a4-4099-ae60-7cffff8e7412_1280x720.gif 848w, https://substackcdn.com/image/fetch/$s_!DtIv!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F281cee7a-03a4-4099-ae60-7cffff8e7412_1280x720.gif 1272w, https://substackcdn.com/image/fetch/$s_!DtIv!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F281cee7a-03a4-4099-ae60-7cffff8e7412_1280x720.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DtIv!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F281cee7a-03a4-4099-ae60-7cffff8e7412_1280x720.gif" width="1280" height="720" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/281cee7a-03a4-4099-ae60-7cffff8e7412_1280x720.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:720,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A digital interface displaying guidelines for camera types in 3ds Max 2026, set in a kitchen environment with chairs and a fridge in the background.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A digital interface displaying guidelines for camera types in 3ds Max 2026, set in a kitchen environment with chairs and a fridge in the background." title="A digital interface displaying guidelines for camera types in 3ds Max 2026, set in a kitchen environment with chairs and a fridge in the background." srcset="https://substackcdn.com/image/fetch/$s_!DtIv!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F281cee7a-03a4-4099-ae60-7cffff8e7412_1280x720.gif 424w, https://substackcdn.com/image/fetch/$s_!DtIv!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F281cee7a-03a4-4099-ae60-7cffff8e7412_1280x720.gif 848w, https://substackcdn.com/image/fetch/$s_!DtIv!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F281cee7a-03a4-4099-ae60-7cffff8e7412_1280x720.gif 1272w, https://substackcdn.com/image/fetch/$s_!DtIv!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F281cee7a-03a4-4099-ae60-7cffff8e7412_1280x720.gif 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>SDK Changes</h2><p>Now let's get into what matters most for plugin developers.</p><ul><li><p><strong>.NET 10.0 Support</strong>: 3ds Max 2027 moves plugin development to .NET 10. Note that .NET 8, introduced in 3ds Max 2026, has reached an end of life and is no longer supported. If you are still on an older version, refer to our porting guide from .NET Framework to .NET for plugins for step-by-step migration guidance.</p></li><li><p><strong>DirectX 9 Removal</strong>: Support for DirectX 9 has been fully removed. This affects several headers that have been dropped from the public SDK &#8212; notably id3d9graphicswindow.h, idx9pixelshader.h, and idx9vertexshader.h &#8212; along with related interfaces in <em>ihardwarematerial.h, ihardwarerender.h, </em>and <em>RTMax.h</em>. Developers relying on these should migrate to the DirectX 11 equivalents now available in the SDK.</p></li><li><p><strong>Updated Core Dependencies</strong>: 3ds Max 2027 development specifies Visual Studio 2022 (v17.14.x), C++20, Qt 6.8.3. Review your plugin dependencies against these versions before building. You could also use the newer Visual Studio 2026, when you set the toolset to be v143.</p></li><li><p><strong>API Changes</strong>: There are targeted API updates across several headers &#8212; including new methods in <em>custcont.h</em>,<em> ioapi.h</em>, and<em> IValidateNodeCallback.h</em>, as well as deprecations in <em>object.h</em>, <em>gfx.h</em>, and others.</p></li><li><p><strong>Updated Samples and Documentation</strong>: Samples and documentation have been updated to reflect all API changes in this release.</p></li></ul>]]></content:encoded></item><item><title><![CDATA[MantisBase v0.3.4 — Admin Upgrades, Bug Fixes & Docker Updates]]></title><description><![CDATA[We're shipping v0.3.4 today with a handful of quality-of-life improvements, a cleaner configuration story, and a few important bug fixes.]]></description><link>https://www.codeart.co.ke/p/mantisbase-v0-3-4-release</link><guid isPermaLink="false">https://www.codeart.co.ke/p/mantisbase-v0-3-4-release</guid><dc:creator><![CDATA[Allan K. Koech]]></dc:creator><pubDate>Thu, 26 Feb 2026 23:24:38 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/d102db5a-65ce-48ca-9db4-94fbd04d1d0b_1920x1080.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!K04a!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcca9d511-1d33-49f6-9a3c-f970fb442a10_1920x1080.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!K04a!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcca9d511-1d33-49f6-9a3c-f970fb442a10_1920x1080.jpeg 424w, https://substackcdn.com/image/fetch/$s_!K04a!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcca9d511-1d33-49f6-9a3c-f970fb442a10_1920x1080.jpeg 848w, https://substackcdn.com/image/fetch/$s_!K04a!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcca9d511-1d33-49f6-9a3c-f970fb442a10_1920x1080.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!K04a!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcca9d511-1d33-49f6-9a3c-f970fb442a10_1920x1080.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!K04a!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcca9d511-1d33-49f6-9a3c-f970fb442a10_1920x1080.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cca9d511-1d33-49f6-9a3c-f970fb442a10_1920x1080.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!K04a!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcca9d511-1d33-49f6-9a3c-f970fb442a10_1920x1080.jpeg 424w, https://substackcdn.com/image/fetch/$s_!K04a!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcca9d511-1d33-49f6-9a3c-f970fb442a10_1920x1080.jpeg 848w, https://substackcdn.com/image/fetch/$s_!K04a!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcca9d511-1d33-49f6-9a3c-f970fb442a10_1920x1080.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!K04a!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcca9d511-1d33-49f6-9a3c-f970fb442a10_1920x1080.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>We're shipping <em><strong>v0.3.4</strong></em> today with a handful of quality-of-life improvements, a cleaner configuration story, and a few important bug fixes. Here's what's new.</p><div><hr></div><h2>Unified <code>MB_*</code> Environment Variable Prefix</h2><p>All environment variables now consistently use the <code>MB_*</code> prefix. If you were previously setting <code>MANTIS_JWT_SECRET</code>, update that to <code>MB_JWT_SECRET</code> &#8212; the old name is no longer recognised. This brings every runtime knob under one coherent namespace and makes it easier to manage configuration in Docker and CI environments.</p><p>Variable Description Default <code>MB_JWT_SECRET</code> JWT signing secret (set this in production!) built-in default <code>MB_DISABLE_FILE_UPLOADS</code> Set to <code>1</code> to disable file uploads globally <code>0</code> <code>MB_DISABLE_ADMIN_ON_FIRST_BOOT</code> Set to <code>1</code> to skip auto-admin creation browser spin off <code>0</code> <code>MB_DISABLE_RATE_LIMIT</code> Set to <code>1</code> to disable rate limiting enabled</p><div><hr></div><h2>File Upload Kill Switch</h2><p>A new <code>MB_DISABLE_FILE_UPLOADS=1</code> flag lets you turn off file uploads at the server level without touching your schema. Any upload attempt while this flag is active will receive a <code>403 Forbidden</code> response. Useful for read-only deployments or environments where you want to enforce that no files land on disk.</p><div><hr></div><h2>Docker Improvements</h2><p>The Dockerfile has been updated to <code>debian:trixie-slim</code> as its base, and the build arg has been renamed from <code>MANTIS_VERSION</code> to <code>MB_VERSION</code> (default <code>0.3.4</code>) to match the new naming convention. The docker-compose file now also surfaces all <code>MB_*</code> variables as commented-out stubs, so it's clear what you can configure without digging through docs.</p><p>The published image on Docker Hub is <code>allankoech/mantisbase</code> &#8212; the quick-start command is simply:</p><pre><code>docker run -p 7070:80 allankoech/mantisbase
</code></pre><p>All submodules have also been switched from SSH to HTTPS URLs and set to shallow fetch, which significantly speeds up fresh clones in CI pipelines.</p><div><hr></div><h2>Admin Panel Updates</h2><p>Several improvements have landed in the admin UI this release:</p><p><strong>Entity cloning</strong> &#8212; You can now clone an existing entity directly from the admin panel. Cloning copies the source entity's schema (field names, types, and structure) into a new entity, giving you a clean starting point to build on without recreating common field sets from scratch. No records are carried over, only the schema.</p><p><strong>Entity renaming</strong> &#8212; Entities can now be renamed from within the admin panel without having to drop and recreate them.</p><p><strong>Long entity name overflow fix</strong> &#8212; Entity names that exceeded the available sidebar/panel width were pushing other UI elements off screen. This has been corrected so long names truncate gracefully and the layout stays intact.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dyiE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a3e558d-7a68-414b-a74f-3d95b0d10f5d_556x366.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dyiE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a3e558d-7a68-414b-a74f-3d95b0d10f5d_556x366.png 424w, https://substackcdn.com/image/fetch/$s_!dyiE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a3e558d-7a68-414b-a74f-3d95b0d10f5d_556x366.png 848w, https://substackcdn.com/image/fetch/$s_!dyiE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a3e558d-7a68-414b-a74f-3d95b0d10f5d_556x366.png 1272w, https://substackcdn.com/image/fetch/$s_!dyiE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a3e558d-7a68-414b-a74f-3d95b0d10f5d_556x366.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dyiE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a3e558d-7a68-414b-a74f-3d95b0d10f5d_556x366.png" width="556" height="366" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1a3e558d-7a68-414b-a74f-3d95b0d10f5d_556x366.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:366,&quot;width&quot;:556,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!dyiE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a3e558d-7a68-414b-a74f-3d95b0d10f5d_556x366.png 424w, https://substackcdn.com/image/fetch/$s_!dyiE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a3e558d-7a68-414b-a74f-3d95b0d10f5d_556x366.png 848w, https://substackcdn.com/image/fetch/$s_!dyiE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a3e558d-7a68-414b-a74f-3d95b0d10f5d_556x366.png 1272w, https://substackcdn.com/image/fetch/$s_!dyiE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a3e558d-7a68-414b-a74f-3d95b0d10f5d_556x366.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Minor bug fixes and improvements</strong> across the admin panel generally.</p><div><hr></div><h2>Bug Fixes</h2><ul><li><p><strong>PATCH requests with password fields</strong> &#8212; a missing-field error that surfaced when patching records containing a <code>password</code> field has been resolved.</p></li><li><p><strong>Last-activity tracking</strong> &#8212; activity timestamps now only update on actual database record changes, not on every SSE ping or passive connection event. This prevents misleading "active" signals for idle clients.</p></li><li><p><strong>SSE session cleanup</strong> &#8212; auth tokens are no longer held in memory on SSE sessions. The token field is explicitly nulled out after verification, reducing the risk of credentials lingering in heap memory across a long-lived connection.</p></li></ul><div><hr></div><h2>Internal / Code Quality</h2><ul><li><p><code>soci_wrappers.h</code> saw a significant cleanup &#8212; redundant nesting in the field-binding logic was flattened, making the type-dispatch code considerably easier to follow.</p></li><li><p>SSE event queuing now uses <code>emplace</code> instead of <code>push</code> for slightly cleaner construction.</p></li><li><p>SQL update column formatting moved to <code>std::format</code> for consistency.</p></li></ul><div><hr></div><h2>Upgrading</h2><p>Update your environment variables from <code>MANTIS_JWT_SECRET</code> &#8594; <code>MB_JWT_SECRET</code> and pull the new image or rebuild from source. No schema migrations are required for this release.</p><p>The full changelog and binaries are available on the <a href="https://github.com/allankoechke/mantisbase/releases/tag/v0.3.4">GitHub releases page</a>.</p>]]></content:encoded></item><item><title><![CDATA[MantisBase Devlog #1—We're Live]]></title><description><![CDATA[If you've been following along, you know I've been busy building MantisBase for a while now.]]></description><link>https://www.codeart.co.ke/p/mb-devlog1-landing-page</link><guid isPermaLink="false">https://www.codeart.co.ke/p/mb-devlog1-landing-page</guid><dc:creator><![CDATA[Allan K. Koech]]></dc:creator><pubDate>Thu, 26 Feb 2026 20:24:07 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/7abe0891-406b-4d16-acb6-41a56d758e80_1920x1080.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5BRd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c2d4df6-27c7-4e50-a1fd-dac9655aac48_1920x1080.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5BRd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c2d4df6-27c7-4e50-a1fd-dac9655aac48_1920x1080.jpeg 424w, https://substackcdn.com/image/fetch/$s_!5BRd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c2d4df6-27c7-4e50-a1fd-dac9655aac48_1920x1080.jpeg 848w, https://substackcdn.com/image/fetch/$s_!5BRd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c2d4df6-27c7-4e50-a1fd-dac9655aac48_1920x1080.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!5BRd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c2d4df6-27c7-4e50-a1fd-dac9655aac48_1920x1080.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5BRd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c2d4df6-27c7-4e50-a1fd-dac9655aac48_1920x1080.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3c2d4df6-27c7-4e50-a1fd-dac9655aac48_1920x1080.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!5BRd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c2d4df6-27c7-4e50-a1fd-dac9655aac48_1920x1080.jpeg 424w, https://substackcdn.com/image/fetch/$s_!5BRd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c2d4df6-27c7-4e50-a1fd-dac9655aac48_1920x1080.jpeg 848w, https://substackcdn.com/image/fetch/$s_!5BRd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c2d4df6-27c7-4e50-a1fd-dac9655aac48_1920x1080.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!5BRd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c2d4df6-27c7-4e50-a1fd-dac9655aac48_1920x1080.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>If you've been following along, you know I've been busy building <a href="https://github.com/allankoech/mantisbase">MantisBase</a> for a while now. Today feels like a good checkpoint to step back, write a bit, and share what's shipped.</p><p>This is the first official devlog. There will be more.</p><div><hr></div><h2>The Project Website is Up</h2><p>First things first, MantisBase now has a proper home on the internet.</p><blockquote><h4><a href="https://mantisbase.com">MantisBase | Lightning fast BaaS in C++</a></h4></blockquote><p>It's a landing page for now, but it does the job &#8212; gives you a quick feel for what MantisBase is and where it's headed. If you've been curious but haven't dug into the GitHub repo yet, the website is probably the gentler starting point.</p><div><hr></div><h2>Try It Without Installing Anything</h2><p>This one I'm particularly happy about.</p><p>One of the biggest friction points with any self-hosted tool is the "I'll check it out later" trap; you bookmark it, tell yourself you'll spin it up over the weekend, and then never do. I've been guilty of this myself more times than I'd like to admit.</p><p>So we now have a live demo environment:</p><blockquote><h4><a href="https://demo.mantisbase.com">Mantis Admin Dashboard</a></h4></blockquote><p>No installation. No Docker. No config files. Just open it and poke around. If it clicks for you, <em>then</em> you can go down the setup rabbit hole.</p><div><hr></div><h2>Docker Image on Docker Hub</h2><p>Speaking of setup&#8212;for those who are comfortable with Docker and just want to pull and run, we've got you covered.</p><pre><code>docker pull allankoech/mantisbase
</code></pre><p>Full image available at:</p><blockquote><h4><a href="https://hub.docker.com/r/allankoech/mantisbase">allankoech/mantisbase - Docker Image</a></h4></blockquote><p>This is probably the fastest path from zero to a running instance if you're on a server or just want to keep things clean on your local machine.</p><div><hr></div><h2>What's Next</h2><p>We're currently in the middle of cleaning up the admin UI &#8212; ironing out some rough edges and squashing a few bugs that have been on the list. Nothing dramatic, just the kind of polish work that makes the difference between something that <em>works</em> and something that feels <em>solid</em>.</p><p>More updates soon. If you want to follow along, the <a href="https://github.com/allankoech/mantisbase">GitHub repo</a> is the most up-to-date place to track progress.</p><p>Until the next devlog. &#9996;&#65039;</p>]]></content:encoded></item><item><title><![CDATA[MantisBase v0.3.3: Real-Time Database Updates with Server-Sent Events]]></title><description><![CDATA[February 4, 2026 &#8212; We're excited to announce the release of MantisBase v0.3.3, bringing real-time database capabilities to this lightweight, high-performance Backend-as-a-Service framework.]]></description><link>https://www.codeart.co.ke/p/mantisbase-v0_3_3-rtdb</link><guid isPermaLink="false">https://www.codeart.co.ke/p/mantisbase-v0_3_3-rtdb</guid><dc:creator><![CDATA[Allan K. Koech]]></dc:creator><pubDate>Tue, 03 Feb 2026 22:04:45 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/81d7baa2-da37-44c7-b207-6efa851b3ba1_1920x1080.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jJqT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4458d63-b839-4549-89ce-f6b1618f0c96_1920x1080.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jJqT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4458d63-b839-4549-89ce-f6b1618f0c96_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!jJqT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4458d63-b839-4549-89ce-f6b1618f0c96_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!jJqT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4458d63-b839-4549-89ce-f6b1618f0c96_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!jJqT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4458d63-b839-4549-89ce-f6b1618f0c96_1920x1080.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jJqT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4458d63-b839-4549-89ce-f6b1618f0c96_1920x1080.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d4458d63-b839-4549-89ce-f6b1618f0c96_1920x1080.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!jJqT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4458d63-b839-4549-89ce-f6b1618f0c96_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!jJqT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4458d63-b839-4549-89ce-f6b1618f0c96_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!jJqT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4458d63-b839-4549-89ce-f6b1618f0c96_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!jJqT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4458d63-b839-4549-89ce-f6b1618f0c96_1920x1080.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><figcaption class="image-caption">MantisBase version 0.3.3 release banner featuring real-time database updates with Server-Sent Events support</figcaption></figure></div><p><strong>February 4, 2026</strong> &#8212; We're excited to announce the release of MantisBase v0.3.3, bringing real-time database capabilities to this lightweight, high-performance Backend-as-a-Service framework. This release introduces Server-Sent Events (SSE) support for both SQLite and PostgreSQL backends, along with critical bug fixes and improved system observability.</p><h2>What's New in v0.3.3</h2><h3>Real-Time Database via SSE</h3><p>The flagship feature of <em>v0.3.3</em> is native real-time database synchronisation using Server-Sent Events. Unlike traditional polling approaches that waste resources checking for updates, SSE establishes a persistent HTTP connection that allows the server to push changes to clients as they happen.</p><p><strong>Why SSE?</strong></p><p>Server-Sent Events offer several advantages for real-time database applications:</p><ul><li><p><strong>Efficient unidirectional streaming</strong> &#8212; Perfect for database change notifications where data flows primarily from server to client</p></li><li><p><strong>Built on standard HTTP</strong> &#8212; No special protocols or WebSocket infrastructure required</p></li><li><p><strong>Automatic reconnection</strong> &#8212; The browser's EventSource API handles connection recovery automatically</p></li><li><p><strong>Firewall-friendly</strong> &#8212; Works through corporate proxies and firewalls that might block WebSocket connections</p></li><li><p><strong>Low overhead</strong> &#8212; Simpler than WebSockets when you don't need bidirectional communication</p></li></ul><h3>Universal Backend Support</h3><p>Real-time updates now work seamlessly with both SQLite and PostgreSQL databases. Whether you're running MantisBase on an embedded device with SQLite or scaling up with PostgreSQL, you get the same real-time capabilities without changing your code.</p><h3>Enhanced System Observability</h3><p>System logs have been moved to a dedicated database and are now accessible through the <code>/api/v1/logs</code> endpoint, providing administrators with powerful filtering and pagination capabilities for monitoring and debugging.</p><h2>Getting Started with Real-Time</h2><h3>Opening an SSE Connection</h3><p>Connect to the real-time endpoint and subscribe to entity changes:</p><pre><code>// Subscribe to changes on the 'posts' entity
const eventSource = new EventSource(
  'http://localhost:7070/api/v1/realtime?topics=posts'
);

// Listen for all events
eventSource.onmessage = (event) =&gt; {
  const data = JSON.parse(event.data);
  console.log('Received event:', data);
};

// Handle connection events
eventSource.onopen = () =&gt; {
  console.log('SSE connection established');
};

eventSource.onerror = (error) =&gt; {
  console.error('SSE connection error:', error);
  // Browser automatically reconnects
};
</code></pre><h3>Subscribing to Multiple Entities</h3><p>You can monitor multiple database entities on a single connection:</p><pre><code>// Subscribe to posts, comments, and users
const eventSource = new EventSource(
  'http://localhost:7070/api/v1/realtime?topics=posts,comments,users'
);

eventSource.onmessage = (event) =&gt; {
  const data = JSON.parse(event.data);
  
  // Events include the entity name they originated from
  switch(data.entity) {
    case 'posts':
      updatePostsUI(data);
      break;
    case 'comments':
      updateCommentsUI(data);
      break;
    case 'users':
      updateUsersUI(data);
      break;
  }
};
</code></pre><h3>Event Format</h3><p>Real-time events follow a consistent JSON structure:</p><pre><code>{
  "type": "INSERT",
  "entity": "posts",
  "data": {
    "id": "abc123",
    "title": "New Post Title",
    "content": "Post content here",
    "created": "2026-02-03T14:30:00Z",
    "updated": "2026-02-03T14:30:00Z"
  },
  "timestamp": "2026-02-03T14:30:00.123Z"
}
</code></pre><p><strong>Event Types:</strong></p><ul><li><p><code>INSERT</code> &#8212; New record created</p></li><li><p><code>UPDATE</code> &#8212; Existing record modified</p></li><li><p><code>DELETE</code> &#8212; Record removed</p></li></ul><h3>Dynamic Topic Management</h3><p>Update your subscriptions without reconnecting:</p><pre><code>// Later, add more topics to the existing connection
async function subscribeToComments() {
  await fetch('http://localhost:7070/api/v1/realtime', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${yourAuthToken}`
    },
    body: JSON.stringify({
      client_id: clientId,
      topics: ['posts', 'comments']  // Updated topic list
    })
  });
}

// Or clear all subscriptions
async function unsubscribeAll() {
  await fetch('http://localhost:7070/api/v1/realtime', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${yourAuthToken}`
    },
    body: JSON.stringify({
      client_id: clientId,
      topics: []  // Empty array clears all topics
    })
  });
}
</code></pre><h2>System Logs API</h2><p>Access and monitor system logs with the new logs endpoint:</p><pre><code># Get recent logs (requires admin authentication)
curl -H "Authorization: Bearer &lt;admin_token&gt;" \
  http://localhost:7070/api/v1/logs

# Filter by log level
curl -H "Authorization: Bearer &lt;admin_token&gt;" \
  "http://localhost:7070/api/v1/logs?level=ERROR"

# Pagination
curl -H "Authorization: Bearer &lt;admin_token&gt;" \
  "http://localhost:7070/api/v1/logs?limit=50&amp;offset=100"

# Filter by time range
curl -H "Authorization: Bearer &lt;admin_token&gt;" \
  "http://localhost:7070/api/v1/logs?from=2026-02-03T00:00:00Z&amp;to=2026-02-03T23:59:59Z"
</code></pre><h2>Migration Guide</h2><p>If you're upgrading from <em>v0.3.2</em>, here's what you need to know:</p><h3>Breaking Changes</h3><p>There are no breaking changes in <em>v0.3.3</em>. The real-time API is a new addition that doesn't affect existing functionality. To enrol existing entities to be real-time "enabled", update the entities to trigger the real-time monitoring.</p><h3>Security Considerations</h3><h3>Authentication for Real-Time</h3><p>Real-time connections respect MantisBase's access control rules:</p><ul><li><p>For public topics (entities), no additional authentication is required.</p></li><li><p>For non-public entities, the following rules are enforced:</p><ul><li><p><em>List Rule</em>: Used to list records in an entity.</p></li><li><p><em>Get Rule</em>: Used to fetch a record by the record ID.</p></li></ul></li></ul><p>For non-public entities, provide the authorisation <strong>JWT</strong> token when creating the <code>EventSource</code> stream.</p><h2>Installation &amp; Upgrade</h2><h3>Download Pre-built Binaries</h3><p>Download the latest release from <a href="https://github.com/allankoechke/mantisbase/releases/tag/v0.3.3">GitHub Releases</a>:</p><pre><code># Linux
wget https://github.com/allankoechke/mantisbase/releases/download/v0.3.3/mantisbase-linux-x64.zip
unzip mantisbase-linux-x64.zip
chmod +x mantisbase
./mantisbase serve

# Windows
# Download mantisbase-windows-x64.zip and extract
mantisbase.exe serve
</code></pre><h3>Build from Source</h3><pre><code>git clone --recurse-submodules https://github.com/allankoechke/mantisbase.git
cd mantisbase
git checkout v0.3.3
cmake -B build
cmake --build build
./build/mantisbase serve
</code></pre><h3>Bug Fixes</h3><p>This release includes several important bug fixes:</p><ul><li><p>Fixed memory leak in SSE connection handling</p></li><li><p>Improved database connection pooling for PostgreSQL</p></li><li><p>Resolved race condition in concurrent entity updates</p></li><li><p>Fixed authentication token refresh timing</p></li><li><p>Corrected <em><strong>CORS</strong></em> headers for cross-origin real-time connections</p></li></ul><h2>Community &amp; Support</h2><p>Join our growing community:</p><ul><li><p><strong>GitHub</strong>: <a href="https://github.com/allankoechke/mantisbase">github.com/allankoechke/mantisbase</a></p></li><li><p><strong>Documentation</strong>: <a href="https://allankoechke.github.io/mantisbase/">allankoechke.github.io/mantisbase</a></p></li><li><p><strong>YouTube Tutorials</strong>: <a href="https://youtube.com/playlist?list=PLsG0sKNmNpyQwsZuReuqo_nl_j4SdJoiJ">MantisBase Playlist</a></p></li><li><p><strong>Discussions</strong>: <a href="https://github.com/allankoechke/mantisbase/discussions">GitHub Discussions</a></p></li><li><p><strong>Support</strong>: <a href="https://patreon.com/allankoech">Patreon</a></p></li></ul><h2>Acknowledgments</h2><p>Thank you to everyone who contributed to this release through bug reports, feature requests, and code contributions. MantisBase is built for developers, by developers.</p><div><hr></div><p>Checkout the video announcement here:</p><div class="captioned-image-container"><figure><div id="youtube2-M6pJJEILPv0" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;M6pJJEILPv0&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/M6pJJEILPv0?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div></figure></div><div><hr></div><p><strong>MantisBase</strong>&#8212;Lightning-fast BaaS in C++. One binary, full backend, anywhere.</p><p><em>For the full changelog, see <a href="https://github.com/allankoechke/mantisbase/compare/v0.3.2...v0.3.3">v0.3.2...v0.3.3</a></em></p>]]></content:encoded></item><item><title><![CDATA[Smart Living & Leadership: Designing for Tomorrow]]></title><description><![CDATA[The second edition of the Smart Living & Tech Leadership Conference 2025 ran for two days, with intense, future-focused dialogue.]]></description><link>https://www.codeart.co.ke/p/smart-living-leadership-conf-2025</link><guid isPermaLink="false">https://www.codeart.co.ke/p/smart-living-leadership-conf-2025</guid><dc:creator><![CDATA[Allan K. Koech]]></dc:creator><pubDate>Thu, 13 Nov 2025 15:00:05 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/3d86c76b-0cf0-4fdd-a220-7aa3349b4dbb_1080x625.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!D5rc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe84854ba-d768-4690-bcb7-ca758666597b_1080x625.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!D5rc!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe84854ba-d768-4690-bcb7-ca758666597b_1080x625.jpeg 424w, https://substackcdn.com/image/fetch/$s_!D5rc!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe84854ba-d768-4690-bcb7-ca758666597b_1080x625.jpeg 848w, https://substackcdn.com/image/fetch/$s_!D5rc!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe84854ba-d768-4690-bcb7-ca758666597b_1080x625.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!D5rc!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe84854ba-d768-4690-bcb7-ca758666597b_1080x625.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!D5rc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe84854ba-d768-4690-bcb7-ca758666597b_1080x625.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e84854ba-d768-4690-bcb7-ca758666597b_1080x625.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!D5rc!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe84854ba-d768-4690-bcb7-ca758666597b_1080x625.jpeg 424w, https://substackcdn.com/image/fetch/$s_!D5rc!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe84854ba-d768-4690-bcb7-ca758666597b_1080x625.jpeg 848w, https://substackcdn.com/image/fetch/$s_!D5rc!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe84854ba-d768-4690-bcb7-ca758666597b_1080x625.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!D5rc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe84854ba-d768-4690-bcb7-ca758666597b_1080x625.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><figcaption class="image-caption">Smart Living &amp; Tech Leadership Conference Banner</figcaption></figure></div><p>The second edition of the <strong>Smart Living &amp; Tech Leadership Conference 2025</strong> ran for two days, with intense, future-focused dialogue. Day one set the foundation &#8212; sustainability, leadership, and the evolving role of cities in shaping human experience. Talks centred on energy management, mobility systems, and the ethics of digital transformation.</p><p>Day two shifted toward <strong>design, technology, and AI</strong>, unpacking how digital tools turn ideas into smarter, more liveable environments.</p><h3>The Core Idea</h3><p>Smart living isn&#8217;t about gadgets. It&#8217;s about intentional design. Every decision &#8212; spatial, structural, or digital &#8212; shapes how we live, move, and connect. Design is the link between people, data, and sustainability.</p><blockquote><p>&#128488;&#65039; "Design is not just what it looks like and feels like. Design is how it works."<br>&#8212; Steve Jobs</p></blockquote><p>Steve Jobs was a visionary who foretold of what it became of today, a fusion of design and functionality. A move toward designing for use rather than designing for aesthetics alone.</p><h3>My Session: Smart Living by Design</h3><p>I presented <strong>&#8220;Smart Living by Design: Evolving from Digital Tools to AI Decisions.&#8221; </strong>The session traced design&#8217;s evolution&#8212;from manual drafting to data-driven modelling and now to intelligent, AI-augmented systems.</p><ul><li><p>Digital tools like <strong>Revit</strong>, <strong>Inventor</strong>, <strong>3ds Max</strong>, and <strong>Maya</strong> redefined collaboration and precision.</p></li><li><p><strong>AI</strong> now extends creativity &#8212; generating forms, predicting outcomes, and guiding sustainable choices.</p></li><li><p>Projects like <strong>Bernini</strong> show how AI-driven 3D generation can reduce waste and optimise performance.</p></li><li><p><strong>Digital twins</strong> enable continuous feedback loops between the real and virtual worlds, making smart cities adaptive by design.</p></li></ul><p>The key takeaway: AI doesn&#8217;t replace creativity &#8212; it amplifies it. The future designer leads with intent, while AI accelerates exploration and insight.</p><h3>Conversations That Followed</h3><p>Post-session discussions were rich. People wanted to know how data models support interoperability across industries and where the limits of AI automation should be drawn. Others shared case studies on generative layouts for housing, mobility planning, and carbon analysis.</p><p>Outside the panels, the networking felt natural. Designers, engineers, urban planners, and educators connected over one shared vision &#8212; building systems that think with us, not for us.</p><h3>Closing Thought</h3><p>The conference reinforced a simple truth: <strong>design is leadership</strong>. Tools evolve, but human intention drives change. If we design with purpose &#8212; aligning creativity, technology, and sustainability &#8212; smart living becomes not an idea, but a lived reality.</p><p>Join the conversation!</p><div class="captioned-image-container"><figure><blockquote><p><a href="https://twitter.com/hashtag/Design?src=hash&amp;ref_src=twsrc%5Etfw">#Design</a> isn&#8217;t only about how it looks &#8212; it&#8217;s how it works.<br><br>At the Smart Living Conf, we explored how <a href="https://twitter.com/hashtag/AI?src=hash&amp;ref_src=twsrc%5Etfw">#AI</a> is becoming a design partner. From <a href="https://twitter.com/hashtag/AEC?src=hash&amp;ref_src=twsrc%5Etfw">#AEC</a> to <a href="https://twitter.com/hashtag/MFG?src=hash&amp;ref_src=twsrc%5Etfw">#MFG</a> and M&amp;E, AI helps us move ideas to reality; optimizing layouts, improving precision, and driving sustainable innovation. <a href="https://t.co/LO5NcE2wfk">pic.twitter.com/LO5NcE2wfk</a></p><p>&#8212; Allan K. Koech, HSC (@allankoechke) <a href="https://twitter.com/allankoechke/status/1986534803051839939?ref_src=twsrc%5Etfw">November 6, 2025</a></p></blockquote></figure></div>]]></content:encoded></item><item><title><![CDATA[Embedding Slint UI Within a Qt Application (C++)]]></title><description><![CDATA[Qt is a mature and feature-rich framework, but building flexible and declarative UIs often requires more than widgets and signals.]]></description><link>https://www.codeart.co.ke/p/embedding-slintui-within-qt-app</link><guid isPermaLink="false">https://www.codeart.co.ke/p/embedding-slintui-within-qt-app</guid><dc:creator><![CDATA[Allan K. Koech]]></dc:creator><pubDate>Fri, 10 Oct 2025 18:51:35 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/fcbee22c-75c9-480a-a922-17b94b718d65_1920x1080.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6Fuj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18dc66be-94f2-4241-9eae-3f553cca4aad_1920x1080.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6Fuj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18dc66be-94f2-4241-9eae-3f553cca4aad_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!6Fuj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18dc66be-94f2-4241-9eae-3f553cca4aad_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!6Fuj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18dc66be-94f2-4241-9eae-3f553cca4aad_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!6Fuj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18dc66be-94f2-4241-9eae-3f553cca4aad_1920x1080.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6Fuj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18dc66be-94f2-4241-9eae-3f553cca4aad_1920x1080.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/18dc66be-94f2-4241-9eae-3f553cca4aad_1920x1080.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!6Fuj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18dc66be-94f2-4241-9eae-3f553cca4aad_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!6Fuj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18dc66be-94f2-4241-9eae-3f553cca4aad_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!6Fuj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18dc66be-94f2-4241-9eae-3f553cca4aad_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!6Fuj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18dc66be-94f2-4241-9eae-3f553cca4aad_1920x1080.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><figcaption class="image-caption">Embedding Slint UI in Qt Banner</figcaption></figure></div><p>Qt is a mature and feature-rich framework, but building flexible and declarative UIs often requires more than widgets and signals. That&#8217;s where Slint fits in. It provides a compact, declarative syntax for building modern UIs while staying native and lightweight.</p><p>Some Qt-based projects, like <em>LibrePCB</em>, already use Slint to modernise their UI without abandoning their existing Qt foundation. Check the full blog details <a href="https://librepcb.org/blog/2025-09-12_preview_of_next_gen_ui/">here</a>. Embedding Slint lets you:</p><ul><li><p>Keep your Qt app logic and backend code intact.</p></li><li><p>Use Slint for fast, declarative, and maintainable UI layers.</p></li><li><p>Mix and match Slint views with traditional Qt widgets.</p></li><li><p>Gradually migrate UI parts without a full rewrite.</p></li></ul><div><hr></div><p>Watch the walkthrough video here:</p><p>Slint UI 102: Embedding Slint UI Within a Qt Application | C++</p><div class="captioned-image-container"><figure><div id="youtube2-0ZPPeeCJA_k" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;0ZPPeeCJA_k&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/0ZPPeeCJA_k?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div></figure></div><div><hr></div><h2>What you&#8217;ll learn:</h2><ul><li><p>How to embed Slint UI components in a Qt app</p></li><li><p>How to compile <code>.slint</code> files to native C++</p></li><li><p>How to synchronise data between Qt and Slint</p></li><li><p>How to manage event flow and cleanup</p></li></ul><h2>Resources:</h2><ul><li><p>Slint Documentation: <a href="https://slint.dev/docs">https://slint.dev/docs</a></p></li><li><p>Slint Source Code: <a href="https://github.com/slint-ui/slint">https://github.com/slint-ui/slint</a></p></li><li><p>Slint C++ Template: <a href="https://github.com/slint-ui/slint-cpp-template">https://github.com/slint-ui/slint-cpp-template</a></p></li><li><p>Full Source Code from this video: <a href="https://github.com/allankoechke/qt-slint-integration-cpp">https://github.com/allankoechke/qt-slint-integration-cpp</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[[Youtube] What is Mantis?]]></title><description><![CDATA[We have earlier introduced Mantis, a C++ BaaS toolkit, designed to be lightweight but powerful.]]></description><link>https://www.codeart.co.ke/p/youtube-what-is-mantis</link><guid isPermaLink="false">https://www.codeart.co.ke/p/youtube-what-is-mantis</guid><dc:creator><![CDATA[Allan K. Koech]]></dc:creator><pubDate>Tue, 30 Sep 2025 22:50:28 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/6be29692-7d84-4619-a95b-46710976b0e9_1920x1080.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!t6Qf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d99134f-8535-4e75-ba9c-29d02ed41ab1_1920x1080.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!t6Qf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d99134f-8535-4e75-ba9c-29d02ed41ab1_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!t6Qf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d99134f-8535-4e75-ba9c-29d02ed41ab1_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!t6Qf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d99134f-8535-4e75-ba9c-29d02ed41ab1_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!t6Qf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d99134f-8535-4e75-ba9c-29d02ed41ab1_1920x1080.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!t6Qf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d99134f-8535-4e75-ba9c-29d02ed41ab1_1920x1080.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2d99134f-8535-4e75-ba9c-29d02ed41ab1_1920x1080.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!t6Qf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d99134f-8535-4e75-ba9c-29d02ed41ab1_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!t6Qf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d99134f-8535-4e75-ba9c-29d02ed41ab1_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!t6Qf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d99134f-8535-4e75-ba9c-29d02ed41ab1_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!t6Qf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d99134f-8535-4e75-ba9c-29d02ed41ab1_1920x1080.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><figcaption class="image-caption">What is Mantis?</figcaption></figure></div><p>We have earlier introduced Mantis, a C++ BaaS toolkit, designed to be lightweight but powerful.</p><blockquote><h4><a href="https://codeart.co.ke/introducing-mantis-the-lightweight-baas-in-cpp/">Introducing Mantis: The C++ Backend Framework Built for Simplicity and Speed</a></h4></blockquote><h1>What's changed?</h1><p>From our last v0.2.7 release a couple of weeks ago, we have some important fixes and improvements added to our v0.2.8 release. Have a look:</p><ul><li><p><em><strong>PostgreSQL Support</strong></em>: v0.2.8 brings full PostgreSQL support for Linux builds. This allows for easy backend database switch when scaling is required while maintaining lightweight backend database (SQLite) where it's needed. How do you switch the database? Easy!</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!A9jM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cef2756-b0c2-47b9-bd2e-6e5b2c5c149d_879x73.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!A9jM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cef2756-b0c2-47b9-bd2e-6e5b2c5c149d_879x73.png 424w, https://substackcdn.com/image/fetch/$s_!A9jM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cef2756-b0c2-47b9-bd2e-6e5b2c5c149d_879x73.png 848w, https://substackcdn.com/image/fetch/$s_!A9jM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cef2756-b0c2-47b9-bd2e-6e5b2c5c149d_879x73.png 1272w, https://substackcdn.com/image/fetch/$s_!A9jM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cef2756-b0c2-47b9-bd2e-6e5b2c5c149d_879x73.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!A9jM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cef2756-b0c2-47b9-bd2e-6e5b2c5c149d_879x73.png" width="879" height="73" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3cef2756-b0c2-47b9-bd2e-6e5b2c5c149d_879x73.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:73,&quot;width&quot;:879,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!A9jM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cef2756-b0c2-47b9-bd2e-6e5b2c5c149d_879x73.png 424w, https://substackcdn.com/image/fetch/$s_!A9jM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cef2756-b0c2-47b9-bd2e-6e5b2c5c149d_879x73.png 848w, https://substackcdn.com/image/fetch/$s_!A9jM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cef2756-b0c2-47b9-bd2e-6e5b2c5c149d_879x73.png 1272w, https://substackcdn.com/image/fetch/$s_!A9jM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cef2756-b0c2-47b9-bd2e-6e5b2c5c149d_879x73.png 1456w" sizes="100vw"></picture><div></div></div></a><figcaption class="image-caption">Connecting to PostgreSQL database</figcaption></figure></div><ul><li><p><em><strong>Minor bug fixes &amp; improvements</strong></em>: Lots of bugs were squashed, solving some performance issues as well as mending some issues that existed. Chief among these is matching data types compatibility for SQLite to/from PostgreSQL, forcing SQLite WAL mode checkpointing for every boot and refactoring project for performance improvements.</p></li></ul><div><hr></div><blockquote><h4><a href="https://github.com/allankoechke/mantis/releases/tag/v0.2.8">Release Release v0.2.8 &#183; allankoechke/mantis</a></h4></blockquote><div><hr></div><h1>Getting Started?</h1><p>We have a YouTube video to guide you out; don't fret. Checkout the video below for a walkthrough of the features mantis provides. If you have any question, lets discuss on YouTube or open a GitHub issue.</p><div class="captioned-image-container"><figure><div id="youtube2-5SdmabDqeqY" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;5SdmabDqeqY&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/5SdmabDqeqY?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div></figure></div><div><hr></div><p>Enjoy!</p>]]></content:encoded></item><item><title><![CDATA[Mantis v0.2.7-beta Release]]></title><description><![CDATA[We&#8217;ve shipped Mantis v0.2.7-beta, a new milestone for our lightweight pluggable backend-as-a-service (BaaS) framework written in C++.]]></description><link>https://www.codeart.co.ke/p/mantis-v0-2-7-beta-release</link><guid isPermaLink="false">https://www.codeart.co.ke/p/mantis-v0-2-7-beta-release</guid><dc:creator><![CDATA[Allan K. Koech]]></dc:creator><pubDate>Sat, 23 Aug 2025 19:20:15 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/62b794e9-a40e-4476-8417-ce777a485ad7_1500x752.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nf6E!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcbe2adf-076e-4cd4-ad9b-1fa4f757d175_1500x752.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nf6E!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcbe2adf-076e-4cd4-ad9b-1fa4f757d175_1500x752.png 424w, https://substackcdn.com/image/fetch/$s_!nf6E!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcbe2adf-076e-4cd4-ad9b-1fa4f757d175_1500x752.png 848w, https://substackcdn.com/image/fetch/$s_!nf6E!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcbe2adf-076e-4cd4-ad9b-1fa4f757d175_1500x752.png 1272w, https://substackcdn.com/image/fetch/$s_!nf6E!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcbe2adf-076e-4cd4-ad9b-1fa4f757d175_1500x752.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!nf6E!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcbe2adf-076e-4cd4-ad9b-1fa4f757d175_1500x752.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fcbe2adf-076e-4cd4-ad9b-1fa4f757d175_1500x752.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!nf6E!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcbe2adf-076e-4cd4-ad9b-1fa4f757d175_1500x752.png 424w, https://substackcdn.com/image/fetch/$s_!nf6E!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcbe2adf-076e-4cd4-ad9b-1fa4f757d175_1500x752.png 848w, https://substackcdn.com/image/fetch/$s_!nf6E!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcbe2adf-076e-4cd4-ad9b-1fa4f757d175_1500x752.png 1272w, https://substackcdn.com/image/fetch/$s_!nf6E!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcbe2adf-076e-4cd4-ad9b-1fa4f757d175_1500x752.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><figcaption class="image-caption">Mantis v0.2.7 release banner</figcaption></figure></div><p>We&#8217;ve shipped <strong>Mantis v0.2.7-beta</strong>, a new milestone for our lightweight pluggable backend-as-a-service (BaaS) framework written in C++. This release focuses on making file management a first-class feature while continuing to strengthen stability and prepare for broader database support.</p><h2>Highlights</h2><h3>File and Files Field Support</h3><ul><li><p>Records now support <code>file</code> and <code>files</code> fields.</p></li><li><p>Upload, update, and remove files directly through record operations.</p></li><li><p>Safer filename handling with sanitization for long names and unknown characters.</p></li></ul><p>For more information on how to handle files in Mantis, check out the docs below:</p><blockquote><h4><a href="https://github.com/allankoechke/mantis/blob/master/doc/11.files.md">mantis/doc/11.files.md at master &#183; allankoechke/mantis</a></h4></blockquote><h3>Health check Endpoint</h3><ul><li><p>Added a <code>/api/v1/health</code> endpoint to make it easier to monitor and integrate Mantis into production setups.</p></li></ul><h3>Mantis Admin v0.1.7</h3><ul><li><p>Updated to the latest version of the Mantis Admin dashboard with file management capabilities.</p></li><li><p>Improved performance in the admin panel when working with larger datasets.</p></li></ul><h3>Bug Fixes and Improvements</h3><ul><li><p>Better handling of database value binding with <code>soci::values</code>.</p></li><li><p>Performance optimization for table ID generation.</p></li><li><p>More consistent cleanup of file references for updates and removals.</p></li></ul><h3>PostgreSQL Support (WIP)</h3><ul><li><p>Early integration of PostgreSQL via SOCI.</p></li><li><p>Initial support for builds, CLI commands, and CI/CD integration.</p></li><li><p>Still marked experimental but progressing quickly.</p></li></ul><blockquote><p>&#9888;&#65039; PostgreSQL support is experimental and currently only built for Linux. The feature still has unresolved issues and may fail to launch entirely.</p></blockquote><h2>Why It Matters</h2><p>Mantis is designed as an alternative to Firebase, Supabase, or PocketBase, but lighter, portable, and embeddable directly into your C++ applications. By keeping the runtime minimal and the architecture pluggable, we aim to make backend services faster to integrate and easier to run anywhere.</p><h2>What&#8217;s Next?</h2><ul><li><p>Complete PostgreSQL support for production use.</p></li><li><p>Expanded file access controls.</p></li><li><p>More integrations for different deployment targets.</p></li></ul><h2>Get Started</h2><p>GitHub Repo: <a href="https://github.com/allankoechke/mantis">https://github.com/allankoechke/mantis</a><br>Release Page: <a href="https://github.com/allankoechke/mantis/releases/tag/v0.2.7-beta">https://github.com/allankoechke/mantis/releases/tag/v0.2.7-beta</a><br>Documentation: <a href="https://docs.mantisapp.dev/">https://docs.mantisapp.dev/</a>.</p><div><hr></div><p>Feedback is welcome. If you try out the new file support or start testing PostgreSQL integration, please let us know how it works for you.</p>]]></content:encoded></item><item><title><![CDATA[Introducing Mantis: The C++ Backend Framework Built for Simplicity and Speed]]></title><description><![CDATA[In a world dominated by heavyweight frameworks and bloated deployment pipelines, Mantis stands out as a fast, modular C++ backend library designed for simplicity, speed, and embeddability.]]></description><link>https://www.codeart.co.ke/p/introducing-mantis-the-lightweight-baas-in-cpp</link><guid isPermaLink="false">https://www.codeart.co.ke/p/introducing-mantis-the-lightweight-baas-in-cpp</guid><dc:creator><![CDATA[Allan K. Koech]]></dc:creator><pubDate>Wed, 09 Jul 2025 21:11:38 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/b30d0675-b9a3-44f9-b165-a92f462342a8_1187x594.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vmMv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1edc2d4-dd7c-4cb0-93af-b8bd87b173c1_1187x594.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vmMv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1edc2d4-dd7c-4cb0-93af-b8bd87b173c1_1187x594.png 424w, https://substackcdn.com/image/fetch/$s_!vmMv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1edc2d4-dd7c-4cb0-93af-b8bd87b173c1_1187x594.png 848w, https://substackcdn.com/image/fetch/$s_!vmMv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1edc2d4-dd7c-4cb0-93af-b8bd87b173c1_1187x594.png 1272w, https://substackcdn.com/image/fetch/$s_!vmMv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1edc2d4-dd7c-4cb0-93af-b8bd87b173c1_1187x594.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vmMv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1edc2d4-dd7c-4cb0-93af-b8bd87b173c1_1187x594.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e1edc2d4-dd7c-4cb0-93af-b8bd87b173c1_1187x594.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!vmMv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1edc2d4-dd7c-4cb0-93af-b8bd87b173c1_1187x594.png 424w, https://substackcdn.com/image/fetch/$s_!vmMv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1edc2d4-dd7c-4cb0-93af-b8bd87b173c1_1187x594.png 848w, https://substackcdn.com/image/fetch/$s_!vmMv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1edc2d4-dd7c-4cb0-93af-b8bd87b173c1_1187x594.png 1272w, https://substackcdn.com/image/fetch/$s_!vmMv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1edc2d4-dd7c-4cb0-93af-b8bd87b173c1_1187x594.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><figcaption class="image-caption">Introducing Mantis</figcaption></figure></div><p>In a world dominated by heavyweight frameworks and bloated deployment pipelines, <strong>Mantis</strong> stands out as a fast, modular C++ backend library designed for simplicity, speed, and embeddability. Whether you're building for the cloud, desktop, or embedded systems, Mantis gives you modern backend capabilities with zero friction.</p><h2>What Makes Mantis Special?</h2><h3>&#128640; Single Binary Deployment</h3><p>No containers, no dependencies, no problem. Mantis compiles to a single <code>mantisapp</code> binary that runs anywhere&#8212;just:</p><pre><code>./mantisapp serve -p 8000
</code></pre><p>It&#8217;s ideal for embedded devices, desktop applications, or servers where simplicity is paramount.</p><h3>Zero-Configuration REST APIs</h3><p>Define your tables&#8212;and that&#8217;s it. Mantis auto-generates RESTful CRUD endpoints, complete with JWT-based authentication and fine-grained access control.</p><h3>&#129504; Minimal Runtime Footprint</h3><p>With SQLite as the default database and tight memory usage, Mantis is engineered for environments where every byte counts.</p><div><hr></div><h2>Key Benefits That Set Mantis Apart</h2><h3>&#128295; Embeddable by Design</h3><p>Unlike traditional backends that require external deployment, Mantis can be embedded directly into your C++ application. Add backend functionality to Qt apps, CLI tools, or microservices&#8212;all from within your codebase.</p><h3>&#128272; Built-in Authentication &amp; Security</h3><p>Mantis handles token generation, validation, and route-level access control using JWT. You get secure, configurable auth out-of-the-box.</p><h3>&#128202; Lightweight Admin Dashboard</h3><p>Each Mantis deployment includes a built-in admin dashboard for managing tables, records, and users&#8212;no separate tooling or setup required.</p><h3>&#128051; Docker-Ready</h3><p>Mantis offers a Docker image with volume mounting support, ideal for containerized environments and CI/CD pipelines.</p><div><hr></div><h2>&#128736; Technology Stack</h2><p>Mantis integrates robust C++ libraries to deliver production-grade performance:</p><ul><li><p><strong>HTTP Server</strong>: <a href="https://github.com/yhirose/cpp-httplib"><code>httplib-cpp</code></a> for lightweight HTTP(S) handling</p></li><li><p><strong>Database</strong>: <a href="https://github.com/SOCI/soci"><code>SOCI</code></a> as an abstraction layer (SQLite by default; MySQL/PostgreSQL coming soon)</p></li><li><p><strong>Authentication</strong>: <a href="https://github.com/GlitchedPolygons/l8w8jwt"><code>l8w8jwt</code></a> for secure token management</p></li><li><p><strong>Logging</strong>: <a href="https://github.com/gabime/spdlog"><code>spdlog</code></a> for structured, high-performance logging</p></li></ul><div><hr></div><h2>&#128679; Future Vision: What&#8217;s Coming</h2><p>Mantis isn&#8217;t just stable&#8212;it&#8217;s growing. Here&#8217;s what&#8217;s on the horizon:</p><ul><li><p><strong>Real-Time Sync Engine</strong><br>WebSocket-powered data sync between clients and the backend&#8212;perfect for offline-first and collaborative apps.</p></li><li><p><strong>Full RDBMS Support</strong><br>Extend Mantis from SQLite to full support for MySQL and PostgreSQL with seamless migration tooling.</p></li><li><p><strong>Pluggable Middleware Architecture</strong><br>Customize request handling with user-defined middleware for logging, validation, authentication, or anything else.</p></li></ul><div><hr></div><h2>Getting Started is Effortless</h2><p>You can start using Mantis today in one of two ways:</p><h3>Option 1: Use Prebuilt Binaries</h3><p>Available for <strong>Linux (x86_64)</strong> and <strong>Windows (x64)&#8212;</strong><em>just download and run</em>.</p><h3>Option 2: Build From Source</h3><p>If you're targeting another platform or want to embed Mantis into your own C++ application:</p><pre><code>git clone --recurse-submodules https://github.com/allankoechke/mantis.git
cd mantis
cmake -B build
cmake --build build
./build/mantisapp serve -p 5000
</code></pre><p>Access the dashboard at <a href="http://localhost:5000/admin">http://localhost:5000/admin</a>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hPTi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcd6579e-e4eb-471f-8eb8-269090d7dba9_3200x1736.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hPTi!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcd6579e-e4eb-471f-8eb8-269090d7dba9_3200x1736.png 424w, https://substackcdn.com/image/fetch/$s_!hPTi!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcd6579e-e4eb-471f-8eb8-269090d7dba9_3200x1736.png 848w, https://substackcdn.com/image/fetch/$s_!hPTi!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcd6579e-e4eb-471f-8eb8-269090d7dba9_3200x1736.png 1272w, https://substackcdn.com/image/fetch/$s_!hPTi!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcd6579e-e4eb-471f-8eb8-269090d7dba9_3200x1736.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hPTi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcd6579e-e4eb-471f-8eb8-269090d7dba9_3200x1736.png" width="2000" height="1085" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bcd6579e-e4eb-471f-8eb8-269090d7dba9_3200x1736.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1085,&quot;width&quot;:2000,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!hPTi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcd6579e-e4eb-471f-8eb8-269090d7dba9_3200x1736.png 424w, https://substackcdn.com/image/fetch/$s_!hPTi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcd6579e-e4eb-471f-8eb8-269090d7dba9_3200x1736.png 848w, https://substackcdn.com/image/fetch/$s_!hPTi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcd6579e-e4eb-471f-8eb8-269090d7dba9_3200x1736.png 1272w, https://substackcdn.com/image/fetch/$s_!hPTi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcd6579e-e4eb-471f-8eb8-269090d7dba9_3200x1736.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Mantis Admin Dashboard (WIP)</figcaption></figure></div><p>&#128073; Read the full setup guide here:<br><strong><a href="https://allankoechke.github.io/mantis/setup.html">https://allankoechke.github.io/mantis/setup.html</a></strong></p><div><hr></div><h2>&#127775; Why Choose Mantis?</h2><p>In a space filled with over-engineered solutions, <strong>Mantis</strong> offers a refreshing alternative: modern backend functionality in a compact, embeddable C++ package. Whether you're targeting embedded devices, developer tools, or just want a backend that won&#8217;t get in your way&#8212;Mantis delivers.</p><div><hr></div><h3>Ready to Try It?</h3><p>Explore the <a href="https://github.com/allankoechke/mantis">GitHub repository</a>, download the binaries, and see what lightweight backend development really feels like.</p>]]></content:encoded></item><item><title><![CDATA[DeepWiki: How to Instantly Understand Any Codebase with AI-Powered Documentation]]></title><description><![CDATA[In the growing jungle of codebases, developer tools, and open-source libraries, one thing remains consistently frustrating: documentation.]]></description><link>https://www.codeart.co.ke/p/deepwiki-understand-any-codebase-with-ai</link><guid isPermaLink="false">https://www.codeart.co.ke/p/deepwiki-understand-any-codebase-with-ai</guid><dc:creator><![CDATA[Allan K. Koech]]></dc:creator><pubDate>Thu, 19 Jun 2025 15:00:47 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/f5ffd3cf-9811-4bc5-8f2c-ba4172d55838_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-veB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7fde676-f6c8-4c01-a485-a23ef0c42a59_1024x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-veB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7fde676-f6c8-4c01-a485-a23ef0c42a59_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!-veB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7fde676-f6c8-4c01-a485-a23ef0c42a59_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!-veB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7fde676-f6c8-4c01-a485-a23ef0c42a59_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!-veB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7fde676-f6c8-4c01-a485-a23ef0c42a59_1024x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-veB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7fde676-f6c8-4c01-a485-a23ef0c42a59_1024x1024.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e7fde676-f6c8-4c01-a485-a23ef0c42a59_1024x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!-veB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7fde676-f6c8-4c01-a485-a23ef0c42a59_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!-veB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7fde676-f6c8-4c01-a485-a23ef0c42a59_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!-veB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7fde676-f6c8-4c01-a485-a23ef0c42a59_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!-veB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7fde676-f6c8-4c01-a485-a23ef0c42a59_1024x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><figcaption class="image-caption">Deepwiki: Chat with your codebase.</figcaption></figure></div><p>In the growing jungle of codebases, developer tools, and open-source libraries, one thing remains consistently frustrating: documentation. Whether they're too shallow, too outdated, or just missing altogether, bad docs slow everyone down. But what if you could talk to a codebase like you would a teammate? That&#8217;s where DeepWiki comes in.</p><blockquote><h4><a href="https://deepwiki.com/">DeepWiki | AI documentation you can talk to, for every repo</a></h4></blockquote><h3>What&#8217;s DeepWiki?</h3><p>It provides up-to-date documentation for any repo in the world that you can talk to. At first glance, you might consider it similar to your favorite LLM, but with the added context of a codebase.</p><blockquote><p>&#128161; A wiki is a form of hypertext publication on the internet which is collaboratively edited and managed by its audience directly through a web browser.&nbsp;A typical wiki contains multiple pages that can either be edited by the public or limited to use within an organization for maintaining its internal knowledge base.</p></blockquote><p>DeepWiki is powered by Devin AI, but over the past few months, there have been new open-source entrants to the space providing the same functionality.</p><h3>Where does it find its use?</h3><p>Consider this: the quality of documentation for repositories can range from excellent to inadequate. Imagine bumping into a repo with no docs, but it seems like a good fit for your needs. This scenario is where DeepWiki can help you create documentation for human understanding without needing to read through the entire codebase line by line.</p><p>You ask, it answers. From <em>"What does this function do?"</em> to <em>"How does authentication work in this repo?" </em>DeepWiki gives you direct, conversational insight based on the actual code, not assumptions.</p><p>It&#8217;s particularly useful when:</p><ul><li><p>You inherit a legacy code and need to quickly catch up.</p></li><li><p>You're actively participating in open-source projects and seeking to understand the internal workings.</p></li><li><p>You&#8217;re assessing a new library or tool and need to grasp its internals without reading 10 files.</p></li></ul><h3>But how does it work?</h3><p>Behind the scenes, DeepWiki syncs with the repository and builds an internal understanding of the code&#8212;structure, logic, and behavior. It doesn&#8217;t just summarize; it contextualizes. Ask a question, and it doesn&#8217;t respond based on generic training but refers to the actual code. That&#8217;s the difference.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!loHd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fbffe42-3154-426a-b93f-0da605b37298_1516x787.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!loHd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fbffe42-3154-426a-b93f-0da605b37298_1516x787.png 424w, https://substackcdn.com/image/fetch/$s_!loHd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fbffe42-3154-426a-b93f-0da605b37298_1516x787.png 848w, https://substackcdn.com/image/fetch/$s_!loHd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fbffe42-3154-426a-b93f-0da605b37298_1516x787.png 1272w, https://substackcdn.com/image/fetch/$s_!loHd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fbffe42-3154-426a-b93f-0da605b37298_1516x787.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!loHd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fbffe42-3154-426a-b93f-0da605b37298_1516x787.png" width="200" height="103" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5fbffe42-3154-426a-b93f-0da605b37298_1516x787.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:103,&quot;width&quot;:200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!loHd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fbffe42-3154-426a-b93f-0da605b37298_1516x787.png 424w, https://substackcdn.com/image/fetch/$s_!loHd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fbffe42-3154-426a-b93f-0da605b37298_1516x787.png 848w, https://substackcdn.com/image/fetch/$s_!loHd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fbffe42-3154-426a-b93f-0da605b37298_1516x787.png 1272w, https://substackcdn.com/image/fetch/$s_!loHd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fbffe42-3154-426a-b93f-0da605b37298_1516x787.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>How do I get started?</h3><p>It's actually quite simple. On any GitHub repo you need to look into, replace the GitHub with the DeepWiki domain. For instance, for the SQLite repo above, replace <code>https://github.com/sqlite/sqlite</code> to <code>https://deepwiki.com/sqlite/sqlite</code>and there you'll be ready to chat with your repo.</p><blockquote><p>&#9888;&#65039; Note that not all repos are already indexed. For those that aren't, you'll have to request indexing and provide your email, and you'll be notified once indexing is completed. Usually in less than an hour or so.</p></blockquote><h3>Why it matters</h3><p>We&#8217;re entering a world where shipping fast isn't enough; you also need to <em>understand fast</em>. DeepWiki makes technical onboarding smoother, reduces context-switching, and adds clarity to chaos, especially in large or under-documented codebases.</p><p>Sure, it&#8217;s not a silver bullet. It won&#8217;t replace solid developer docs. But it&#8217;s a powerful fallback when the docs are missing, outdated, or nonexistent.</p><h3>Final thoughts</h3><p>In a way, DeepWiki brings back the original promise of AI in dev tooling&#8212;not to replace developers, but to augment them. It&#8217;s not just about code completion or chat-based assistance anymore; it&#8217;s about deep understanding. And in that space, DeepWiki has carved out something genuinely useful.</p><p>Whether you're delving deeply into a complex system or simply exploring a new library, DeepWiki is a valuable resource.</p><h3>FAQs</h3><blockquote><h4><strong>1. What is DeepWiki, and how does it work?</strong></h4><p>DeepWiki is an AI-powered tool by Devin AI that generates real-time, conversational documentation for any code repository. It analyzes the codebase and lets developers ask natural language questions to understand functionality, architecture, and workflows.</p></blockquote><blockquote><h4><strong>2. Can DeepWiki document private or internal repositories?</strong></h4><p>Yes, DeepWiki can be used with private repositories as long as you grant it access. It can then generate documentation and allow conversational querying based on your internal codebase.</p></blockquote><blockquote><h4><strong>3. How is DeepWiki different from ChatGPT or other AI coding assistants?</strong></h4><p>While general AI assistants like ChatGPT are trained on broad programming knowledge, DeepWiki is built to ingest and understand specific repositories, offering contextual, code-aware responses tailored to your exact codebase.</p></blockquote><blockquote><h4><strong>4. Is DeepWiki useful for teams and enterprise-level development?</strong></h4><p>Absolutely. DeepWiki can dramatically speed up developer onboarding, reduce time spent searching through code, and improve collaboration by acting as a living, interactive documentation layer for your projects.</p></blockquote><blockquote><h4><strong>5. Does DeepWiki replace traditional documentation?</strong></h4><p>No, but it complements it. While human-written documentation provides structure and intent, DeepWiki helps fill gaps, explain code in real time, and answer questions dynamically&#8212;especially when traditional docs are missing or outdated.</p></blockquote>]]></content:encoded></item><item><title><![CDATA[Upgrading 3ds Max .NET Projects from .NET Framework to .NET 8]]></title><description><![CDATA[3ds Max 2025 and earlier versions have used the .NET Framework for building plugins, but, with the VFX world transitioning to .NET, 3ds Max 2026 brings this support with it. As such, .NET projects require a bit of housekeeping to get them compiled and wor]]></description><link>https://www.codeart.co.ke/p/upgrading-3dsmax-net-framework-projects-to-net8</link><guid isPermaLink="false">https://www.codeart.co.ke/p/upgrading-3dsmax-net-framework-projects-to-net8</guid><dc:creator><![CDATA[Allan K. Koech]]></dc:creator><pubDate>Sun, 01 Jun 2025 07:41:26 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/86aff209-0566-4c65-9b1e-46de0e6d95ca_2588x1292.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ftFi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe89b88ae-ef63-4e72-a6d8-0e8d5a45b8f1_2588x1292.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ftFi!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe89b88ae-ef63-4e72-a6d8-0e8d5a45b8f1_2588x1292.png 424w, https://substackcdn.com/image/fetch/$s_!ftFi!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe89b88ae-ef63-4e72-a6d8-0e8d5a45b8f1_2588x1292.png 848w, https://substackcdn.com/image/fetch/$s_!ftFi!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe89b88ae-ef63-4e72-a6d8-0e8d5a45b8f1_2588x1292.png 1272w, https://substackcdn.com/image/fetch/$s_!ftFi!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe89b88ae-ef63-4e72-a6d8-0e8d5a45b8f1_2588x1292.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ftFi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe89b88ae-ef63-4e72-a6d8-0e8d5a45b8f1_2588x1292.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e89b88ae-ef63-4e72-a6d8-0e8d5a45b8f1_2588x1292.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ftFi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe89b88ae-ef63-4e72-a6d8-0e8d5a45b8f1_2588x1292.png 424w, https://substackcdn.com/image/fetch/$s_!ftFi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe89b88ae-ef63-4e72-a6d8-0e8d5a45b8f1_2588x1292.png 848w, https://substackcdn.com/image/fetch/$s_!ftFi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe89b88ae-ef63-4e72-a6d8-0e8d5a45b8f1_2588x1292.png 1272w, https://substackcdn.com/image/fetch/$s_!ftFi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe89b88ae-ef63-4e72-a6d8-0e8d5a45b8f1_2588x1292.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><figcaption class="image-caption">.NET Upgrade Assistant Installation</figcaption></figure></div><div><hr></div><p>Originally posted at:</p><blockquote><h4><a href="https://blog.autodesk.io/upgrading-3ds-max-net-projects-from-net-framework-to-net-8/">Autodesk Developer BlogUpgrading 3ds Max .NET projects from .NET Framework to .NET 8</a></h4></blockquote><div><hr></div><p>3ds Max 2025 and earlier versions have used the .NET Framework for building plugins, but, with the VFX world transitioning to .NET, 3ds Max 2026 brings this support with it. As such, .NET projects require a bit of housekeeping to get them compiled and working in .NET 8 for our case. This blog is a simple walkthrough of the upgrade process, using the public <em><strong>Explode Geometry</strong></em> Sample hosted <a href="https://github.com/ADN-DevTech/3dsMax-Explode-Geometry">here</a>.</p><h2><strong>The Upgrade Assistant</strong></h2><p>Microsoft provides an <em><a href="https://dotnet.microsoft.com/en-us/platform/upgrade-assistant">Upgrade Assistant</a></em> extension to help with porting .NET Framework projects to .NET as well as .NET Core to .NET and much more. The tool simplifies and aids in handling the upgrade intricacies, leaving just a few more steps for us to complete.</p><p>First, to get things going, check if you have the Upgrade Assistant installed. If not installed, the <em><strong>Visual Studio Extensions Manager</strong></em><strong> </strong>should be your first stop. With Visual Studio opened, navigate to <em>Menu &gt; Extensions &gt; Manage Extensions. </em>Under the Extensions Manager, search for <em>Upgrade Assistant</em> and get it installed as shown below.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FHCl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84bfb8f3-db7d-4750-b197-f9b711174d37_800x399.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FHCl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84bfb8f3-db7d-4750-b197-f9b711174d37_800x399.png 424w, https://substackcdn.com/image/fetch/$s_!FHCl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84bfb8f3-db7d-4750-b197-f9b711174d37_800x399.png 848w, https://substackcdn.com/image/fetch/$s_!FHCl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84bfb8f3-db7d-4750-b197-f9b711174d37_800x399.png 1272w, https://substackcdn.com/image/fetch/$s_!FHCl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84bfb8f3-db7d-4750-b197-f9b711174d37_800x399.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FHCl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84bfb8f3-db7d-4750-b197-f9b711174d37_800x399.png" width="800" height="399" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/84bfb8f3-db7d-4750-b197-f9b711174d37_800x399.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:399,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!FHCl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84bfb8f3-db7d-4750-b197-f9b711174d37_800x399.png 424w, https://substackcdn.com/image/fetch/$s_!FHCl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84bfb8f3-db7d-4750-b197-f9b711174d37_800x399.png 848w, https://substackcdn.com/image/fetch/$s_!FHCl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84bfb8f3-db7d-4750-b197-f9b711174d37_800x399.png 1272w, https://substackcdn.com/image/fetch/$s_!FHCl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84bfb8f3-db7d-4750-b197-f9b711174d37_800x399.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Install Upgrade Asistant</figcaption></figure></div><p>With the assistant downloaded, follow the instructions to get it installed. This may include having to restart the IDE to get the extension installed and loaded for use.</p><blockquote><p>Please note that the Upgrade Assistant is available as both a Visual Studio extension and a command-line utility. In this blog, we will only cover using the Visual Studio extension.</p></blockquote><h2>Explode Geometry Project .NET Upgrade</h2><p>Let's work with our [ADN Explode Geometry Sample] (GitHub link) available on GitHub and see how we can get it building and running in .NET 8.</p><h3>Fetching the project</h3><p>This is an easy step: clone the repo <em>(SSH, HTTPS, ZIP, or any format you prefer)</em>. Note that our GitHub repo is already updated, so fetch the older tagged version for the 2025 code for the sample.</p><pre><code>git clone --branch v2.7 https://github.com/ADN-DevTech/3dsMax-Explode-Geometry
cd 3dsMax-Explode-Geometry</code></pre><p>In the Source folder, open the .csproj in Visual Studio.</p><h3>Upgrading to .NET the Assistant way</h3><p>As detailed in the Microsoft blog on the assistant, we can upgrade by right-clicking the project and selecting Upgrade. This opens a new project upgrade page.</p><p>Here, we select the project upgrade way and work with the <em><strong>in-place project upgrade,</strong></em> but feel free to test the other options if they suit your needs.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3W_C!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3de06d9-f8e3-451e-976f-91e501185774_800x337.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3W_C!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3de06d9-f8e3-451e-976f-91e501185774_800x337.png 424w, https://substackcdn.com/image/fetch/$s_!3W_C!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3de06d9-f8e3-451e-976f-91e501185774_800x337.png 848w, https://substackcdn.com/image/fetch/$s_!3W_C!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3de06d9-f8e3-451e-976f-91e501185774_800x337.png 1272w, https://substackcdn.com/image/fetch/$s_!3W_C!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3de06d9-f8e3-451e-976f-91e501185774_800x337.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3W_C!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3de06d9-f8e3-451e-976f-91e501185774_800x337.png" width="800" height="337" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f3de06d9-f8e3-451e-976f-91e501185774_800x337.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:337,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!3W_C!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3de06d9-f8e3-451e-976f-91e501185774_800x337.png 424w, https://substackcdn.com/image/fetch/$s_!3W_C!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3de06d9-f8e3-451e-976f-91e501185774_800x337.png 848w, https://substackcdn.com/image/fetch/$s_!3W_C!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3de06d9-f8e3-451e-976f-91e501185774_800x337.png 1272w, https://substackcdn.com/image/fetch/$s_!3W_C!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3de06d9-f8e3-451e-976f-91e501185774_800x337.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Selecting the .NET version</figcaption></figure></div><p>With the <em>upgrade type</em> ironed out, next, we need to select the <em>.NET version</em> to upgrade to. For our case, we are going for <em><strong>.NET 8.0 LTS;</strong></em> this is the expectation for 3ds Max 2026 .NET plugins. You may optionally get an action requirement to download and install <em>.NET SDK</em> if you don't have it yet; if so, proceed with that installation, then pick it up from here once done.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-d-a!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5bcce68-9284-4534-86c2-996031715841_800x349.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-d-a!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5bcce68-9284-4534-86c2-996031715841_800x349.png 424w, https://substackcdn.com/image/fetch/$s_!-d-a!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5bcce68-9284-4534-86c2-996031715841_800x349.png 848w, https://substackcdn.com/image/fetch/$s_!-d-a!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5bcce68-9284-4534-86c2-996031715841_800x349.png 1272w, https://substackcdn.com/image/fetch/$s_!-d-a!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5bcce68-9284-4534-86c2-996031715841_800x349.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-d-a!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5bcce68-9284-4534-86c2-996031715841_800x349.png" width="800" height="349" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c5bcce68-9284-4534-86c2-996031715841_800x349.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:349,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!-d-a!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5bcce68-9284-4534-86c2-996031715841_800x349.png 424w, https://substackcdn.com/image/fetch/$s_!-d-a!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5bcce68-9284-4534-86c2-996031715841_800x349.png 848w, https://substackcdn.com/image/fetch/$s_!-d-a!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5bcce68-9284-4534-86c2-996031715841_800x349.png 1272w, https://substackcdn.com/image/fetch/$s_!-d-a!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5bcce68-9284-4534-86c2-996031715841_800x349.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">.NET version</figcaption></figure></div><p>In the next step, we are required to select the files to upgrade. By default, all files in the project are checked, including the project file. Let's leave it as is and proceed with upgrading the selection. After the process completes, if no errors come up, then our project will have been transformed.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!02Rd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc029e1-d5de-4e5a-a373-420b00e0ec53_1565x825.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!02Rd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc029e1-d5de-4e5a-a373-420b00e0ec53_1565x825.png 424w, https://substackcdn.com/image/fetch/$s_!02Rd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc029e1-d5de-4e5a-a373-420b00e0ec53_1565x825.png 848w, https://substackcdn.com/image/fetch/$s_!02Rd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc029e1-d5de-4e5a-a373-420b00e0ec53_1565x825.png 1272w, https://substackcdn.com/image/fetch/$s_!02Rd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc029e1-d5de-4e5a-a373-420b00e0ec53_1565x825.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!02Rd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc029e1-d5de-4e5a-a373-420b00e0ec53_1565x825.png" width="1565" height="825" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0fc029e1-d5de-4e5a-a373-420b00e0ec53_1565x825.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:825,&quot;width&quot;:1565,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Upgrade completion status. Some warnings for now, nothing to worry about.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Upgrade completion status. Some warnings for now, nothing to worry about." title="Upgrade completion status. Some warnings for now, nothing to worry about." srcset="https://substackcdn.com/image/fetch/$s_!02Rd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc029e1-d5de-4e5a-a373-420b00e0ec53_1565x825.png 424w, https://substackcdn.com/image/fetch/$s_!02Rd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc029e1-d5de-4e5a-a373-420b00e0ec53_1565x825.png 848w, https://substackcdn.com/image/fetch/$s_!02Rd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc029e1-d5de-4e5a-a373-420b00e0ec53_1565x825.png 1272w, https://substackcdn.com/image/fetch/$s_!02Rd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc029e1-d5de-4e5a-a373-420b00e0ec53_1565x825.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Upgrade completion status. Some warnings for now, nothing to worry about.</figcaption></figure></div><p>Probably, the project will try to build but fail; we will fix that shortly. To check the logs, switch the combo box as shown below to the Upgrade Assistant option.</p><p>At the end of this process, you should have a project file <code>`.csproj`</code> that has been updated to conform to the .NET 8 project format. Running the build fails due to missing references to the Autodesk assemblies that may have been dropped in the upgrade process. Don't worry; fixing this is easy!</p><p>To fix the missing Autodesk assemblies or any other internal or 3rd party assemblies, we can achieve that either by;</p><ul><li><p>Directly edit the <code>csproj</code> file by copying the <em>ItemGroup</em> for the assemblies from the old project file and updating where necessary. For instance, for the Autodesk.Max.dll assembly, we can have it included in the new project file as shown below, with the version updated to 2026, of course. The same will be done for the other project dependencies like <code>CSharpUtilities.dll, ManagedServices.dll</code>, etc.</p></li></ul><pre><code>&lt;ItemGroup&gt;
  &lt;Reference Include="Autodesk.Max"&gt;
    &lt;HintPath&gt;$(ADSK_3DSMAX_x64_2026)\Autodesk.Max.dll&lt;/HintPath&gt;
    &lt;Private&gt;False&lt;/Private&gt;
  &lt;/Reference&gt; .... other assemblies
&lt;/ItemGroup&gt;</code></pre><ul><li><p>Another way is making use of the Visual Studio Reference Manager that is accessed from the <strong><code>Menu &gt; Project &gt; Add Project Reference &gt; Browse</code></strong> &amp; find your assemblies. Once all intended libraries are selected, click OK to add them. Just as in the option above, the new assemblies will be added back into the project file. Just FYI, we can change the assembly paths for the Autodesk assemblies to make use of the path variables. This way, any user can use our plugin despite the installation path they chose. For our case, we will use the path as<em><strong> $(ADSK_3DSMAX_x64_2026)\&lt;assembly name&gt;</strong></em>.&nbsp;</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!AVAY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febe83595-e3f3-4e80-a070-a76ba584b143_800x399.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!AVAY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febe83595-e3f3-4e80-a070-a76ba584b143_800x399.png 424w, https://substackcdn.com/image/fetch/$s_!AVAY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febe83595-e3f3-4e80-a070-a76ba584b143_800x399.png 848w, https://substackcdn.com/image/fetch/$s_!AVAY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febe83595-e3f3-4e80-a070-a76ba584b143_800x399.png 1272w, https://substackcdn.com/image/fetch/$s_!AVAY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febe83595-e3f3-4e80-a070-a76ba584b143_800x399.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!AVAY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febe83595-e3f3-4e80-a070-a76ba584b143_800x399.png" width="800" height="399" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ebe83595-e3f3-4e80-a070-a76ba584b143_800x399.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:399,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!AVAY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febe83595-e3f3-4e80-a070-a76ba584b143_800x399.png 424w, https://substackcdn.com/image/fetch/$s_!AVAY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febe83595-e3f3-4e80-a070-a76ba584b143_800x399.png 848w, https://substackcdn.com/image/fetch/$s_!AVAY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febe83595-e3f3-4e80-a070-a76ba584b143_800x399.png 1272w, https://substackcdn.com/image/fetch/$s_!AVAY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febe83595-e3f3-4e80-a070-a76ba584b143_800x399.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Add ASM reference.</figcaption></figure></div><p>At this stage, we can build our project, and everything should now be okay. But, looking at the logs, our build outputs are going to the default Visual Studio project build directory and not the one defined under our <em><strong>debug/release</strong></em> property group. In .NET Framework project configuration, we had defined <em><strong>&lt;OutputPath&gt;</strong></em> inside each build type (Build/Debug); in .NET, we define it inside the first global <em>&lt;PropertyGroup&gt;</em> where we have our project defaults, as shown below:</p><pre><code>&lt;PropertyGroup&gt;
  &lt;TargetFramework&gt;net8.0-windows&lt;/TargetFramework&gt;&nbsp;&nbsp;
  ... the other config here
  &lt;OutputPath&gt;C:\Our\intended\path\for\the\assembly&lt;/OutputPath&gt;&nbsp;
&lt;/PropertyGroup&gt;</code></pre><p>For our case, we will be using the Autodesk plugin path as our output path. This way, any plugin development changes can be updated in the plugin directory for testing.</p><p>Note: There may be extra steps to getting the assembly loaded into 3ds Max. If using <em><strong>PackageContents.xml</strong></em>, update where necessary to get the assembly and its dependencies loaded. Refer to the following documentation pages for help: <a href="https://help.autodesk.com/view/MAXDEV/2025/ENU/?guid=menu_migration_guide">Menu System Migration</a>, <a href="https://help.autodesk.com/view/MAXDEV/2025/ENU/?guid=packaging_plugins">Packaging Samples</a>&nbsp;</p><h3>Final Housekeeping&nbsp;</h3><p>As a close, we need to do a bit of housekeeping. Here are a few things to consider:</p><ul><li><p>Setting included assemblies as not private: When we add assembly references in our project, the build process ends up copying the assembly references and their dependencies to the `OutputPath` directory, which is not the intended behavior. This results in duplication of assemblies, which may cause 3ds Max to crash. To resolve this, update each of the included assemblies as follows:</p></li></ul><pre><code>&lt;Reference Include="ManagedServices"&gt;&nbsp;
  &lt;HintPath&gt; $(ADSK_3DSMAX_x64_2026)\ManagedServices.dll &lt;/HintPath&gt;&nbsp;&nbsp;
  &lt;Private&gt; false &lt;/Private&gt;&nbsp;
&lt;/Reference&gt;&nbsp;</code></pre><ul><li><p>Disabling <code>AppendTargetFrameworkToOutputPath</code>&nbsp;flag: Since plugins for 3ds Max are meant for Windows, we can disable the flag above to ensure the assemblies don't end up nested in a target framework directory.</p></li></ul><pre><code>&lt;AppendTargetFrameworkToOutputPath&gt; false &lt;/AppendTargetFrameworkToOutputPath&gt;</code></pre><ul><li><p>Updating/bumping up plugin versions. This applies to both ApplicationVersion and AssemblyVersion</p></li><li><p>Cleaning up some left-behind <code>&lt;ItemGroup&gt;</code> locks, which are not required in .NET 8.</p></li><li><p>Since our plugin is intended to run only on the Windows platform, expect to see lots of <a href="https://learn.microsoft.com/en-us/dotnet/core/compatibility/code-analysis/5.0/ca1416-platform-compatibility-analyzer">warnings on platform compatibility</a>. We can safely ignore these warnings using the snippet below:</p></li></ul><pre><code>&lt;NoWarn&gt;$(NoWarn);CA1416&lt;/NoWarn&gt;</code></pre><p>You can find the complete upgraded sample on our <a href="https://github.com/ADN-DevTech/3dsMax-Explode-Geometry/tree/v2.8">ADN GitHub repo</a> for reference.</p><div><hr></div><p>What did I miss out on? Please let us know! Happy upgrading: smile:!</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ujtT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85892715-d98d-4c78-86a6-6ddfbf2dc43c_2736x1669.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ujtT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85892715-d98d-4c78-86a6-6ddfbf2dc43c_2736x1669.png 424w, https://substackcdn.com/image/fetch/$s_!ujtT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85892715-d98d-4c78-86a6-6ddfbf2dc43c_2736x1669.png 848w, https://substackcdn.com/image/fetch/$s_!ujtT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85892715-d98d-4c78-86a6-6ddfbf2dc43c_2736x1669.png 1272w, https://substackcdn.com/image/fetch/$s_!ujtT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85892715-d98d-4c78-86a6-6ddfbf2dc43c_2736x1669.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ujtT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85892715-d98d-4c78-86a6-6ddfbf2dc43c_2736x1669.png" width="2000" height="1220" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/85892715-d98d-4c78-86a6-6ddfbf2dc43c_2736x1669.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1220,&quot;width&quot;:2000,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!ujtT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85892715-d98d-4c78-86a6-6ddfbf2dc43c_2736x1669.png 424w, https://substackcdn.com/image/fetch/$s_!ujtT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85892715-d98d-4c78-86a6-6ddfbf2dc43c_2736x1669.png 848w, https://substackcdn.com/image/fetch/$s_!ujtT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85892715-d98d-4c78-86a6-6ddfbf2dc43c_2736x1669.png 1272w, https://substackcdn.com/image/fetch/$s_!ujtT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85892715-d98d-4c78-86a6-6ddfbf2dc43c_2736x1669.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Side by side (triangle vs polygon) explode geometry results.</figcaption></figure></div>]]></content:encoded></item><item><title><![CDATA[What's new in 3ds Max 2026?]]></title><description><![CDATA[A few weeks ago, 3ds Max 2026 saw its official release, introducing new features and capabilities for both artists and developers.]]></description><link>https://www.codeart.co.ke/p/whats-new-in-3ds-max-2026</link><guid isPermaLink="false">https://www.codeart.co.ke/p/whats-new-in-3ds-max-2026</guid><dc:creator><![CDATA[Allan K. Koech]]></dc:creator><pubDate>Fri, 30 May 2025 21:00:27 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/1a1c5ed2-8036-48a9-b2f5-0efd206a1157_1503x735.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LKBZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24891bb3-da2d-4b75-988b-2039934b5277_1503x735.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LKBZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24891bb3-da2d-4b75-988b-2039934b5277_1503x735.jpeg 424w, https://substackcdn.com/image/fetch/$s_!LKBZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24891bb3-da2d-4b75-988b-2039934b5277_1503x735.jpeg 848w, https://substackcdn.com/image/fetch/$s_!LKBZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24891bb3-da2d-4b75-988b-2039934b5277_1503x735.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!LKBZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24891bb3-da2d-4b75-988b-2039934b5277_1503x735.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LKBZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24891bb3-da2d-4b75-988b-2039934b5277_1503x735.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/24891bb3-da2d-4b75-988b-2039934b5277_1503x735.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!LKBZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24891bb3-da2d-4b75-988b-2039934b5277_1503x735.jpeg 424w, https://substackcdn.com/image/fetch/$s_!LKBZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24891bb3-da2d-4b75-988b-2039934b5277_1503x735.jpeg 848w, https://substackcdn.com/image/fetch/$s_!LKBZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24891bb3-da2d-4b75-988b-2039934b5277_1503x735.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!LKBZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24891bb3-da2d-4b75-988b-2039934b5277_1503x735.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><figcaption class="image-caption">Image courtesy of Mohanad Hossam</figcaption></figure></div><p>A few weeks ago, 3ds Max 2026 saw its official release, introducing new features and capabilities for both artists and developers. With this release, 3ds Max saw many upgrades in <em>Arnold to 5.8</em>, <em>Substance to 3.0.8</em>, <em>performance improvements</em> in different modifiers, and much more. Refer to the <a href="https://help.autodesk.com/view/MAXDEV/2026/ENU/?guid=whats_new_3dsmax_2026_sdk">release page</a> for a detailed feature change for 3ds Max 2026.</p><div><hr></div><p>Originally Posted At:</p><blockquote><h4><a href="https://blog.autodesk.io/whats-new-in-3ds-max-2026/">Autodesk Developer BlogWhat&#8217;s new in 3ds Max 2026?</a></h4></blockquote><div><hr></div><h2>SDK Changes</h2><p>First, let's discuss the SDK changes that came with 3ds Max 2025 last year. We didn't have full information then, as there were breaking changes that are now fully documented.</p><ul><li><p>&nbsp;3ds Max 2025 came with a revamped new menu system. The update necessitates the rewriting of all plugins that generate menus or quad menus to align with 3ds Max 2025. Check out more information on the <a href="https://help.autodesk.com/view/MAXDEV/2025/ENU/?guid=GUID-FF48D0EC-6669-4EC7-AB43-E9998A14A198">menu changes</a>, <a href="https://help.autodesk.com/view/MAXDEV/2026/ENU/?guid=menu_migration_guide">menu migration guide</a>, and <a href="https://codeart.co.ke/porting-plugins-from-3ds-max-2024-to-3ds-max-2025/">general plugin migration guide to 2025 using the Explode Geometry Sample</a>.</p></li></ul><p>With that out of the way, what's new in 2026?</p><ul><li><p>NET 8.0 Support: In 2026, .NET 8 support for plugin development was introduced to align with Autodesk's goal of updating all our projects to more modern standards. This upgrade is a shift from the framework-based projects of previous 3ds Max versions; as such, this breaking change will require project restructuring and rebuilding to work in 2026. Refer to our <a href="https://codeart.co.ke/upgrading-3dsmax-net-framework-projects-to-net8/">porting guide from .NET Framework to .NET for plugins</a>.</p></li><li><p>Lots of minor API changes in ActionTable, Animatable, CustCont, ColorManagement, Menu System, etc. for performance and/or added functionality.</p></li><li><p>SDK licensing updates: SDK header licenses have been updated for conformity to the Autodesk license agreement, which is provided at the time of installation or download.</p></li><li><p>Updated samples and documentation to reflect API changes.</p></li></ul><p>Refer to our <a href="https://help.autodesk.com/view/MAXDEV/2026/ENU/?guid=whats_new_3dsmax_2026_sdk">What's New</a> docs section for a full list of API &amp; SDK changes in 2026.</p>]]></content:encoded></item><item><title><![CDATA[Porting Plugins from 3ds Max 2024 to 3ds Max 2025]]></title><description><![CDATA[3ds Max 2025 came with some breaking changes that marked an era of new approaches aimed at improving performance at the core.]]></description><link>https://www.codeart.co.ke/p/porting-plugins-from-3ds-max-2024-to-3ds-max-2025</link><guid isPermaLink="false">https://www.codeart.co.ke/p/porting-plugins-from-3ds-max-2024-to-3ds-max-2025</guid><dc:creator><![CDATA[Allan K. Koech]]></dc:creator><pubDate>Fri, 30 May 2025 06:54:49 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/b252223d-dbed-4982-ab84-730ea45fe816_1040x450.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!9IkT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d90aae3-8f6f-4595-874c-4d3049034934_1040x450.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!9IkT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d90aae3-8f6f-4595-874c-4d3049034934_1040x450.jpeg 424w, https://substackcdn.com/image/fetch/$s_!9IkT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d90aae3-8f6f-4595-874c-4d3049034934_1040x450.jpeg 848w, https://substackcdn.com/image/fetch/$s_!9IkT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d90aae3-8f6f-4595-874c-4d3049034934_1040x450.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!9IkT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d90aae3-8f6f-4595-874c-4d3049034934_1040x450.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!9IkT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d90aae3-8f6f-4595-874c-4d3049034934_1040x450.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9d90aae3-8f6f-4595-874c-4d3049034934_1040x450.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!9IkT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d90aae3-8f6f-4595-874c-4d3049034934_1040x450.jpeg 424w, https://substackcdn.com/image/fetch/$s_!9IkT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d90aae3-8f6f-4595-874c-4d3049034934_1040x450.jpeg 848w, https://substackcdn.com/image/fetch/$s_!9IkT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d90aae3-8f6f-4595-874c-4d3049034934_1040x450.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!9IkT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d90aae3-8f6f-4595-874c-4d3049034934_1040x450.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><figcaption class="image-caption">Image courtesy of Autodesk 3ds Max Announcement</figcaption></figure></div><p>3ds Max 2025 came with some breaking changes that marked an era of new approaches aimed at improving performance at the core. As a result, some tools tailored to pre-2025 3ds Max versions may fail to load into 2025. In this blog, we will give you a walkthrough of how to handle porting a plugin to 2025, using one of our samples as a base example.</p><div><hr></div><p>Originally Posted:</p><blockquote><h4><a href="https://blog.autodesk.io/porting-plugins-from-3ds-max-2024-to-3ds-max-2025/">Autodesk Developer BlogPorting Plugins from 3ds Max 2024 to 3ds Max 2025</a></h4></blockquote><div><hr></div><h2>What Changed in 2025?</h2><p>2025 brings some exciting changes; here are some highlights:</p><ul><li><p><em><strong>Menu Changes</strong></em>: 3ds Max 2025 introduces an overhaul of the menu system, deprecating the old <code>MenuManager (menuMan)</code> and introducing the new CuiMenuManager and its associated classes. This brings more ways of creating menus, statically or dynamically.</p></li><li><p><em><strong>shape.h</strong></em>: Introduced 8 helper methods for enhanced Bezier shape handling and end cap material options to improve shape rendering and control.</p></li><li><p><em><strong>spline3d.h</strong></em>: Added methods for checking spline segment linearity , managing angle constraints, and enhancing spline manipulation capabilities.</p></li><li><p><em><strong>splshape.h</strong></em>: Implemented methods to override spline closure prompts, providing customizable control over user interface prompts.</p></li><li><p><em><strong>mnmesh.h</strong></em>: Introduced new classes and optimizations for vertex origins and mesh management, focusing on performance improvements with complex meshes.</p></li><li><p><em><strong>Mesh.h</strong></em>: We have added a helper method in <em>ScopedLockChannels</em> to efficiently update transformed bounding boxes, thereby enhancing mesh rendering accuracy.</p></li><li><p><em><strong>MaxGuid.h</strong></em>: Introduced the MaxGuid class for GUID management within MaxSDK, facilitating efficient unique identifier handling.</p></li><li><p><em><strong>iparamm2.h</strong></em>: A virtual SetRange method has been added for defining UI control ranges, enabling flexible parameter range customization.</p></li><li><p><em><strong>imorpher.h</strong></em>: Established interface classes for Morpher modifiers, expanding morphing capabilities with multiple channels of geometric transformation control.</p></li><li><p><em><strong>control.h</strong></em>: Implemented the GetDefaultController method to select appropriate user controls based on parameter types, ensuring efficient and accurate UI element assignment.</p></li></ul><p>Now that you have seen some SDK changes in 2025, let's see how you would <em>port a 2024 sample to work with 3ds Max 2025</em>.</p><h2>Porting the Explode Geometry Sample</h2><p>The Explode Geometry Sample is one of the sample plugins maintained by the <a href="https://aps.autodesk.com/adn">ADN team</a>, with the installer available on the <a href="https://apps.autodesk.com/3DSMAX/en/Detail/Index?id=2091841125298582567&amp;appLang=en&amp;os=Win64">Autodesk App Store</a> and the source code hosted on the <a href="https://github.com/ADN-DevTech">ADN GitHub repo</a>. To begin the porting process, let's begin with fetching the code we will be working with.</p><h3>Fetching the source code</h3><p>Using your favourite tool, whether<em> GitHub Desktop, SourceTree, the command line</em>, etc., clone the Explode Geometry Sample. Note that you need to clone on the version tag indicated below, as the code has been updated already to support newer versions of 3ds Max.</p><pre><code>// Lets fetch code from v2.6 tag which supports 3ds Max 2024
// https://github.com/ADN-DevTech/3dsMax-Explode-Geometry/releases/tag/v2.6
git clone --branch v2.6 https://github.com/ADN-DevTech/3dsMax-Explode-Geometry.git
cd 3dsMax-Explode-Geometry
</code></pre><h3>Updating to 2025 Assembly Versions</h3><p>First, let's start by updating the assembly reference version to 3ds Max 2025 as well as setting our assembly output directory to 2025. In the <em><strong>.csproj</strong></em> project file, update the occurrence of 2024 to 2025, the assumption here being that you already have the 3ds Max 2025 setup installed.</p><p>With the project version updated, another interesting section to update is in our <em><code>AssemblyInfo.cs</code></em> file. Here we have predefined assembly versions and other organizational details, which may need to be bumped up to match the 3ds Max 2025 version.</p><pre><code>// Update the following two lines if need be
[assembly: AssemblyVersion("2.7.0.0")]
[assembly: AssemblyFileVersion("2.7.0.0")]</code></pre><p>At this point, we have all versioning details updated and ready to work on 2025 feature changes. First, let's look at our core functionality.</p><blockquote><p>Note: The system requirements for .NET are v4.8, if you have any build issues with version incompatibility, switch to v4.8.1.</p></blockquote><h3>MNVert Changes</h3><p>As noted earlier about some small changes to the C++ API that affect .NET-generated code, MNVert is among the victims of performance optimization. With the C++ API dropping the original copy operator for the C++ default copy, making MNVert trivially copiable and allocatable. This subtle change that works the same on the C++ side results in missing the `<em>Assign</em>` operator on the .NET `MNVert`.</p><p>Below is the final output for the 2024 vs. 2025 `<em>ExplodeGeometry</em>` with the MNVert functionality unchanged. You will note that the 2025 version shows no shapes on the scene as the Assign method is missing, which assigns vertex points to the `<em>polyNewFace</em>` which we are using to create new faces.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!9U9M!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cbec2c7-0019-46a9-a337-171f434a6aee_800x253.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!9U9M!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cbec2c7-0019-46a9-a337-171f434a6aee_800x253.png 424w, https://substackcdn.com/image/fetch/$s_!9U9M!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cbec2c7-0019-46a9-a337-171f434a6aee_800x253.png 848w, https://substackcdn.com/image/fetch/$s_!9U9M!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cbec2c7-0019-46a9-a337-171f434a6aee_800x253.png 1272w, https://substackcdn.com/image/fetch/$s_!9U9M!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cbec2c7-0019-46a9-a337-171f434a6aee_800x253.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!9U9M!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cbec2c7-0019-46a9-a337-171f434a6aee_800x253.png" width="800" height="253" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7cbec2c7-0019-46a9-a337-171f434a6aee_800x253.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:253,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!9U9M!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cbec2c7-0019-46a9-a337-171f434a6aee_800x253.png 424w, https://substackcdn.com/image/fetch/$s_!9U9M!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cbec2c7-0019-46a9-a337-171f434a6aee_800x253.png 848w, https://substackcdn.com/image/fetch/$s_!9U9M!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cbec2c7-0019-46a9-a337-171f434a6aee_800x253.png 1272w, https://substackcdn.com/image/fetch/$s_!9U9M!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cbec2c7-0019-46a9-a337-171f434a6aee_800x253.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Visual changes in 3ds Max 2024 and 3ds Max 2025</figcaption></figure></div><p>&nbsp;How can we remedy this? A quick fix is as shown below:</p><pre><code>// Original code ..
polyNewFace.Mesh.V(k).Assign(vert);
// New code ...
polyNewFace.Mesh.V(k).P = vert.P;</code></pre><blockquote><p>Note that the method `<em><strong>Assign</strong></em>` still works on the `<em>MNVert</em>`, but this originates from the `FlagUser`, where `MNVert` derives from.</p></blockquote><p>With that fixed, let's have a look at the menu system changes and how to effect those changes in our sample.</p><h3>Upgrading to the new Menu System</h3><p>Unfortunately, we will go over all the menu changes and how to make use of them here, but you can find a detailed guide on the following topic below:</p><ul><li><p>The Menu System: <a href="https://help.autodesk.com/view/MAXDEV/2025/ENU/?guid=GUID-FF48D0EC-6669-4EC7-AB43-E9998A14A198">https://help.autodesk.com/view/MAXDEV/2025/ENU/?guid=GUID-FF48D0EC-6669-4EC7-AB43-E9998A14A198</a> &amp; <a href="https://help.autodesk.com/view/MAXDEV/2025/ENU/?guid=menu_system">https://help.autodesk.com/view/MAXDEV/2025/ENU/?guid=menu_system</a></p></li><li><p>Menu Migration Guide: <a href="https://help.autodesk.com/view/MAXDEV/2025/ENU/?guid=menu_migration_guide">https://help.autodesk.com/view/MAXDEV/2025/ENU/?guid=menu_migration_guide</a></p></li></ul><p>&nbsp;The sample packs a few useful packaging scripts and configuration files. This is where we will be modifying to set up the menu changes. But first, why do we need to make use of the menu in this sample?</p><p>The sample is distributed as part of the ADN samples through the Autodesk AppStore. As such, to invoke the sample:</p><ul><li><p><em>First, the assembly must be loaded to 3ds Max, which we are achieving by using the `PackageContents.xml` file. You can check the section on ` Packaging Your Project` in the Menu Migration Guide linked above.</em></p></li><li><p><em>Secondly, we expose the action items through MacroScript. This is not necessary, as we can make use of the Action table IDs directly to invoke the .NET methods.</em></p></li><li><p><em>Lastly, we create a menu entry under the `<code>App Store</code>` menu item on the main menu bar.</em></p></li></ul><p>With all that in mind, let's navigate to the `<em><code>PackageContents.xml</code></em>` located in the `<em><code>/Bundle2</code></em>` directory and start the packaging process upgrade.</p><h3>PackageContents.xml file</h3><p>First, let's expand the package runtime environment to support 2025. This can be done by updating the `<em><code>SeriesMax</code></em>` value to 2025. Similarly, let's expand the `<em><code>SeriesMax</code></em>` for `<em><code>macroscripts parts</code></em>` segment as shown below:</p><pre><code>&lt;RuntimeRequirements
&nbsp;&nbsp;&nbsp;&nbsp;OS="Win64"
&nbsp;&nbsp;&nbsp;&nbsp;Platform="3ds Max"
&nbsp;&nbsp;&nbsp;&nbsp;SeriesMin="2020"
&nbsp;&nbsp;&nbsp;&nbsp;SeriesMax="2025"
/&gt;
&lt;Components Description="macroscripts parts"&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;RuntimeRequirements ... SeriesMax="2025" /&gt;
&nbsp;&nbsp;&nbsp;&nbsp;...
&lt;/Components&gt;</code></pre><p>With MacroScript support for 2025 added, let's add .NET assembly as shown below.</p><pre><code>&lt;Components Description="assemblies parts"&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;RuntimeRequirements OS="Win64" Platform="3ds Max" SeriesMin="2025" SeriesMax="2025" /&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;ComponentEntry AppName="ADNExplodeGeometry" Version="2.6.0" ModuleName="./Contents/ManagedAssemblies/2025/ADNExplodeGeometry.dll" AppDescription="..." /&gt;
&lt;/Components&gt;</code></pre><p>Lastly, let's add a post-startup script for 2025 that will create the menu entry for us on 3ds Max boot. Don't worry for now; we'll create the actual file and its contents next.</p><pre><code>&lt;Components Description="post-start-up scripts parts"&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;RuntimeRequirements OS="Win64" Platform="3ds Max" SeriesMin="2025" SeriesMax="2025" /&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;ComponentEntry AppName="ADNExplodeGeometry" Version="2.6.0" ModuleName="./Contents/Post-Start-Up_Scripts/ADNGeometryExplodeSetupMenu2025.ms" AppDescription="Setup Menu to execute tool" /&gt;
&lt;/Components&gt;</code></pre><h3>&nbsp;Creating Menu Entries for 2025</h3><p>As we noted above in the `PackageContents.xml`, we need to add a new post-startup script that will handle menu creation for our package when it's loaded. We create a new file called ` <em>ADNGeometryExplodeSetupMenu2025.ms</em>` in the <em>`./Bundle2/Contents/Post-Start-Up_Scripts/</em>` directory. Unlike 3ds Max 2024 and earlier versions, 2025 uses callbacks to register new menu entries for both regular and quad menus.</p><p>Within the file, let's add code to register our callback as well as get the main menu bar handle, which we will use to add new menu or submenu entries.</p><pre><code>function menuCallback = (
&nbsp;&nbsp;&nbsp;&nbsp;local menuMgr = callbacks.notificationParam()
&nbsp;&nbsp;&nbsp;&nbsp;local mainMenuBar = menuMgr.mainMenuBar
)

-- Register Callback to the #CuiRegisterMenus
callbacks.removeScripts id:#adnExplodeGeomMenu
callbacks.addScript #cuiRegisterMenus menuCallback id:#adnExplodeGeomMenu</code></pre><p>From the callback, we can access the menu manager, which hosts the main menu bar. We'll use the main menu bar to add a sub-menu item, which will have our menu item. Let's see how this is attained:</p><pre><code>local stringAppStoreDefaultMenu = "App Store"
-- ADN Explode Geometry MXS
local stringMacroScriptName = "ADNExplodeGeomMS"
local stringMacroScriptCategory = "ADN Samples"
local helpMenuId = "cee8f758-2199-411b-81e7-d3ff4a80d143"

-- ID of the "App Store" menu in the menu bar. Use this ID when in need of adding new submenu items to the "App Store" menu entry.
local appStoreMenuId = "CC18FEFC-E8A4-4B16-B519-664E8FA3B549"
local newSubMenu = mainMenuBar.CreateSubMenu appStoreMenuId stringAppStoreDefaultMenu beforeId:helpMenuId

local separatorId = "96FB49D4-6263-4D8E-AC95-6D304673327B"
newSubMenu.CreateSeparator separatorId
local permanentId = stringMacroScriptName + "`" + stringMacroScriptCategory

-- 647394: MacroScript Action Table ID
newSubMenu.CreateAction "9DE5BDB9-E47F-4614-BDF8-C331B9B38C70" 647394 permanentId</code></pre><p>This script defines some local variables like the menu entry name and our menu name, as well as some system data like the `<em><strong>Help Menu</strong></em>` GUID. From the main menu bar, we create a submenu called `App Store` using <em><code>mainMenuBar.CreateSubMenu&#8291;. </code></em>In the submenu, we add a separator at the end of the existing menu items, if any (in our case it may be empty), and then we create a new action entry for our sample.</p><p>With the script completed, we have one last piece remaining: adding the built assembly to the package.</p><h3>Building &amp; Adding Assembly to the Package</h3><p>Under the `<em><code>PackageContents.xml</code></em>` section, we added a reference to a built assembly supporting 2025. From our upgraded project, we build our project, and the generated assembly is written out to our project's <em>`OutputPath</em>` directive. We can copy over this assembly to the <em>`<code>./Bundle2/Contents/ManagedAssemblies/2025/</code>`</em> directory.</p><p>The Bundle2 directory can be copied over as is now to the 3ds Max plugin directory, and on the next 3ds Max boot, the plugin will be loaded. For a full guide to plugin packaging, check the docs here: <a href="https://help.autodesk.com/view/MAXDEV/2025/ENU/?guid=packaging_plugins">https://help.autodesk.com/view/MAXDEV/2025/ENU/?guid=packaging_plugins</a>.</p><h2>Final touches</h2><p>To finalise the plugin details, we can do a couple of things:</p><ul><li><p>&nbsp;Update version information for the plugin, as encompassed in the `<em><code>PackageContents.xml</code></em>`</p></li><li><p><em>Documentation</em>: Updating the documentation details for new features, fixes, and/or new version information.</p></li></ul><p>We hope you found that helpful!</p>]]></content:encoded></item></channel></rss>