Skip to content

Commit a060425

Browse files
TMHSDigitalclaude
andcommitted
feat(gallery): supported-version line and a copyable falsifier on example detail pages
Each example detail page now shows, from examples/index.json: - "runs on": the smoked series at or above the row's min_version (4.5 LTS · 5.1 · 5.2 LTS), with "skips below X" when the floor is above 4.5 - "falsify it": the first exit-code falsifier as a copyable command under the run line, with the exit it must produce (and its own min_version when it has one) Showcase pieces have no index row and are unchanged apart from the shared detail JS, which now wires both copy buttons. 56 example pages carry both lines; check_site_links passes (139 pages, 0 failures). Closes #377 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 0066093 commit a060425

137 files changed

Lines changed: 1199 additions & 549 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎docs/gallery/anvil/index.html‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -269,10 +269,12 @@ <h3><a class="stretch" href="../wooden-barrel/">Wooden Barrel</a></h3>
269269
});
270270
});
271271
}
272-
var copy = document.getElementById('copyRun');
273-
if (copy) {
274-
wireCopy(copy, function () { return document.getElementById('runCmd').textContent; });
275-
}
272+
[['copyRun', 'runCmd'], ['copyFalsify', 'falsifyCmd']].forEach(function (ids) {
273+
var copy = document.getElementById(ids[0]);
274+
if (copy) {
275+
wireCopy(copy, function () { return document.getElementById(ids[1]).textContent; });
276+
}
277+
});
276278
Array.prototype.forEach.call(document.querySelectorAll('.md pre'), function (block) {
277279
var wrap = block;
278280
if (block.tagName === 'PRE') {

‎docs/gallery/apothecary-shelf/index.html‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -274,10 +274,12 @@ <h3><a class="stretch" href="../wooden-barrel/">Wooden Barrel</a></h3>
274274
});
275275
});
276276
}
277-
var copy = document.getElementById('copyRun');
278-
if (copy) {
279-
wireCopy(copy, function () { return document.getElementById('runCmd').textContent; });
280-
}
277+
[['copyRun', 'runCmd'], ['copyFalsify', 'falsifyCmd']].forEach(function (ids) {
278+
var copy = document.getElementById(ids[0]);
279+
if (copy) {
280+
wireCopy(copy, function () { return document.getElementById(ids[1]).textContent; });
281+
}
282+
});
281283
Array.prototype.forEach.call(document.querySelectorAll('.md pre'), function (block) {
282284
var wrap = block;
283285
if (block.tagName === 'PRE') {

‎docs/gallery/archery-target/index.html‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -276,10 +276,12 @@ <h3><a class="stretch" href="../wooden-barrel/">Wooden Barrel</a></h3>
276276
});
277277
});
278278
}
279-
var copy = document.getElementById('copyRun');
280-
if (copy) {
281-
wireCopy(copy, function () { return document.getElementById('runCmd').textContent; });
282-
}
279+
[['copyRun', 'runCmd'], ['copyFalsify', 'falsifyCmd']].forEach(function (ids) {
280+
var copy = document.getElementById(ids[0]);
281+
if (copy) {
282+
wireCopy(copy, function () { return document.getElementById(ids[1]).textContent; });
283+
}
284+
});
283285
Array.prototype.forEach.call(document.querySelectorAll('.md pre'), function (block) {
284286
var wrap = block;
285287
if (block.tagName === 'PRE') {

‎docs/gallery/armature-bend/index.html‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,16 @@ <h1>Armature Bend</h1>
5555
<p class="zoom-hint">Rendered headless by the example itself. Select it to enlarge.</p>
5656
<div class="callout"><span class="tag">witnesses</span> edit_bones exists only in edit mode (empty in object mode), vertex groups bind to bones strictly by name, and the armature modifier is exactly linear blend skinning: every evaluated vertex must equal Σ wᵢ·(pose_bone.matrix @ bone.matrix_local.inverted()) @ rest, root ring pinned, tip deflected — a straight tube fails.</div>
5757
<p class="taglist"><span class="tag">tags</span> <a href="../#tag=armature">armature</a> <a href="../#tag=depsgraph">depsgraph</a></p>
58+
<p class="taglist"><span class="tag">runs on</span> 4.5 LTS &middot; 5.1 &middot; 5.2 LTS</p>
5859
<div class="runline">
5960
<pre id="runCmd" tabindex="0">blender --background --python examples/armature-bend/armature_bend.py --</pre>
6061
<button class="copy-btn" id="copyRun" type="button">Copy</button>
6162
</div>
63+
<p class="taglist"><span class="tag">falsify it</span> Breaks the contract on purpose and must exit 8</p>
64+
<div class="runline falsify">
65+
<pre id="falsifyCmd" tabindex="0">blender --background --python examples/armature-bend/armature_bend.py -- --zero-curl</pre>
66+
<button class="copy-btn" id="copyFalsify" type="button">Copy</button>
67+
</div>
6268
<nav class="jumpnav" aria-label="On this page"><span class="tag">jump to</span> <a href="#run">Run</a> <a href="#exit-codes">Exit codes</a> <a href="#source">Source</a> <a href="#related">Related</a></nav>
6369
<section class="detail-section md">
6470
<p>A runnable example that rigs a tapered, ribbed bellows hose with a four-bone armature chain — <code>edit_bones</code> construction, name-bound vertex groups with smoothstep blend zones, a posed curl — and checks the depsgraph-evaluated result against closed-form linear blend skinning, following <a href="https://github.com/TMHSDigital/Blender-Developer-Tools/tree/main/skills/mesh-editing-and-bmesh/SKILL.md"><code>mesh-editing-and-bmesh</code></a> and <a href="https://github.com/TMHSDigital/Blender-Developer-Tools/tree/main/skills/depsgraph-and-evaluated-data/SKILL.md"><code>depsgraph-and-evaluated-data</code></a>.</p>
@@ -250,10 +256,12 @@ <h3><a class="stretch" href="../lod-decimate-chain/">LOD Decimate Chain</a></h3>
250256
});
251257
});
252258
}
253-
var copy = document.getElementById('copyRun');
254-
if (copy) {
255-
wireCopy(copy, function () { return document.getElementById('runCmd').textContent; });
256-
}
259+
[['copyRun', 'runCmd'], ['copyFalsify', 'falsifyCmd']].forEach(function (ids) {
260+
var copy = document.getElementById(ids[0]);
261+
if (copy) {
262+
wireCopy(copy, function () { return document.getElementById(ids[1]).textContent; });
263+
}
264+
});
257265
Array.prototype.forEach.call(document.querySelectorAll('.md pre'), function (block) {
258266
var wrap = block;
259267
if (block.tagName === 'PRE') {

‎docs/gallery/attribute-domain-shear/index.html‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,16 @@ <h1>Attribute Domain Shear</h1>
5555
<p class="zoom-hint">Rendered headless by the example itself. Select it to enlarge.</p>
5656
<div class="callout"><span class="tag">witnesses</span> Authoring per-face colors into a POINT-domain attribute overwrites shared vertices once per neighbor; the last write wins and intended colors shear across every shared vertex.</div>
5757
<p class="taglist"><span class="tag">tags</span> <a href="../#tag=attributes">attributes</a> <a href="../#tag=mesh">mesh</a> <a href="../#tag=rendering">rendering</a></p>
58+
<p class="taglist"><span class="tag">runs on</span> 4.5 LTS &middot; 5.1 &middot; 5.2 LTS</p>
5859
<div class="runline">
5960
<pre id="runCmd" tabindex="0">blender --background --python examples/attribute-domain-shear/attribute_domain_shear.py --</pre>
6061
<button class="copy-btn" id="copyRun" type="button">Copy</button>
6162
</div>
63+
<p class="taglist"><span class="tag">falsify it</span> Breaks the contract on purpose and must exit 5</p>
64+
<div class="runline falsify">
65+
<pre id="falsifyCmd" tabindex="0">blender --background --python examples/attribute-domain-shear/attribute_domain_shear.py -- --no-overwrite</pre>
66+
<button class="copy-btn" id="copyFalsify" type="button">Copy</button>
67+
</div>
6268
<nav class="jumpnav" aria-label="On this page"><span class="tag">jump to</span> <a href="#run">Run</a> <a href="#exit-codes">Exit codes</a> <a href="#source">Source</a> <a href="#related">Related</a></nav>
6369
<section class="detail-section md">
6470
<p>A runnable example that witnesses what <code>POINT</code> versus <code>CORNER</code> <strong>means</strong> on <code>Mesh.color_attributes</code> once the mesh has shared vertices — the domain is not a storage detail, it decides where colors can live. Companion to <a href="https://github.com/TMHSDigital/Blender-Developer-Tools/tree/main/examples/color-attribute-wheel"><code>color-attribute-wheel</code></a> (which covers <code>color_attributes.new()</code> versus the deprecated alias, CORNER sizing == <code>len(loops)</code>, and <code>active_color</code>); this example covers the trap one step later, when AI code knows the API exists but authors per-face colors into a <code>POINT</code>-domain attribute.</p>
@@ -249,10 +255,12 @@ <h3><a class="stretch" href="../custom-normals-shade/">Custom Normals Shade</a><
249255
});
250256
});
251257
}
252-
var copy = document.getElementById('copyRun');
253-
if (copy) {
254-
wireCopy(copy, function () { return document.getElementById('runCmd').textContent; });
255-
}
258+
[['copyRun', 'runCmd'], ['copyFalsify', 'falsifyCmd']].forEach(function (ids) {
259+
var copy = document.getElementById(ids[0]);
260+
if (copy) {
261+
wireCopy(copy, function () { return document.getElementById(ids[1]).textContent; });
262+
}
263+
});
256264
Array.prototype.forEach.call(document.querySelectorAll('.md pre'), function (block) {
257265
var wrap = block;
258266
if (block.tagName === 'PRE') {

‎docs/gallery/bake-normal-high-to-low/index.html‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,16 @@ <h1>Bake Normal High To Low</h1>
5555
<p class="zoom-hint">Rendered headless by the example itself. Select it to enlarge.</p>
5656
<div class="callout"><span class="tag">witnesses</span> Statistical gates not byte-identity: detail frac 0.7211 / MAD 0.09356 vs flat 0.0000 / 0.00277; --flat-source exits 5; type=NORMAL not bake_type; RNA identical on 4.5.11, 5.1.2, 5.2.1</div>
5757
<p class="taglist"><span class="tag">tags</span> <a href="../#tag=mesh">mesh</a></p>
58+
<p class="taglist"><span class="tag">runs on</span> 4.5 LTS &middot; 5.1 &middot; 5.2 LTS</p>
5859
<div class="runline">
5960
<pre id="runCmd" tabindex="0">blender --background --python examples/bake-normal-high-to-low/bake_normal_high_to_low.py --</pre>
6061
<button class="copy-btn" id="copyRun" type="button">Copy</button>
6162
</div>
63+
<p class="taglist"><span class="tag">falsify it</span> Breaks the contract on purpose and must exit 5</p>
64+
<div class="runline falsify">
65+
<pre id="falsifyCmd" tabindex="0">blender --background --python examples/bake-normal-high-to-low/bake_normal_high_to_low.py -- --flat-source</pre>
66+
<button class="copy-btn" id="copyFalsify" type="button">Copy</button>
67+
</div>
6268
<nav class="jumpnav" aria-label="On this page"><span class="tag">jump to</span> <a href="#run">Run</a> <a href="#exit-codes">Exit codes</a> <a href="#source">Source</a> <a href="#related">Related</a></nav>
6369
<section class="detail-section md">
6470
<p>A runnable example that cage-bakes a ribbed bronze hatch plate onto a <code>DECIMATE COLLAPSE</code> LOD and asserts the tangent-space normal map carries measurable surface detail — following <a href="https://github.com/TMHSDigital/Blender-Developer-Tools/tree/main/skills/bake-high-to-low/SKILL.md"><code>bake-high-to-low</code></a>.</p>
@@ -256,10 +262,12 @@ <h3><a class="stretch" href="../shape-key-blend/">Shape Key Blend</a></h3>
256262
});
257263
});
258264
}
259-
var copy = document.getElementById('copyRun');
260-
if (copy) {
261-
wireCopy(copy, function () { return document.getElementById('runCmd').textContent; });
262-
}
265+
[['copyRun', 'runCmd'], ['copyFalsify', 'falsifyCmd']].forEach(function (ids) {
266+
var copy = document.getElementById(ids[0]);
267+
if (copy) {
268+
wireCopy(copy, function () { return document.getElementById(ids[1]).textContent; });
269+
}
270+
});
263271
Array.prototype.forEach.call(document.querySelectorAll('.md pre'), function (block) {
264272
var wrap = block;
265273
if (block.tagName === 'PRE') {

‎docs/gallery/bamboo-clump/index.html‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -280,10 +280,12 @@ <h3><a class="stretch" href="../wooden-barrel/">Wooden Barrel</a></h3>
280280
});
281281
});
282282
}
283-
var copy = document.getElementById('copyRun');
284-
if (copy) {
285-
wireCopy(copy, function () { return document.getElementById('runCmd').textContent; });
286-
}
283+
[['copyRun', 'runCmd'], ['copyFalsify', 'falsifyCmd']].forEach(function (ids) {
284+
var copy = document.getElementById(ids[0]);
285+
if (copy) {
286+
wireCopy(copy, function () { return document.getElementById(ids[1]).textContent; });
287+
}
288+
});
287289
Array.prototype.forEach.call(document.querySelectorAll('.md pre'), function (block) {
288290
var wrap = block;
289291
if (block.tagName === 'PRE') {

‎docs/gallery/basketball-hoop/index.html‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -275,10 +275,12 @@ <h3><a class="stretch" href="../wooden-barrel/">Wooden Barrel</a></h3>
275275
});
276276
});
277277
}
278-
var copy = document.getElementById('copyRun');
279-
if (copy) {
280-
wireCopy(copy, function () { return document.getElementById('runCmd').textContent; });
281-
}
278+
[['copyRun', 'runCmd'], ['copyFalsify', 'falsifyCmd']].forEach(function (ids) {
279+
var copy = document.getElementById(ids[0]);
280+
if (copy) {
281+
wireCopy(copy, function () { return document.getElementById(ids[1]).textContent; });
282+
}
283+
});
282284
Array.prototype.forEach.call(document.querySelectorAll('.md pre'), function (block) {
283285
var wrap = block;
284286
if (block.tagName === 'PRE') {

‎docs/gallery/bmesh-gear/index.html‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,16 @@ <h1>BMesh Gear</h1>
5555
<p class="zoom-hint">Rendered headless by the example itself. Select it to enlarge.</p>
5656
<div class="callout"><span class="tag">witnesses</span> Parametric bmesh topology is exactly predictable: verts, edges, and faces match their closed forms, and every edge borders exactly two faces (watertight).</div>
5757
<p class="taglist"><span class="tag">tags</span> <a href="../#tag=mesh">mesh</a> <a href="../#tag=bmesh">bmesh</a></p>
58+
<p class="taglist"><span class="tag">runs on</span> 4.5 LTS &middot; 5.1 &middot; 5.2 LTS</p>
5859
<div class="runline">
5960
<pre id="runCmd" tabindex="0">blender --background --python examples/bmesh-gear/bmesh_gear.py --</pre>
6061
<button class="copy-btn" id="copyRun" type="button">Copy</button>
6162
</div>
63+
<p class="taglist"><span class="tag">falsify it</span> Breaks the contract on purpose and must exit 3</p>
64+
<div class="runline falsify">
65+
<pre id="falsifyCmd" tabindex="0">blender --background --python examples/bmesh-gear/bmesh_gear.py -- --no-extrude</pre>
66+
<button class="copy-btn" id="copyFalsify" type="button">Copy</button>
67+
</div>
6268
<nav class="jumpnav" aria-label="On this page"><span class="tag">jump to</span> <a href="#run">Run</a> <a href="#exit-codes">Exit codes</a> <a href="#source">Source</a> <a href="#related">Related</a></nav>
6369
<section class="detail-section md">
6470
<p>A runnable example that builds a 14-tooth gear entirely with bmesh — profile ring, face, <code>extrude_face_region</code>, translate — following the ownership contract from <a href="https://github.com/TMHSDigital/Blender-Developer-Tools/tree/main/skills/mesh-editing-and-bmesh/SKILL.md"><code>mesh-editing-and-bmesh</code></a> and the <a href="https://github.com/TMHSDigital/Blender-Developer-Tools/tree/main/rules/always-free-bmesh.mdc"><code>always-free-bmesh</code></a> rule: every <code>bmesh.new()</code> is paired with <code>bm.free()</code> in a <code>try</code>/<code>finally</code>.</p>
@@ -249,10 +255,12 @@ <h3><a class="stretch" href="../mesh-hygiene-audit/">Mesh Hygiene Audit</a></h3>
249255
});
250256
});
251257
}
252-
var copy = document.getElementById('copyRun');
253-
if (copy) {
254-
wireCopy(copy, function () { return document.getElementById('runCmd').textContent; });
255-
}
258+
[['copyRun', 'runCmd'], ['copyFalsify', 'falsifyCmd']].forEach(function (ids) {
259+
var copy = document.getElementById(ids[0]);
260+
if (copy) {
261+
wireCopy(copy, function () { return document.getElementById(ids[1]).textContent; });
262+
}
263+
});
256264
Array.prototype.forEach.call(document.querySelectorAll('.md pre'), function (block) {
257265
var wrap = block;
258266
if (block.tagName === 'PRE') {

‎docs/gallery/book-trolley/index.html‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -274,10 +274,12 @@ <h3><a class="stretch" href="../wooden-barrel/">Wooden Barrel</a></h3>
274274
});
275275
});
276276
}
277-
var copy = document.getElementById('copyRun');
278-
if (copy) {
279-
wireCopy(copy, function () { return document.getElementById('runCmd').textContent; });
280-
}
277+
[['copyRun', 'runCmd'], ['copyFalsify', 'falsifyCmd']].forEach(function (ids) {
278+
var copy = document.getElementById(ids[0]);
279+
if (copy) {
280+
wireCopy(copy, function () { return document.getElementById(ids[1]).textContent; });
281+
}
282+
});
281283
Array.prototype.forEach.call(document.querySelectorAll('.md pre'), function (block) {
282284
var wrap = block;
283285
if (block.tagName === 'PRE') {

0 commit comments

Comments
 (0)