Skip to content

Update to 1.21.110, improve touch UI#299

Merged
SuperLlama88888 merged 22 commits into
mainfrom
dev
Sep 27, 2025
Merged

Update to 1.21.110, improve touch UI#299
SuperLlama88888 merged 22 commits into
mainfrom
dev

Conversation

@SuperLlama88888

@SuperLlama88888 SuperLlama88888 commented Sep 24, 2025

Copy link
Copy Markdown
Owner

Vanilla resources are already cached and this just takes up lots of memory. Even when I add full resource pack support I think the memory impact outweights the performance benefits.
Inspired by PurpleRain-PR/Rainbow-Editor - thanks @PurpleRain-PR. Note that unlike Rainbow-Editor, it can only export the whole scene, not individual blocks.
Also flip the UVs manually rather than setting flipY on the texture - GLTFExporter doesn't recognise flipY.
esbuild can append file hashes to file names, preventing browsers from serving outdated scripts when HoloPrint updates.
Fixes #298
Caches will now look at older versions if they can and automatically delete unused entries.
This is a significant change and I haven't fully tested everything if anything's broken yet.
FINALLY they're adding in these missing stuff! Much more intuitive...
EntityGeoMaker is still in early days (can't handle nested rotations) but it works for copper golem statues so I'm happy.
Because it needs to load the geometry file, many things have been made async.
Also changed blockShapes.json #tex system to use <> instead of {} (looks more like templates) and improve types a bit.
Also make methods I made static last commit non-static again.
For blocks such as beds, chests and pistons, many variants use the exact same textures but in a different image file or position. HoloPrint now compares image fragments pixel-by-pixel and ignores ones that are duplicates of others. They are hashed first so the pixel-by-pixel comparisons are rare.
@coderabbitai

coderabbitai Bot commented Sep 24, 2025

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds a versioned lazy caching fetchers module; removes bespoke caching from ResourcePackStack and utils; introduces EntityGeoMaker and async-aware BlockGeoMaker; stages builds via a temp workspace with hashed outputs; adds GLB export and texture-atlas deduplication; updates schemas, types, data, imports, and many public signatures.

Changes

Cohort / File(s) Summary
Build & ignore
/.gitignore, pipeline/build.js
.gitignore now ignores **/node_modules repository-wide; build script stages src→temp, runs esbuild with hashed outputs into dist, derives externals from import maps, rewrites/injects import maps in HTML, and removes the temp workspace.
Global typings
/globalPatches.d.ts
Replaced an augmented String.startsWith conditional-type overload with a String.endsWith conditional-type overload using the same generic/conditional return form.
Fetchers & cache utilities
/src/fetchers.js, src/utils.js
Adds src/fetchers.js exporting lazy, versioned caching fetchers (vanillaData, bedrockData, bedrockBlockUpgradeSchema) with per-version Cache, change detection, retries; removes CachingFetcher from src/utils.js and adds lazyLoadAsyncFunctionFactory (duplicated declaration present).
Resource pack lifecycle & startup
/src/ResourcePackStack.js, src/index.js, tests/checkBlockTranslationCoverage.js
ResourcePackStack no longer extends AsyncFactory, constructor simplified and caching removed; fetchResource now merges JSON on-the-fly and delegates vanilla fetches to fetchers.vanillaData; code/tests now use new ResourcePackStack() and perform cache cleanup.
Data fetching & orchestration
/src/HoloPrint.js, src/components/ItemCriteriaInput.js, src/BlockUpdater.js, tests/*
Rewires data access to fetchers.*; makePack defaults resourcePackStack = new ResourcePackStack(); removes some bespoke fetcher factories; BlockUpdater stops extending AsyncFactory and uses fetchers.bedrockBlockUpgradeSchema.
Entity geometry integration
/src/EntityGeoMaker.js, src/BlockGeoMaker.js
Adds EntityGeoMaker (default export) with async entityModelToCubes(...); BlockGeoMaker now accepts an entityGeoMaker, several methods become async, introduces CubeWithEasyProperties typedef, supports copy_entity_model, and updates texture parsing to angle-bracket <...> syntax.
Rendering & export
/src/PreviewRenderer.js, src/translations/en_US.json, src/translations/zh_CN.json
PreviewRenderer adds exportGlb() (dynamically imports GLTFExporter) and a helper to expand instanced meshes; flips V coordinate in UVs and removes texture.flipY; adds preview.options.exportGlb translation keys.
Texture atlas & deduplication
/src/TextureAtlas.js, src/data/textureAtlasMappings.json
TextureAtlas replaces atlasWidth/atlasHeight with textureWidth/textureHeight, computes per-fragment pixel hash, deduplicates identical fragments (reuses UVs and zeroes duplicate sizes), and adds #hashPixels/#checkImageDataEquivalence; atlas mappings trimmed and flipbook list cleared.
Schemas & TypeScript types
/src/data/schemas/blockShapeGeos.schema.json, src/data/schemas/blockShapes.schema.json, src/data/schemas/index.ts
Adds copy_entity_model schema and textureFilePath definition, adds box_uv_flip_east_west, allows angle-bracket textured syntax in patterns, and overhauls TS exports with new Cube, EntityModelInfo, TextureFilePath, and BlockStateVariants types; index signatures updated.
Data assets & mappings
/src/data/blockShapeGeos.json, src/data/blockShapes.json, src/data/blockStateDefinitions.json, src/data/materialListMappings.json, src/data/textureAtlasMappings.json
Renames iron_barsmetal_bars; adds shapes (template_dried_ghast_tentacle, dried_ghast, shelf, copper_golem_statue); migrates many texture specifiers from {...} to <...>; adds rehydration_level state; adds "chain": "iron_chain" mapping; trims atlas patch entries.
Entity scripts export surface
/src/entityScripts.molang.js
Converts previously named exports (ACTIONS and helper functions) to internal symbols and exposes a single default export object aggregating them, removing named exports.
Preview & UI templates
/src/packTemplate/ui/*, src/packTemplate/ui/_global_variables.json
UI changes: added $holoprint:material_list_offset, adjusted touch button sizes and offsets; replaced menu input panel with hud_toggle/button variants; reworked material list wrapper and stacking/padding for touch controls (layout and anchoring changes).
Tests & README updates
/tests/*, README.md, README.zh-CN.md
Tests updated to use fetchers.vanillaData and synchronous ResourcePackStack; README and Chinese README updated credits/acknowledgments.

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning This pull request also introduces substantial out-of-scope changes including a complete rebuild of the pipeline with import map handling in pipeline/build.js, a swap of String.startsWith to endsWith in globalPatches.d.ts, a new fetchers.js caching module, extensive public API and schema expansions in src/data/schemas and index.ts, MoLang script export consolidation, utility refactoring in utils.js, and other broad refactors unrelated to version support, UI layout, or caching fixes. Please split the build pipeline overhaul, type and schema expansions, fetchers and utilities refactoring, and MoLang export changes into separate pull requests so this one can remain focused on version update, touch UI improvements, and caching fixes as defined by the linked issues.
✅ Passed checks (4 passed)
Check name Status Explanation
Title Check ✅ Passed The title “Update to 1.21.110, improve touch UI” directly references two significant aspects of the changeset—updating to a new Minecraft Bedrock version and enhancing the touch interface—which are genuine features of the PR. It is concise and clearly related to the code, even though it does not enumerate every update such as caching fixes or the new GLB export. This phrasing is sufficient for a teammate to understand the primary focus.
Linked Issues Check ✅ Passed The code changes satisfy issue #290 by adding new blockShape and blockShapeGeos entries for dried ghasts, copper blocks, and shelves and updating related geometry and schemas; they satisfy issue #297 via extensive touch UI rework in the UI templates and global variables; and they address issue #298 by adding cache cleanup hooks, introducing new fetchers, and disabling stale ResourcePackStack caches.
Description Check ✅ Passed The pull request description outlines the addition of dried ghasts, copper blocks, shelves, touch UI improvements, GLB exporting, caching behavior changes, texture deduplication, and resource pack cache disabling, which all correspond directly to the modifications in the code. It is clearly related to the changeset and summarizes the scope without drifting off-topic.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c861366 and 0b7bc32.

📒 Files selected for processing (2)
  • README.md (1 hunks)
  • README.zh-CN.md (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Test Sample Structures (Firefox)
  • GitHub Check: Test Sample Structures (Chrome)
🔇 Additional comments (2)
README.md (1)

69-70: Credits update looks good

Appreciate the added attribution for the new contributors.

README.zh-CN.md (1)

69-70: Acknowledgements update looks good

The new credit entries align with the PR scope and maintain consistent formatting with the surrounding list.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
pipeline/build.js (2)

122-122: Same regex issue with negated empty character class.

This line has the same [^] pattern issue as line 36.

Apply the same fix:

-code = code.replaceAll(/<script type="(importmap|application\/ld\+json)">([^]+?)<\/script>/g, (_, scriptType, json) => `<script type="${scriptType}">${processJSON(json).code}</script>`);
+code = code.replaceAll(/<script type="(importmap|application\/ld\+json)">([\s\S]+?)<\/script>/g, (_, scriptType, json) => `<script type="${scriptType}">${processJSON(json).code}</script>`);

171-171: Another instance of the negated empty character class pattern.

Line 171 also uses [^] in the template literal regex.

Update to use standard pattern:

-code = await replaceAllAsync(code, /html`([^]+?)`/g, async (_, html) => "`" + (await processHTML(html, filename)).code + "`");
+code = await replaceAllAsync(code, /html`([\s\S]+?)`/g, async (_, html) => "`" + (await processHTML(html, filename)).code + "`");
🧹 Nitpick comments (12)
src/data/blockStateDefinitions.json (1)

34-34: Fix the trailing comma.

The trailing comma after the array is unnecessary and makes the JSON less clean.

Apply this diff to remove the trailing comma:

-			"brushed_progress": [0, 1, 2, 3], // suspicious sand/gravel
+			"brushed_progress": [0, 1, 2, 3] // suspicious sand/gravel
src/EntityGeoMaker.js (1)

38-43: Consider extracting inflate handling to improve readability.

The inflate calculation logic could be extracted to a helper method for better maintainability.

Consider extracting the inflate logic:

+/**
+ * Applies inflate to cube position and size
+ * @param {Data.Cube} cube
+ * @param {number} inflate
+ */
+#applyInflate(cube, inflate) {
+    let inflate3 = tuple([inflate, inflate, inflate]);
+    cube["pos"] = subVec3(cube["pos"], inflate3);
+    cube["size"] = addVec3(cube["size"], mulVec3(inflate3, 2));
+}

 if("inflate" in geoCube) {
-    let { inflate } = geoCube;
-    let inflate3 = tuple([inflate, inflate, inflate]);
-    cube["pos"] = subVec3(cube["pos"], inflate3);
-    cube["size"] = addVec3(cube["size"], mulVec3(inflate3, 2));
+    this.#applyInflate(cube, geoCube.inflate);
 }
src/TextureAtlas.js (1)

489-502: Potential performance issue with nested loops for large textures.

The pixel hashing function iterates over every pixel channel individually, which could be slow for large texture regions. Consider processing pixels in chunks.

For better performance on large textures, consider processing pixels in larger chunks:

 #hashPixels(imageData, startX, startY, w, h) {
     let hash = 2166136261;
+    const data = imageData.data;
+    const width = imageData.width;
     for(let y = startY; y < startY + h; y++) {
+        let rowStart = (y * width + startX) * 4;
+        let rowEnd = rowStart + w * 4;
         for(let x = startX; x < startX + w; x++) {
             let startI = (y * imageData.width + x) * 4;
-            for(let i = startI; i < startI + 4; i++) {
-                hash ^= imageData.data[i];
-                hash *= 16777619;
-                hash >>>= 0;
-            }
+            // Process RGBA as a single unit
+            hash ^= data[startI];
+            hash *= 16777619;
+            hash ^= data[startI + 1];
+            hash *= 16777619;
+            hash ^= data[startI + 2];
+            hash *= 16777619;
+            hash ^= data[startI + 3];
+            hash *= 16777619;
+            hash >>>= 0;
         }
     }
     return hash;
 }
src/PreviewRenderer.js (1)

1-1: Missing import for assertAs usage.

The assertAs function is imported but appears to be used only for TypeScript type assertions (Line 285). This seems redundant in a JavaScript file.

Consider removing the TypeScript-style assertion if not needed:

-import { abs, assertAs, AsyncFactory, cosDeg, distanceSquared, downloadFile, JSONSet, max, min, pi, round, sinDeg, subVec2, tanDeg, toImageData, tuple } from "./utils.js";
+import { abs, AsyncFactory, cosDeg, distanceSquared, downloadFile, JSONSet, max, min, pi, round, sinDeg, subVec2, tanDeg, toImageData, tuple } from "./utils.js";
-TS: assertAs(glbData, ArrayBuffer);
+// glbData is already an ArrayBuffer when binary: true
src/entityScripts.molang.js (1)

3-3: Consider keeping ACTIONS as a named export for better tree-shaking.

Making ACTIONS private and only accessible through default export prevents tree-shaking optimizations. If other modules only need ACTIONS, they'll import the entire module.

Consider maintaining both named and default exports:

-const ACTIONS = createNumericEnum([...]);
+export const ACTIONS = createNumericEnum([...]);

 export default {
     ACTIONS,
     armorStandInitialization,
     // ... rest
 };
src/fetchers.js (2)

90-96: Consider exponential backoff instead of fixed delay.

The retry mechanism uses a fixed 1-second delay, which might not be optimal for rate limiting scenarios.

 res = await retrieve(fullUrl);
 let fetchAttempsLeft = 5;
-const fetchRetryTimeout = 1000;
+let fetchRetryTimeout = 1000;
 while(BAD_STATUS_CODES.includes(res.status) && fetchAttempsLeft--) {
     console.debug(`Encountered bad HTTP status ${res.status} from ${fullUrl}, trying again in ${fetchRetryTimeout}ms`);
     await sleep(fetchRetryTimeout);
+    fetchRetryTimeout = Math.min(fetchRetryTimeout * 2, 10000); // Exponential backoff with max 10s
     res = await retrieve(fullUrl);
 }

112-114: Simplify version sorting logic.

The sorting function creates unnecessary intermediate arrays and could be more readable.

 function sortVersions(versions) {
     let versionsAndParsed = versions.map(v => [v, Array.from(v.matchAll(/\d+/g)).map(m => +m[0])]);
-    versionsAndParsed.sort(([, a], [, b]) => Array(max(a.length, b.length)).fill().map((_, i) => (a[i] ?? 0) - (b[i] ?? 0)).find(d => d) || 0);
+    versionsAndParsed.sort(([, a], [, b]) => {
+        const maxLen = max(a.length, b.length);
+        for(let i = 0; i < maxLen; i++) {
+            const diff = (a[i] ?? 0) - (b[i] ?? 0);
+            if(diff !== 0) return diff;
+        }
+        return 0;
+    });
     return versionsAndParsed.map(([ver]) => ver);
 }
src/ResourcePackStack.js (1)

34-46: Consider caching merged JSON responses.

The current implementation merges JSON files on every request, which could be inefficient for frequently accessed resources.

Would you like me to implement a simple in-memory cache for merged JSON responses to improve performance?

src/BlockGeoMaker.js (1)

498-509: Consider using Proxy for cleaner property access.

The current implementation using Object.defineProperties works but could be simplified.

 #addEasyPropertyAccessors(cube) {
-    Object.defineProperties(cube, Object.fromEntries(["x", "y", "z", "w", "h", "d"].map((prop, i) => [prop, {
-        get() {
-            return (i < 3? this["pos"] : this["size"])[i % 3];
-        },
-        set(value) {
-            (i < 3? this["pos"] : this["size"])[i % 3] = value;
-        }
-    }])));
-    // @ts-expect-error
-    return cube;
+    return new Proxy(cube, {
+        get(target, prop) {
+            const propMap = { x: 0, y: 1, z: 2, w: 3, h: 4, d: 5 };
+            if (prop in propMap) {
+                const index = propMap[prop];
+                return (index < 3 ? target.pos : target.size)[index % 3];
+            }
+            return target[prop];
+        },
+        set(target, prop, value) {
+            const propMap = { x: 0, y: 1, z: 2, w: 3, h: 4, d: 5 };
+            if (prop in propMap) {
+                const index = propMap[prop];
+                (index < 3 ? target.pos : target.size)[index % 3] = value;
+                return true;
+            }
+            target[prop] = value;
+            return true;
+        }
+    });
 }
src/HoloPrint.js (3)

150-153: Harden item_tags.json fetch against failures.

If the fetch fails, pack generation for customized controls could abort. Consider a tolerant fallback to keep building.

-    itemTagsPromise = fetchers.bedrockData("item_tags.json").then(res => res.json());
+    itemTagsPromise = fetchers.bedrockData("item_tags.json")
+      .then(res => res.json())
+      .catch(() => ({}));

1526-1538: Ensure integer sizes for generated overlay textures.

safeSize can become non‑integer when scaling, leading to decimal file names and potential atlas issues. Round once and reuse.

-                let safeSize = lcm((await paddedTexturePromise).width, itemTextureSize) * config.CONTROL_ITEM_TEXTURE_SCALE; // ...
-                controlItemTextureSizes.add(safeSize);
+                let safeSize = lcm((await paddedTexturePromise).width, itemTextureSize) * config.CONTROL_ITEM_TEXTURE_SCALE; // ...
+                let safeSizeInt = Math.max(1, Math.round(safeSize));
+                controlItemTextureSizes.add(safeSizeInt);
-                (usingTerrainAtlas? terrainTexture : itemTexture)["texture_data"][itemName] = {
-                    "textures": [originalTexturePath, `${controlTexturePath.slice(0, -4)}_${safeSize}`],
+                (usingTerrainAtlas? terrainTexture : itemTexture)["texture_data"][itemName] = {
+                    "textures": [originalTexturePath, `${controlTexturePath.slice(0, -4)}_${safeSizeInt}`],
                     "additive": true // texture compositing means resource packs that change the item textures will still work
                 };
@@
-        await Promise.all(Array.from(controlItemTextureSizes).map(async size => {
-            let resizedImagePath = `${controlTexturePath.slice(0, -4)}_${size}.png`;
-            let resizedTextureBlob = await resizeImageToBlob(await paddedTexturePromise, size);
+        await Promise.all(Array.from(controlItemTextureSizes).map(async sizeInt => {
+            let resizedImagePath = `${controlTexturePath.slice(0, -4)}_${sizeInt}.png`;
+            let resizedTextureBlob = await resizeImageToBlob(await paddedTexturePromise, sizeInt);
             controlItemTextures.push([resizedImagePath, resizedTextureBlob]);
         }));

2052-2053: Mark pixelsHash optional to match intent.

Docstring says “optional” in PR notes, but JSDoc marks it required. Make it optional to avoid misleading consumers.

- * @property {number} pixelsHash FNV-1a hash of all pixels in this fragment
+ * @property {number} [pixelsHash] FNV-1a hash of all pixels in this fragment
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between edbe738 and f036c9c.

📒 Files selected for processing (25)
  • .gitignore (1 hunks)
  • globalPatches.d.ts (1 hunks)
  • pipeline/build.js (1 hunks)
  • src/BlockGeoMaker.js (25 hunks)
  • src/BlockUpdater.js (4 hunks)
  • src/EntityGeoMaker.js (1 hunks)
  • src/HoloPrint.js (11 hunks)
  • src/PreviewRenderer.js (7 hunks)
  • src/ResourcePackStack.js (2 hunks)
  • src/TextureAtlas.js (6 hunks)
  • src/components/ItemCriteriaInput.js (1 hunks)
  • src/data/blockShapeGeos.json (2 hunks)
  • src/data/blockShapes.json (6 hunks)
  • src/data/blockStateDefinitions.json (1 hunks)
  • src/data/schemas/blockShapeGeos.schema.json (5 hunks)
  • src/data/schemas/blockShapes.schema.json (2 hunks)
  • src/data/schemas/index.ts (3 hunks)
  • src/data/textureAtlasMappings.json (2 hunks)
  • src/entityScripts.molang.js (6 hunks)
  • src/fetchers.js (1 hunks)
  • src/index.js (4 hunks)
  • src/translations/en_US.json (1 hunks)
  • src/translations/zh_CN.json (1 hunks)
  • src/utils.js (2 hunks)
  • tests/checkBlockTranslationCoverage.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.js

⚙️ CodeRabbit configuration file

**/*.js: Ignore the issue of using let instead of const for variables that aren't changed later.
Ignore the issue of importing native Node modules (e.g. http) without the node: protocol prefix.
Ignore the use of [^] (a negated character class with nothing in it) in regular expressions.

Files:

  • tests/checkBlockTranslationCoverage.js
  • src/EntityGeoMaker.js
  • src/fetchers.js
  • src/BlockUpdater.js
  • src/entityScripts.molang.js
  • src/components/ItemCriteriaInput.js
  • src/HoloPrint.js
  • pipeline/build.js
  • src/BlockGeoMaker.js
  • src/ResourcePackStack.js
  • src/PreviewRenderer.js
  • src/index.js
  • src/TextureAtlas.js
  • src/utils.js
🧠 Learnings (6)
📚 Learning: 2025-08-24T07:02:00.962Z
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#291
File: src/packTemplate/ui/holoprint_material_list.json:76-83
Timestamp: 2025-08-24T07:02:00.962Z
Learning: In the SuperLlama88888/holoprint repository, placeholder strings like "this gets put in by the JS" in JSON UI files are replaced by JavaScript during the pack generation/build process, before Minecraft loads the files. The replacement happens at build-time, not runtime.

Applied to files:

  • tests/checkBlockTranslationCoverage.js
  • src/HoloPrint.js
📚 Learning: 2025-06-15T03:58:32.640Z
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#208
File: src/data/blockShapes.json:79-86
Timestamp: 2025-06-15T03:58:32.640Z
Learning: In src/data/blockShapes.json, the explicit suffix mappings for various sign types (like "spruce_wall_sign": "wall_sign_suffix") are intentionally kept to preserve Minecraft's inconsistent behavior, rather than letting regex patterns normalize them to prefix-based shapes. This inconsistency should not be "fixed" as it's meant to match how Minecraft itself handles these blocks.

Applied to files:

  • src/data/schemas/blockShapes.schema.json
  • src/data/textureAtlasMappings.json
  • src/data/blockShapes.json
📚 Learning: 2025-06-17T12:09:52.939Z
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#208
File: src/MaterialList.js:45-46
Timestamp: 2025-06-17T12:09:52.939Z
Learning: In MaterialList.js, the block-to-item pattern mappings intentionally use non-global regex patterns with .replace() because only the first match needs to be replaced, not all occurrences in the block name.

Applied to files:

  • src/data/schemas/blockShapes.schema.json
📚 Learning: 2025-08-17T21:05:27.558Z
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#285
File: pipeline/package.json:7-7
Timestamp: 2025-08-17T21:05:27.558Z
Learning: html-minifier-next works with named ESM imports (import { minify }) in the holoprint project's pipeline/build.js setup, contrary to general compatibility concerns.

Applied to files:

  • pipeline/build.js
📚 Learning: 2025-08-17T21:05:27.558Z
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#285
File: pipeline/package.json:7-7
Timestamp: 2025-08-17T21:05:27.558Z
Learning: In the holoprint project, html-minifier-next v1.2.0 works perfectly with named ESM imports using `import { minify as minifyHTML } from "html-minifier-next"` in the pipeline/build.js file.

Applied to files:

  • pipeline/build.js
📚 Learning: 2025-07-03T05:20:20.863Z
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#252
File: src/data/blockShapeGeos.json:4265-4270
Timestamp: 2025-07-03T05:20:20.863Z
Learning: In blockShapeGeos.json geometry definitions, zero-height faces at different Y coordinates (like y=4 and y=3.125) do not overlap and do not cause Z-fighting. Be more careful when analyzing 3D positioning before claiming geometry issues - verify that faces actually occupy the same coordinates before suggesting overlapping face problems.

Applied to files:

  • src/data/schemas/index.ts
🧬 Code graph analysis (10)
tests/checkBlockTranslationCoverage.js (2)
src/ResourcePackStack.js (1)
  • ResourcePackStack (10-56)
src/LocalResourcePack.js (1)
  • LocalResourcePack (3-54)
src/EntityGeoMaker.js (1)
src/utils.js (4)
  • jsonc (98-112)
  • subVec3 (218-220)
  • addVec3 (210-212)
  • mulVec3 (226-228)
src/fetchers.js (1)
src/utils.js (11)
  • lazyLoadAsyncFunctionFactory (924-932)
  • c (182-182)
  • res (314-314)
  • res (330-330)
  • res (345-345)
  • sleep (158-158)
  • sleep (158-158)
  • a (844-844)
  • i (281-281)
  • i (649-649)
  • i (663-663)
src/entityScripts.molang.js (2)
src/utils.js (1)
  • createNumericEnum (405-408)
src/HoloPrint.js (3)
  • initVariables (1199-1199)
  • renderingControls (1200-1213)
  • broadcastActions (1214-1216)
src/components/ItemCriteriaInput.js (1)
src/HoloPrint.js (3)
  • res (829-829)
  • res (852-852)
  • data (191-191)
src/HoloPrint.js (4)
src/ResourcePackStack.js (1)
  • ResourcePackStack (10-56)
src/EntityGeoMaker.js (1)
  • EntityGeoMaker (3-53)
src/BlockGeoMaker.js (1)
  • BlockGeoMaker (10-1108)
src/BlockUpdater.js (1)
  • BlockUpdater (6-239)
src/BlockGeoMaker.js (2)
src/HoloPrint.js (5)
  • entityGeoMaker (192-192)
  • i (1578-1578)
  • i (1733-1733)
  • i (1769-1769)
  • i (1885-1885)
src/utils.js (5)
  • i (281-281)
  • i (649-649)
  • i (663-663)
  • tuple (163-163)
  • tuple (163-163)
src/ResourcePackStack.js (1)
src/utils.js (2)
  • jsonc (98-112)
  • removeFalsies (776-778)
src/PreviewRenderer.js (1)
src/utils.js (6)
  • assertAs (167-167)
  • assertAs (167-167)
  • downloadFile (843-850)
  • i (281-281)
  • i (649-649)
  • i (663-663)
src/index.js (2)
src/ResourcePackStack.js (1)
  • ResourcePackStack (10-56)
src/utils.js (5)
  • random (295-297)
  • res (314-314)
  • res (330-330)
  • res (345-345)
  • toImage (118-156)
🪛 GitHub Actions: Automated Testing
src/data/schemas/blockShapes.schema.json

[error] 1-1: Failed to fetch schema for blockShapes.json: Invalid regular expression: /^\w+<[\w/]*>$/u: Invalid escape

🪛 Biome (2.1.2)
pipeline/build.js

[error] 36-36: The regular expression includes this negated empty character class.

Negated empty character classes match anything.
If you want to match against [, escape it [.
Otherwise, remove the character class or fill it.

(lint/correctness/noEmptyCharacterClassInRegex)

src/data/textureAtlasMappings.json

[error] 5-5: Expected a property but instead found '// just a few inconsistencies here. ideally this doesn't exist. can be generated with: const missingFromBlocksDotJson=async VER=>[...(new Set((await fetch(https://raw.githubusercontent.com/Mojang/bedrock-samples/${VER}/metadata/vanilladata_modules/mojang-blocks.json).then(r=>r.json())).data_items.map(x=>x.name.replace("minecraft:","")))).difference(new Set(Object.keys(await fetch(https://raw.githubusercontent.com/Mojang/bedrock-samples/${VER}/resource_pack/blocks.json).then(res=>res.json()))))].filter(blockName=>!(["hard_","element_","colored_torch_"].some(prefix=>blockName.startsWith(prefix))||["chemical_heat","compound_creator","lab_table","material_reducer","underwater_torch"].includes(blockName)))'.

Expected a property here.

(parse)


[error] 6-6: expected , but instead found "grass_block"

Remove "grass_block"

(parse)


[error] 9-9: expected , but instead found // shows missing_tile.png

Remove // shows missing_tile.png

(parse)

src/data/blockStateDefinitions.json

[error] 34-34: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 34-34: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 34-34: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 34-34: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 35-35: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 35-35: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 35-35: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)

src/data/blockShapes.json

[error] 77-77: Expected a property but instead found '// top face is rotated'.

Expected a property here.

(parse)


[error] 78-78: expected , but instead found "piston"

Remove "piston"

(parse)


[error] 85-85: Expected a property but instead found '// all the other shulker boxes have appropriately named textures (e.g. shulker_red.png), but this one is shulker_silver.png because they haven't updated it :('.

Expected a property here.

(parse)


[error] 96-96: Expected a property but instead found '// new signs use the wall_sign_prefix shape, defined as a regular expression above for best future compatibility'.

Expected a property here.

(parse)


[error] 97-97: expected , but instead found "birch_wall_sign"

Remove "birch_wall_sign"

(parse)


[error] 122-122: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 122-122: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 122-122: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 122-122: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 230-230: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 230-230: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 230-230: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 230-230: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 231-231: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 231-231: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 231-231: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 231-231: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 232-232: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 232-232: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 232-232: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 232-233: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 234-234: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 234-234: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 234-234: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 234-234: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 235-235: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 235-235: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 235-235: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 235-235: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 259-259: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 259-259: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 259-259: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 259-259: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 261-261: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 261-261: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 261-261: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)

🔇 Additional comments (66)
src/translations/en_US.json (1)

54-54: LGTM!

The new translation key for GLB export follows the existing naming convention and is properly formatted.

.gitignore (1)

1-1: LGTM! Cleaner pattern for ignoring node_modules.

The pattern **/node_modules correctly ignores all node_modules directories throughout the repository hierarchy, which is more maintainable than listing specific paths.

src/translations/zh_CN.json (1)

54-54: Translation key matches English version correctly.

The GLB export translation key is consistent with the English version and follows the established pattern.

pipeline/build.js (5)

10-12: Good practice extracting directory names as constants.

Making srcDir and distDir configurable constants improves maintainability.


21-34: Staging directory approach improves build isolation.

The new workflow that creates a temp directory for processing before copying to dist is a solid improvement - it prevents partial builds from corrupting the output directory if the build fails.


49-51: Good addition for Molang file handling.

Treating .molang.js files as assets that should be copied without processing is appropriate for domain-specific language files.


59-75: Clever approach to update script import paths after hashing.

The logic correctly maps old entry points to their hashed output filenames and updates all HTML references. The word boundary assertions in the regex pattern help prevent false matches.


36-36: Regex pattern contains problematic negated empty character class.

The regex /([^]+?)/ contains [^] which is a negated empty character class that matches any character including newlines. While this works in JavaScript, it's non-standard and can cause confusion or compatibility issues.

Use the standard multiline dot pattern instead:

-let importMapJSON = fs.readFileSync(`${distDir}/index.html`, "utf-8").match(/<script type="importmap">([^]+?)<\/script>/)[1];
+let importMapJSON = fs.readFileSync(`${distDir}/index.html`, "utf-8").match(/<script type="importmap">([\s\S]+?)<\/script>/)[1];

Or with the dotAll flag:

-let importMapJSON = fs.readFileSync(`${distDir}/index.html`, "utf-8").match(/<script type="importmap">([^]+?)<\/script>/)[1];
+let importMapJSON = fs.readFileSync(`${distDir}/index.html`, "utf-8").match(/<script type="importmap">(.+?)<\/script>/s)[1];
⛔ Skipped due to learnings
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#208
File: pipeline/build.js:98-98
Timestamp: 2025-06-21T08:22:03.879Z
Learning: In JavaScript regular expressions, `[^]` is valid syntax that matches any character including newlines. The user SuperLlama88888 prefers this pattern over `[\s\S]` and has it explicitly covered in their coding guidelines to ignore linter warnings about it.
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#296
File: src/HoloPrint.js:1748-1749
Timestamp: 2025-09-14T10:11:36.023Z
Learning: JavaScript supports variable-length lookbehind assertions including alternation since ES2018, unlike many other regex engines. Patterns like `(?<!return|let)` are valid and work correctly in modern JavaScript environments.
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#208
File: src/MaterialList.js:45-46
Timestamp: 2025-06-17T12:09:52.939Z
Learning: In MaterialList.js, the block-to-item pattern mappings intentionally use non-global regex patterns with .replace() because only the first match needs to be replaced, not all occurrences in the block name.
tests/checkBlockTranslationCoverage.js (1)

11-11: LGTM!

The change from the factory method to direct constructor instantiation aligns with the ResourcePackStack refactor to a plain class.

src/components/ItemCriteriaInput.js (2)

5-11: LGTM!

The migration from VanillaDataFetcher to the shared fetchers module follows the broader refactor pattern and maintains the same data source and functionality.


12-18: LGTM!

The transition to using fetchers.bedrockData for item tags is consistent with the broader data fetching consolidation.

src/data/blockStateDefinitions.json (1)

35-35: LGTM!

The addition of rehydration_level for dried ghasts is correctly structured and follows the existing pattern.

src/data/schemas/blockShapeGeos.schema.json (3)

158-161: LGTM!

The addition of box_uv_flip_east_west provides useful control over UV mapping for box UV mode.


203-222: LGTM!

The copy_entity_model feature is well-structured with appropriate validation for the geometry identifier, file path, and texture requirements.


326-332: LGTM!

The textureFilePath definition provides consistent validation for texture file paths across the schema.

src/index.js (4)

44-44: LGTM!

The switch from an async factory to direct constructor instantiation simplifies the ResourcePackStack usage and aligns with the class refactor.


428-432: LGTM!

Adding cache cleanup for old ResourcePackStack caches is good housekeeping to prevent storage bloat.


461-461: LGTM!

The direct call to fetchResource on the existing instance simplifies the code by removing the promise chain.


661-661: LGTM!

The direct constructor call with local resource packs follows the updated ResourcePackStack API pattern.

src/BlockUpdater.js (4)

3-3: LGTM!

The migration from internal fetcher instantiation to the shared fetchers module aligns with the broader refactoring pattern.


6-6: LGTM!

Removing the AsyncFactory inheritance simplifies the class and makes it a plain standalone class.


32-32: LGTM!

The transition to fetchers.bedrockBlockUpgradeSchema maintains the same functionality while using the centralized data fetching approach.


48-48: LGTM!

The consistent use of the fetchers module for schema loading is appropriate and maintains the same data retrieval behavior.

src/data/textureAtlasMappings.json (2)

5-5: Fix the comment formatting in JSON.

The inline comment contains unescaped characters and breaks JSON syntax. JSON doesn't support inline comments, so this should be removed or moved to the description field.

Apply this diff to fix the syntax error:

-	"blocks_dot_json_patches": { // just a few inconsistencies here. ideally this doesn't exist. can be generated with: const missingFromBlocksDotJson=async VER=>[...(new Set((await fetch(`https://raw.githubusercontent.com/Mojang/bedrock-samples/${VER}/metadata/vanilladata_modules/mojang-blocks.json`).then(r=>r.json())).data_items.map(x=>x.name.replace("minecraft:","")))).difference(new Set(Object.keys(await fetch(`https://raw.githubusercontent.com/Mojang/bedrock-samples/${VER}/resource_pack/blocks.json`).then(res=>res.json()))))].filter(blockName=>!(["hard_","element_","colored_torch_"].some(prefix=>blockName.startsWith(prefix))||["chemical_heat","compound_creator","lab_table","material_reducer","underwater_torch"].includes(blockName)))
+	"blocks_dot_json_patches": {
⛔ Skipped due to learnings
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#291
File: src/packTemplate/ui/holoprint_keybinds.json:47-58
Timestamp: 2025-08-24T06:58:06.537Z
Learning: In the holoprint project, inline JavaScript-style comments (// comments) are supported across ALL JSON files in the entire codebase, not just specific files like blockStateDefinitions.json. The project has preprocessing that removes these comments before JSON parsing, so they don't cause parsing errors. Do not flag inline comments in any JSON files in this project as issues.
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#208
File: src/data/blockStateDefinitions.json:254-254
Timestamp: 2025-06-16T01:21:18.458Z
Learning: In the holoprint codebase, JSON files like blockStateDefinitions.json can contain inline comments (// comments) because they have a preprocessing step that removes comments before parsing the JSON. This means inline comments don't break JSON parsing in this codebase.
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#291
File: src/packTemplate/ui/holoprint_keybinds.json:181-187
Timestamp: 2025-08-24T06:58:38.029Z
Learning: Do not flag inline JavaScript-style comments (// comments) in JSON files as issues in the holoprint project. The user SuperLlama88888 has explicitly requested not to be warned about JSON comments as they are intentionally supported throughout the entire project.
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#291
File: src/packTemplate/ui/_global_variables.json:1-6
Timestamp: 2025-08-24T06:58:06.117Z
Learning: The holoprint codebase has a jsonc() function in src/utils.js that uses the strip-json-comments library to preprocess JSON files, allowing inline // comments in JSON files throughout the packTemplate directory. This function is used by getResponseContents() in HoloPrint.js for all .json and .material files.
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#208
File: src/data/blockShapes.json:79-86
Timestamp: 2025-06-15T03:58:32.640Z
Learning: In src/data/blockShapes.json, the explicit suffix mappings for various sign types (like "spruce_wall_sign": "wall_sign_suffix") are intentionally kept to preserve Minecraft's inconsistent behavior, rather than letting regex patterns normalize them to prefix-based shapes. This inconsistency should not be "fixed" as it's meant to match how Minecraft itself handles these blocks.

9-9: Fix the trailing comment.

JSON doesn't support trailing comments. This should be removed or included in the description at the top of the file.

Apply this diff to fix the syntax error:

-		"unknown": "info_update" // shows missing_tile.png
+		"unknown": "info_update"
⛔ Skipped due to learnings
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#291
File: src/packTemplate/ui/holoprint_keybinds.json:47-58
Timestamp: 2025-08-24T06:58:06.537Z
Learning: In the holoprint project, inline JavaScript-style comments (// comments) are supported across ALL JSON files in the entire codebase, not just specific files like blockStateDefinitions.json. The project has preprocessing that removes these comments before JSON parsing, so they don't cause parsing errors. Do not flag inline comments in any JSON files in this project as issues.
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#208
File: src/data/blockStateDefinitions.json:254-254
Timestamp: 2025-06-16T01:21:18.458Z
Learning: In the holoprint codebase, JSON files like blockStateDefinitions.json can contain inline comments (// comments) because they have a preprocessing step that removes comments before parsing the JSON. This means inline comments don't break JSON parsing in this codebase.
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#291
File: src/packTemplate/ui/holoprint_keybinds.json:181-187
Timestamp: 2025-08-24T06:58:38.029Z
Learning: Do not flag inline JavaScript-style comments (// comments) in JSON files as issues in the holoprint project. The user SuperLlama88888 has explicitly requested not to be warned about JSON comments as they are intentionally supported throughout the entire project.
src/data/schemas/blockShapes.schema.json (2)

43-43: Fix the regex pattern.

Same issue as above - the angle brackets need to be properly escaped.

Apply this diff to fix the regex pattern:

-							"pattern": "^\\w+\\<[\\w/]*\\>$"
+							"pattern": "^\\w+\\\\<[\\w/]*\\\\>$"

Likely an incorrect or invalid review comment.


16-25: Fix the regex pattern.

The regex pattern contains an unescaped < character which causes a pipeline failure. Angle brackets need to be escaped in JSON Schema regex patterns.
Apply this diff to fix the regex pattern:

-							"pattern": "^\\w+\\<[\\w/]*\\>$"
+							"pattern": "^\\w+\\\\<[\\w/]*\\\\>$"
⛔ Skipped due to learnings
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#208
File: src/data/blockShapes.json:79-86
Timestamp: 2025-06-15T03:58:32.640Z
Learning: In src/data/blockShapes.json, the explicit suffix mappings for various sign types (like "spruce_wall_sign": "wall_sign_suffix") are intentionally kept to preserve Minecraft's inconsistent behavior, rather than letting regex patterns normalize them to prefix-based shapes. This inconsistency should not be "fixed" as it's meant to match how Minecraft itself handles these blocks.
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#208
File: src/MaterialList.js:45-46
Timestamp: 2025-06-17T12:09:52.939Z
Learning: In MaterialList.js, the block-to-item pattern mappings intentionally use non-global regex patterns with .replace() because only the first match needs to be replaced, not all occurrences in the block name.
src/EntityGeoMaker.js (1)

1-56: LGTM! Well-structured class for entity geometry processing.

The implementation correctly handles entity model-to-cube conversion with proper async patterns and structured data handling. The use of JSDoc annotations for type safety is good practice.

src/TextureAtlas.js (3)

340-341: Good addition of texture deduplication via hashing!

The implementation of FNV-1a hashing for pixel data deduplication is a smart optimization that can significantly reduce texture atlas size.


411-429: Clever deduplication handling in UV mapping.

The logic to reuse UV entries for identical fragments is well-implemented and will help with memory efficiency.


32-35: Public field rename is a breaking change — verify compatibility

Renaming atlasWidth/atlasHeight → textureWidth/textureHeight in src/TextureAtlas.js (lines 32–35) is an API break; repo search for "atlasWidth|atlasHeight" returned no hits, but that doesn't guarantee no external usage — restore backward-compatible aliases (e.g., getters) or document this as a breaking change.

src/utils.js (1)

917-932: Excellent implementation of lazy async function loading!

The lazyLoadAsyncFunctionFactory is a clean pattern for lazy initialization of async functions with proper memoization. The use of nullish coalescing for the promise ensures single initialization.

src/PreviewRenderer.js (3)

162-162: Good use of dynamic import for GLTFExporter.

Lazy loading the GLTFExporter only when needed is a good optimization for bundle size.


564-564: UV coordinate flip is correctly implemented.

The change from texture.flipY = true to flipping V coordinates in UV generation (Line 564: 1 - polyMesh.uvs[uvIndex][1]) is the correct approach for consistent texture orientation.


281-294: Well-structured GLB export implementation!

The exportGlb method properly expands instanced meshes before export and includes appropriate error handling. The use of trs: true option is documented well.

src/entityScripts.molang.js (1)

492-502: Default export provides good module encapsulation.

The consolidation to a default export is clean and maintains all functionality while allowing for better build-time optimizations with hashed filenames.

src/data/blockShapeGeos.json (3)

2611-2611: Good rename for consistency.

The rename from iron_bars to metal_bars makes the key more generic and applicable to different metal bar types.


5350-5362: Well-structured tentacle template.

The template_dried_ghast_tentacle provides a reusable component with proper texture mapping and UV coordinates.


5401-5433: Nice use of EntityGeoMaker integration.

The copper_golem_statue implementation elegantly switches between different poses using entity models. This showcases the new copy_entity_model feature well.

src/BlockGeoMaker.js (6)

102-104: Good texture extraction logic.

The new angle-bracket syntax <textures/...> is cleanly extracted here.


261-262: Nice integration with EntityGeoMaker.

The async handling for entity model conversion is properly implemented.


270-271: Good abstraction with helper method.

The #addEasyPropertyAccessors method provides a clean way to add computed properties.


742-743: Good addition for box UV flipping support.

The box_uv_flip_east_west feature provides needed flexibility for entity model rendering.


1025-1030: Proper handling of special texture variable.

The #tex variable correctly uses the specialTexture parameter passed through the interpolation chain.


237-238: Fix typo in error message.

"formated" should be "formatted".

-console.error(`Incorrect formatted copy_block property: ${match}`);
+console.error(`Incorrectly formatted copy_block property: ${cube["copy_block"]}`);

Likely an incorrect or invalid review comment.

src/data/schemas/index.ts (4)

8-68: Well-structured Cube interface.

The new Cube interface provides comprehensive typing for all cube properties, including the new copy_entity_model field.


71-73: Good texture path typing.

The TextureFilePath type alias provides clear semantic meaning for texture file paths.


74-81: Clean EntityModelInfo structure.

The interface clearly defines the required fields for entity model copying.


114-122: Good flexibility in BlockStateVariants.

The new BlockStateVariants type properly supports both exclusive addition mode and flexible value mappings.

src/fetchers.js (1)

71-76: Potential undefined access when res is falsy.

If res is undefined/null, accessing res?.status returns undefined, which won't match any status codes. However, the delete operation on line 74 could be called unnecessarily.

-if(BAD_STATUS_CODES.includes(res?.status)) {
+if(res && BAD_STATUS_CODES.includes(res.status)) {
     await cache.delete(cacheLink);
-} else if(res) {
+} else if(res?.ok) {
     prevCache?.delete(cacheLink);
     return res;
 }

Likely an incorrect or invalid review comment.

src/HoloPrint.js (8)

9-16: Imports alignment looks good.

Consolidating fetchers and introducing EntityGeoMaker ties in with the new data flow.


61-61: Default ResourcePackStack parameter is a good API improvement.

Defaulting to new ResourcePackStack() per call is safe and simplifies call‑sites.


172-173: Retexture pipeline hookup matches the new signature.

Order and arguments to retextureControlItems look correct.


191-196: EntityGeoMaker integration into BlockGeoMaker is correct.

Constructor params match the updated BlockGeoMaker contract; awaiting makePolyMeshTemplates is aligned with its async return.


839-841: Good: metadata now loads via fetchers.vanillaData + jsonc.

Keeps sources consistent with the new fetchers subsystem and preserves JSONC support.


880-880: Correct BlockUpdater usage.

Instantiating with new BlockUpdater() matches the class design.


1459-1471: LGTM: legacy item id back‑mapping via fetchers.

Graceful error handling and reverse mapping setup are solid.


216-218: TextureAtlas properties verified — OK
src/TextureAtlas.js declares and assigns textureWidth/textureHeight (declaration ~lines 33–35, assignment ~401–402); HoloPrint's usage is correct.

src/data/blockShapes.json (8)

32-41: Pattern additions look correct; regexes are precise.

Lantern family, lightning rod, chain, bars, and copper golem statue variants read well and won’t over‑match.

Please confirm these new patterns don’t shadow any existing per‑block overrides unintentionally (e.g., via your pattern resolution order).


60-71: Chest family updated to angle‑bracket texture spec.

Looks consistent with the new schema.

Double‑check that all referenced textures exist in 1.21.110 vanilla RP (e.g., textures/entity/chest/copper_default and variants).


78-80: Piston arm texture paths: verify casing and location.

textures/entity/pistonarm/pistonArm(Sticky) may be case‑sensitive on some tooling.

Can you confirm these exact texture paths exist in the vanilla assets you target?


85-85: Shulker “silver” quirk handled.

Correctly maps light_gray_shulker_box to shulker_silver.


95-103: Sign mappings: prefix form plus explicit suffix entries retained.

Good: honors the known Mojang inconsistencies (as per prior decision).


122-123: Confirm copper_torch availability.

If copper_torch isn’t a vanilla block in 1.21.110, this entry is harmless but unnecessary.

Is copper_torch present in your loaded mojang-blocks.json metadata?


230-236: Head/skull texture spec migration looks consistent.

Paths align with entity skull textures and the player head fallback is reasonable.


259-262: New mappings (wildflowers → pink_petals, dried_ghast) look fine.

No schema conflicts apparent.

Comment thread globalPatches.d.ts
Comment thread src/EntityGeoMaker.js Outdated
Comment thread src/EntityGeoMaker.js
Comment thread src/fetchers.js
Comment thread src/fetchers.js
Comment thread src/PreviewRenderer.js
Comment thread src/ResourcePackStack.js
Comment on lines +36 to +42
let vanillaJson = await jsonc(vanillaRes.clone()); // clone it so it can be read later if need be (responses can only be read once)
let resourcePackFiles = this.localResourcePacks.map(resourcePack => resourcePack.getFile(resourcePath));
let resourcePackJsons = await Promise.all(removeFalsies(resourcePackFiles).map(file => jsonc(file)));
resourcePackJsons.reverse(); // start with the lowest priority pack, so that they get overwritten by higher priority packs
let allJsons = [vanillaJson, ...resourcePackJsons];
if(allJsons.length == 1) { // if only the vanilla resources had this file, use that response
return vanillaRes;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add error handling for JSON parsing failures.

The jsonc() function can reject, but the error isn't caught here.

 let vanillaRes = await fetchers.vanillaData(filePath);
-let vanillaJson = await jsonc(vanillaRes.clone()); // clone it so it can be read later if need be (responses can only be read once)
+let vanillaJson;
+try {
+    vanillaJson = await jsonc(vanillaRes.clone());
+} catch(e) {
+    console.error(`Failed to parse JSON from ${resourcePath}:`, e);
+    return vanillaRes; // Return the original response if parsing fails
+}
 let resourcePackFiles = this.localResourcePacks.map(resourcePack => resourcePack.getFile(resourcePath));
-let resourcePackJsons = await Promise.all(removeFalsies(resourcePackFiles).map(file => jsonc(file)));
+let resourcePackJsons = [];
+for(let file of removeFalsies(resourcePackFiles)) {
+    try {
+        resourcePackJsons.push(await jsonc(file));
+    } catch(e) {
+        console.error(`Failed to parse resource pack JSON:`, e);
+    }
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let vanillaJson = await jsonc(vanillaRes.clone()); // clone it so it can be read later if need be (responses can only be read once)
let resourcePackFiles = this.localResourcePacks.map(resourcePack => resourcePack.getFile(resourcePath));
let resourcePackJsons = await Promise.all(removeFalsies(resourcePackFiles).map(file => jsonc(file)));
resourcePackJsons.reverse(); // start with the lowest priority pack, so that they get overwritten by higher priority packs
let allJsons = [vanillaJson, ...resourcePackJsons];
if(allJsons.length == 1) { // if only the vanilla resources had this file, use that response
return vanillaRes;
let vanillaRes = await fetchers.vanillaData(filePath);
let vanillaJson;
try {
vanillaJson = await jsonc(vanillaRes.clone()); // clone it so it can be read later if need be (responses can only be read once)
} catch (e) {
console.error(`Failed to parse JSON from ${resourcePath}:`, e);
return vanillaRes; // Return the original response if parsing fails
}
let resourcePackFiles = this.localResourcePacks.map(resourcePack => resourcePack.getFile(resourcePath));
let resourcePackJsons = [];
for (let file of removeFalsies(resourcePackFiles)) {
try {
resourcePackJsons.push(await jsonc(file));
} catch (e) {
console.error(`Failed to parse resource pack JSON:`, e);
}
}
resourcePackJsons.reverse(); // start with the lowest priority pack, so that they get overwritten by higher priority packs
let allJsons = [vanillaJson, ...resourcePackJsons];
if(allJsons.length == 1) { // if only the vanilla resources had this file, use that response
return vanillaRes;
🤖 Prompt for AI Agents
In src/ResourcePackStack.js around lines 36 to 42, add explicit error handling
for jsonc() calls so JSON parse rejections are not left unhandled: wrap the call
to jsonc(vanillaRes.clone()) in a try/catch and surface a clear error (or
rethrow) if the vanilla JSON cannot be parsed; for the resourcePackJsons
Promise.all, instead of letting Promise.all reject, map each file to an
individual try/catch (or Promise that resolves to {ok:false,err} /
{ok:true,value}) so you can log parse errors with context (resourcePath and pack
id) and skip malformed pack files while continuing processing, then proceed with
only successfully parsed JSONs; ensure logs include enough context and fail fast
only for vanilla file parse errors.

Comment thread src/TextureAtlas.js

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/data/schemas/blockShapes.schema.json (1)

16-25: No schema change required for current data — extract a shared TexturedShapeString if you want future-proofing.

Scan of src/data/blockShapes.json shows all textured entries use only letters/digits/underscore/slash (e.g. chest<textures/entity/chest/normal>); no <> or characters outside [A-Za-z0-9_/] were found, so ^\w+<[\w/]*>$ accepts current values.

  • If you expect namespaces (minecraft:...), dashes or dots in future texture names, expand the charset (example: ^\w+<(?:(?:[A-Za-z0-9_\-\.]+/)*[A-Za-z0-9_\-\.]+)>$).
  • Optional: move the pattern into definitions (TexturedShapeString) and $ref it in both places to avoid duplication.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f036c9c and 9441e49.

📒 Files selected for processing (1)
  • src/data/schemas/blockShapes.schema.json (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-06-15T03:58:32.640Z
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#208
File: src/data/blockShapes.json:79-86
Timestamp: 2025-06-15T03:58:32.640Z
Learning: In src/data/blockShapes.json, the explicit suffix mappings for various sign types (like "spruce_wall_sign": "wall_sign_suffix") are intentionally kept to preserve Minecraft's inconsistent behavior, rather than letting regex patterns normalize them to prefix-based shapes. This inconsistency should not be "fixed" as it's meant to match how Minecraft itself handles these blocks.

Applied to files:

  • src/data/schemas/blockShapes.schema.json
📚 Learning: 2025-06-17T12:09:52.939Z
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#208
File: src/MaterialList.js:45-46
Timestamp: 2025-06-17T12:09:52.939Z
Learning: In MaterialList.js, the block-to-item pattern mappings intentionally use non-global regex patterns with .replace() because only the first match needs to be replaced, not all occurrences in the block name.

Applied to files:

  • src/data/schemas/blockShapes.schema.json

Comment thread src/data/schemas/blockShapes.schema.json
Comment thread src/data/schemas/blockShapes.schema.json
This means all new blocks are added. Close #290

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/TextureAtlas.js (1)

432-435: Fix NaN/overwrite bug when assigning transparency for duplicate fragments.

Duplicates have w/h = 0, causing division by zero in transparency calc and then overwriting the original UV object (shared reference) with NaN.

Apply this diff to reuse the original fragment’s transparency for duplicates:

-		let transparencies = this.#getImageFragmentTransparencies(canImageData, packedImageFragments);
-		transparencies.forEach((transparency, i) => {
-			imageUvs[i]["transparency"] = transparency;
-		});
+		let transparencies = this.#getImageFragmentTransparencies(canImageData, packedImageFragments);
+		transparencies.forEach((transparency, i) => {
+			if (identicalFragmentIndices.has(i)) {
+				const original = identicalFragmentIndices.get(i);
+				imageUvs[i]["transparency"] = transparencies[original];
+			} else {
+				imageUvs[i]["transparency"] = transparency;
+			}
+		});
🧹 Nitpick comments (9)
src/EntityGeoMaker.js (7)

18-24: Wrap JSONC parse in try/catch and include status in fetch failure

Handles non-OK nicely, but JSON parse errors will currently reject the whole method. Prefer returning [] with context.

-		let geoFileRes = await this.resourcePackStack.fetchResource(entityModelInfo["geo_file"]);
-		if(!geoFileRes.ok) {
-			console.error(`Unable to load geometry file ${entityModelInfo["geo_file"]}`);
-			return [];
-		}
-		let geoFile = await jsonc(geoFileRes);
+		const geoFileRes = await this.resourcePackStack.fetchResource(entityModelInfo["geo_file"]);
+		if (!geoFileRes || !geoFileRes.ok) {
+			console.error(`Unable to load geometry file ${entityModelInfo["geo_file"]} (status: ${geoFileRes?.status ?? "unknown"})`);
+			return [];
+		}
+		let geoFile;
+		try {
+			geoFile = await jsonc(geoFileRes);
+		} catch (e) {
+			console.error(`Failed to parse geometry file ${entityModelInfo["geo_file"]}: ${e?.message ?? e}`);
+			return [];
+		}

29-31: Default texture size to avoid undefined

Some geos omit texture dims. Safe default avoids undefined downstream.

-		let textureWidth = matchingGeo["description"]["texture_width"];
-		let textureHeight = matchingGeo["description"]["texture_height"];
+		const desc = matchingGeo["description"] ?? {};
+		const textureWidth = desc["texture_width"] ?? 64;
+		const textureHeight = desc["texture_height"] ?? 64;

31-33: Guard against missing/empty bones

If bones is absent, current code will throw. Early-exit with context keeps behavior consistent with other error paths.

-		let cubes = [];
-		matchingGeo["bones"].forEach(bone => {
+		let cubes = [];
+		const bones = Array.isArray(matchingGeo["bones"]) ? matchingGeo["bones"] : [];
+		if (bones.length === 0) {
+			console.error(`No bones found in geometry ${entityModelInfo["identifier"]} (${entityModelInfo["geo_file"]})`);
+			return [];
+		}
+		bones.forEach(bone => {

41-41: Confirm always-on east/west UV flip

Hardcoding "box_uv_flip_east_west: true" may invert textures unexpectedly depending on source "mirror" or face UVs. Confirm this is intended for entity models; otherwise, tie it to source data.

-					"box_uv_flip_east_west": true,
+					"box_uv_flip_east_west": !!geoCube["mirror"],

53-56: Set pivot only when present

Avoids injecting undefined when a cube has rotation but no pivot.

-				if("rotation" in geoCube) {
-					cube["rot"] = geoCube["rotation"];
-					cube["pivot"] = geoCube["pivot"];
-				}
+				if ("rotation" in geoCube) {
+					cube["rot"] = geoCube["rotation"];
+					if ("pivot" in geoCube) {
+						cube["pivot"] = geoCube["pivot"];
+					}
+				}

48-51: Inline inflate vec (no tuple dependency)

Functionally equivalent and simpler.

-					let { inflate } = geoCube;
-					let inflate3 = tuple([inflate, inflate, inflate]);
+					let { inflate } = geoCube;
+					let inflate3 = [inflate, inflate, inflate];

32-59: Follow-up: bone-level transforms are ignored

If accuracy matters, accumulate bone pivot/rotation (and hierarchy) and apply to cubes. Current output uses only cube-level transforms; complex entities will look wrong.

Would you like a follow-up patch that composes bone transforms (pivot/rotation order, degrees-to-radians, and parent hierarchy) into cube transforms?

src/data/blockShapeGeos.json (1)

5363-5400: dried_ghast assembly: UV/rotations OK; consider face culling if overdraw becomes an issue.

If overdraw shows up, hide interior faces on rotated copies via per-face "none" mappings.

src/TextureAtlas.js (1)

480-489: Typo: “FRV-1a” → “FNV-1a”.

Correct the algorithm name in the doc to avoid confusion in maintenance.

-	 * Calculates the FRV-1a hash of a specific region of an `ImageData`.
+	 * Calculates the FNV-1a hash of a specific region of an `ImageData`.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4b74ae5 and c4a7fb8.

📒 Files selected for processing (5)
  • src/EntityGeoMaker.js (1 hunks)
  • src/TextureAtlas.js (6 hunks)
  • src/data/blockShapeGeos.json (2 hunks)
  • src/data/blockShapes.json (6 hunks)
  • src/fetchers.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/fetchers.js
🧰 Additional context used
📓 Path-based instructions (1)
**/*.js

⚙️ CodeRabbit configuration file

**/*.js: Ignore the issue of using let instead of const for variables that aren't changed later.
Ignore the issue of importing native Node modules (e.g. http) without the node: protocol prefix.
Ignore the use of [^] (a negated character class with nothing in it) in regular expressions.

Files:

  • src/TextureAtlas.js
  • src/EntityGeoMaker.js
🧠 Learnings (2)
📚 Learning: 2025-06-15T03:58:32.640Z
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#208
File: src/data/blockShapes.json:79-86
Timestamp: 2025-06-15T03:58:32.640Z
Learning: In src/data/blockShapes.json, the explicit suffix mappings for various sign types (like "spruce_wall_sign": "wall_sign_suffix") are intentionally kept to preserve Minecraft's inconsistent behavior, rather than letting regex patterns normalize them to prefix-based shapes. This inconsistency should not be "fixed" as it's meant to match how Minecraft itself handles these blocks.

Applied to files:

  • src/data/blockShapeGeos.json
  • src/data/blockShapes.json
📚 Learning: 2025-09-24T12:16:07.696Z
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#299
File: src/data/schemas/blockShapes.schema.json:16-25
Timestamp: 2025-09-24T12:16:07.696Z
Learning: In src/data/schemas/blockShapes.schema.json, the regex pattern `^\\w+<[\\w/]*>$` intentionally allows empty texture paths (e.g., `block_name<>`) using the `*` quantifier. This is by design and should not be "fixed" to require at least one character with `+`.

Applied to files:

  • src/data/blockShapes.json
🧬 Code graph analysis (1)
src/EntityGeoMaker.js (1)
src/utils.js (4)
  • jsonc (98-112)
  • subVec3 (218-220)
  • addVec3 (210-212)
  • mulVec3 (226-228)
🪛 Biome (2.1.2)
src/data/blockShapes.json

[error] 78-78: Expected a property but instead found '// top face is rotated'.

Expected a property here.

(parse)


[error] 79-79: expected , but instead found "piston"

Remove "piston"

(parse)


[error] 86-86: Expected a property but instead found '// all the other shulker boxes have appropriately named textures (e.g. shulker_red.png), but this one is shulker_silver.png because they haven't updated it :('.

Expected a property here.

(parse)


[error] 97-97: Expected a property but instead found '// new signs use the wall_sign_prefix shape, defined as a regular expression above for best future compatibility'.

Expected a property here.

(parse)


[error] 98-98: expected , but instead found "birch_wall_sign"

Remove "birch_wall_sign"

(parse)


[error] 123-123: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 123-123: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 123-123: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 123-123: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 231-231: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 231-231: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 231-231: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 231-231: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 232-232: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 232-232: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 232-232: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 232-232: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 233-233: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 233-233: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 233-233: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 233-234: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 235-235: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 235-235: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 235-235: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 235-235: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 236-236: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 236-236: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 236-236: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 236-236: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 260-260: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 260-260: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 260-260: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 260-260: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 262-262: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 262-262: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 262-262: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)

🔇 Additional comments (18)
src/EntityGeoMaker.js (3)

24-28: Good: defensive check for missing geometry added

This resolves the prior runtime risk flagged earlier. Thanks for adding it.


38-38: Verify translate baseline

Translate [8, 0, 8] is typical for block-space centering; for entity-space this may need [0, 0, 0] or per-bone offsets. Confirm alignment with consumers of Data.Cube.


1-1: Do not remove tuple import — it's exported and used.
src/utils.js exports tuple (export const tuple = x => x; at src/utils.js:163) and src/EntityGeoMaker.js calls it (src/EntityGeoMaker.js:49: let inflate3 = tuple([inflate, inflate, inflate]);); removing the import will break the file.

Likely an incorrect or invalid review comment.

src/data/blockShapeGeos.json (4)

5350-5362: Template for dried ghast tentacle looks sane.

Box UVs and 32×32 texture size align with small segment use. No issues spotted.


5401-5496: Shelf: verify powered_shelf_type mapping order matches runtime.

The four powered variants (0–3) are mapped; comments suggest center/right/left ambiguity. Please confirm against actual block states so indicators match expected positions.


5497-5529: copy_entity_model usage for copper_golem_statue: validate runtime support and asset presence.

Assuming BlockGeoMaker/EntityGeoMaker support copy_entity_model and texture param injection (#tex), this is correct. Ensure the referenced geometry and textures exist in the active resource packs.


2611-2624: Rename to metal_bars — no remaining "iron_bars" references found.
Searched tracked files (and common variants); no matches for "iron_bars". Pattern mapping *_bars → metal_bars is consistent.

src/data/blockShapes.json (8)

61-72: Chest mappings migrated to angle-bracket textures; copper variants included.

Paths look correct and waxed variants reuse same textures. Good.

Please confirm these texture paths match Bedrock 1.21.110 pack names (copper_default/exposed/weathered/oxidized).


79-81: Piston textures switched to entity piston arm textures; validate Bedrock paths.

If your fetchers point at Bedrock resources, verify:

  • textures/entity/pistonarm/pistonArm(.png)
  • textures/entity/pistonarm/pistonArmSticky(.png)
    exist and case matches.

86-86: light_gray_shulker_box special-case is correct.

Using shulker_silver texture is necessary. Nice catch.


96-105: Signs: angle-bracket base plus explicit suffix overrides retained.

Matches prior decision to preserve Bedrock inconsistencies for legacy sign textures. Good.


123-123: Add copper_torch to torch family; OK.

Shares torch shape; align with MCPE-180783 note. No issues.


231-236: Heads/skulls migrated to <textures/...> forms; OK.

Default player head fallback to steve is fine. Ensure these texture paths exist in the pack.


260-263: wildflowers → pink_petals shape and dried_ghast mapping added.

  • Mapping wildflowers to pink_petals is plausible; please confirm in-game parity.
  • dried_ghast links to new geo; consistent with geos file.

32-41: Approve — patterns verified

No explicit "lantern"/"soul_lantern" mappings found; regex entries for lantern, chain, _bars, _shelf and copper_golem_statue variants exist in src/data/blockShapes.json and cover the intended forms. LGTM.

src/TextureAtlas.js (3)

340-342: Good: stable per-fragment region hashing added.

Hashing the cropped region up-front is the right primitive for dedup.


514-527: Pixel indexing in equivalence check is correct now.

The indices include loop offsets; this resolves the earlier report.


32-37: Field rename confirmed — no lingering atlasWidth/atlasHeight references

No matches for atlasWidth/atlasHeight; callers use textureWidth/textureHeight/textureFillEfficiency (seen in src/TextureAtlas.js, src/BlockGeoMaker.js, src/EntityGeoMaker.js, src/HoloPrint.js).

Comment thread src/TextureAtlas.js

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
pipeline/build.js (3)

34-34: Complex filter logic should be extracted for readability.

The inline filter function combines multiple conditions and file system operations, making it difficult to understand at a glance.

Apply this diff to extract the filter logic:

+function shouldCopyFile(filename) {
+	const isJsFile = path.extname(filename) === ".js";
+	if (isJsFile) return false;
+	
+	if (fs.statSync(filename).isDirectory()) {
+		return !fs.readdirSync(filename).every(file => path.extname(file) === ".js");
+	}
+	return true;
+}
+
 fs.cpSync("temp", distDir, {
 	recursive: true,
-	filter: filename => !(path.extname(filename) == ".js" || (fs.statSync(filename).isDirectory() && fs.readdirSync(filename).every(file => path.extname(file) == ".js")))
+	filter: shouldCopyFile
 });

60-65: Extract script replacement logic for better maintainability.

The inline logic for building script import replacements could be extracted to improve readability and testability.

Apply this diff to extract the logic:

+function buildScriptImportReplacements(metafile) {
+	return Object.entries(metafile["outputs"])
+		.filter(([_, { entryPoint }]) => entryPoint)
+		.map(([output, { entryPoint }]) => [
+			entryPoint.replace("temp/", ""), 
+			output.replace(distDir + "/", "")
+		]);
+}
+
-let scriptImportReplacements = [];
-Object.entries(metafile["outputs"]).forEach(([output, { entryPoint }]) => {
-	if(entryPoint) {
-		scriptImportReplacements.push([entryPoint.replace("temp/", ""), output.replace(distDir + "/", "")]);
-	}
-});
+let scriptImportReplacements = buildScriptImportReplacements(metafile);

66-86: Extract HTML processing logic into a separate function.

The HTML file processing logic is complex and handles multiple responsibilities (reading, processing imports, updating import maps, writing). This makes the main build flow harder to follow.

Apply this diff to extract the HTML processing:

+function processHtmlFiles(scriptImportReplacements) {
+	fs.readdirSync(distDir).forEach(filename => {
+		if(path.extname(filename) === ".html") {
+			const filepath = path.join(distDir, filename);
+			let html = fs.readFileSync(filepath, "utf-8");
+			
+			// Replace script imports
+			scriptImportReplacements.forEach(([oldName, newName]) => {
+				const regExp = new RegExp(`\\b${oldName.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`, "g");
+				html = html.replaceAll(regExp, newName);
+			});
+			
+			// Update or inject import map
+			if(importMapPattern.test(html)) {
+				html = html.replace(importMapPattern, (_, importMapJSON) => 
+					addImportReplacementsToImportMap(JSON.parse(importMapJSON))
+				);
+			} else {
+				const importMapScript = addImportReplacementsToImportMap({});
+				if(html.includes("</head>")) {
+					html = html.replace("</head>", `${importMapScript}</head>`);
+				} else {
+					html += importMapScript;
+				}
+			}
+			
+			fs.writeFileSync(filepath, html);
+		}
+	});
+}
+
-fs.readdirSync(distDir).forEach(filename => {
-	if(path.extname(filename) == ".html") {
-		let filepath = path.join(distDir, filename);
-		let html = fs.readFileSync(filepath, "utf-8");
-		scriptImportReplacements.forEach(([oldName, newName]) => {
-			let regExp = new RegExp(`\\b${oldName.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`, "g");
-			html = html.replaceAll(regExp, newName);
-		});
-		if(importMapPattern.test(html)) {
-			html = html.replace(importMapPattern, (_, importMapJSON) => addImportReplacementsToImportMap(JSON.parse(importMapJSON)));
-		} else {
-			let importMapScript = addImportReplacementsToImportMap({});
-			if(html.includes("</head>")) {
-				html = html.replace("</head>", `${importMapScript}</head>`);
-			} else {
-				html += importMapScript;
-			}
-		}
-		fs.writeFileSync(filepath, html);
-	}
-});
+processHtmlFiles(scriptImportReplacements);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c4a7fb8 and 7df851f.

📒 Files selected for processing (1)
  • pipeline/build.js (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.js

⚙️ CodeRabbit configuration file

**/*.js: Ignore the issue of using let instead of const for variables that aren't changed later.
Ignore the issue of importing native Node modules (e.g. http) without the node: protocol prefix.
Ignore the use of [^] (a negated character class with nothing in it) in regular expressions.

Files:

  • pipeline/build.js
🧠 Learnings (3)
📚 Learning: 2025-08-17T21:05:27.558Z
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#285
File: pipeline/package.json:7-7
Timestamp: 2025-08-17T21:05:27.558Z
Learning: html-minifier-next works with named ESM imports (import { minify }) in the holoprint project's pipeline/build.js setup, contrary to general compatibility concerns.

Applied to files:

  • pipeline/build.js
📚 Learning: 2025-08-17T21:05:27.558Z
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#285
File: pipeline/package.json:7-7
Timestamp: 2025-08-17T21:05:27.558Z
Learning: In the holoprint project, html-minifier-next v1.2.0 works perfectly with named ESM imports using `import { minify as minifyHTML } from "html-minifier-next"` in the pipeline/build.js file.

Applied to files:

  • pipeline/build.js
📚 Learning: 2025-08-17T21:05:00.229Z
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#285
File: pipeline/build.js:5-5
Timestamp: 2025-08-17T21:05:00.229Z
Learning: The html-minifier-next package provides proper ESM support with named exports, including `export const minify`. The import syntax `import { minify } from "html-minifier-next"` works correctly, unlike the original html-minifier package which was CommonJS-only.

Applied to files:

  • pipeline/build.js
🪛 Biome (2.1.2)
pipeline/build.js

[error] 20-20: The regular expression includes this negated empty character class.

Negated empty character classes match anything.
If you want to match against [, escape it [.
Otherwise, remove the character class or fill it.

(lint/correctness/noEmptyCharacterClassInRegex)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Test Sample Structures (Firefox)
  • GitHub Check: Test Sample Structures (Chrome)
🔇 Additional comments (2)
pipeline/build.js (2)

194-204: LGTM! Clean import map handling.

The addImportReplacementsToImportMap function properly handles both existing and new import maps, using nullish coalescing to ensure the imports object exists before adding replacements.


20-20: Remove the [^] character class from the regex - it matches anything.

The negated empty character class [^] in the importMapPattern regex matches any character, which is redundant. This creates noise in the regex pattern.

Based on the coding guidelines, I should ignore the use of [^] in regular expressions, but the static analysis tool correctly identifies this as problematic code that should be addressed. Apply this diff to fix it:

-const importMapPattern = /<script type="importmap">([^]+?)<\/script>/;
+const importMapPattern = /<script type="importmap">([\s\S]+?)<\/script>/;

The [\s\S] pattern explicitly matches any character including newlines, making the intent clearer.

⛔ Skipped due to learnings
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#208
File: pipeline/build.js:98-98
Timestamp: 2025-06-21T08:22:03.879Z
Learning: In JavaScript regular expressions, `[^]` is valid syntax that matches any character including newlines. The user SuperLlama88888 prefers this pattern over `[\s\S]` and has it explicitly covered in their coding guidelines to ignore linter warnings about it.
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#299
File: src/data/schemas/blockShapes.schema.json:16-25
Timestamp: 2025-09-24T12:16:07.696Z
Learning: In src/data/schemas/blockShapes.schema.json, the regex pattern `^\\w+<[\\w/]*>$` intentionally allows empty texture paths (e.g., `block_name<>`) using the `*` quantifier. This is by design and should not be "fixed" to require at least one character with `+`.

Comment thread pipeline/build.js
Comment thread pipeline/build.js
Will be removed  once pmmp/BedrockBlockUpgradeSchema releases the new upgrade schema which will happen when the update actually drops.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (4)
pipeline/build.js (4)

20-20: Optional: avoid [^] to satisfy linters (use dotAll).

Biome flags this. Using the s flag keeps intent and quiets the rule.

-const importMapPattern = /<script type="importmap">([^]+?)<\/script>/;
+const importMapPattern = /<script type="importmap">(.+?)<\/script>/s;

38-41: Use tempDir for entry; external is fine.

Prevents hard-coding "temp".

-	entryPoints: ["temp/index.js"],
+	entryPoints: [path.join(tempDir, "index.js")],

53-53: Ensure cleanup uses the unique tempDir.

-rmDir("temp");
+rmDir(tempDir);

189-196: Optional: force deletion for stubborn files.

Adds resilience on Windows/CI.

 	if(fs.existsSync(dir)) {
 		fs.rmSync(dir, {
-			recursive: true
+			recursive: true,
+			force: true
 		});
 	}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7df851f and 6082c86.

📒 Files selected for processing (2)
  • pipeline/build.js (2 hunks)
  • src/data/materialListMappings.json (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.js

⚙️ CodeRabbit configuration file

**/*.js: Ignore the issue of using let instead of const for variables that aren't changed later.
Ignore the issue of importing native Node modules (e.g. http) without the node: protocol prefix.
Ignore the use of [^] (a negated character class with nothing in it) in regular expressions.

Files:

  • pipeline/build.js
🧠 Learnings (2)
📚 Learning: 2025-08-17T21:05:27.558Z
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#285
File: pipeline/package.json:7-7
Timestamp: 2025-08-17T21:05:27.558Z
Learning: html-minifier-next works with named ESM imports (import { minify }) in the holoprint project's pipeline/build.js setup, contrary to general compatibility concerns.

Applied to files:

  • pipeline/build.js
📚 Learning: 2025-08-17T21:05:27.558Z
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#285
File: pipeline/package.json:7-7
Timestamp: 2025-08-17T21:05:27.558Z
Learning: In the holoprint project, html-minifier-next v1.2.0 works perfectly with named ESM imports using `import { minify as minifyHTML } from "html-minifier-next"` in the pipeline/build.js file.

Applied to files:

  • pipeline/build.js
🪛 Biome (2.1.2)
pipeline/build.js

[error] 20-20: The regular expression includes this negated empty character class.

Negated empty character classes match anything.
If you want to match against [, escape it [.
Otherwise, remove the character class or fill it.

(lint/correctness/noEmptyCharacterClassInRegex)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Test Sample Structures (Firefox)
  • GitHub Check: Test Sample Structures (Chrome)
🔇 Additional comments (7)
pipeline/build.js (7)

10-12: LGTM: Clear source/dist config.

Good to have centralized constants.


22-33: Make staging dir robust: avoid import.meta.dirname portability pitfall; use unique temp dir and always clean up.

  • import.meta.dirname isn’t guaranteed across Node versions. Prefer fileURLToPath.
  • Using a fixed "temp" risks collisions and leaves debris on errors. Use a unique tempDir and try/finally cleanup.

Apply this diff in-place:

-process.chdir(path.resolve(import.meta.dirname, "../"));
-rmDir("temp");
-fs.cpSync(srcDir, "temp", {
+process.chdir(rootDir);
+const tempDir = `temp-${process.pid}-${Date.now()}`;
+rmDir(tempDir);
+fs.cpSync(srcDir, tempDir, {
 	recursive: true
 });
-await processDir("temp");
+await processDir(tempDir);
 rmDir(distDir);
-fs.cpSync("temp", distDir, {
+fs.cpSync(tempDir, distDir, {
 	recursive: true,
 	filter: filename => !(path.extname(filename) == ".js" || (fs.statSync(filename).isDirectory() && fs.readdirSync(filename).every(file => path.extname(file) == ".js")))
 });

Add these helper lines outside the range:

// imports
import { fileURLToPath } from "node:url";
// before chdir
const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../");
// optionally, wrap the staging/build steps in try/finally and call rmDir(tempDir) in finally

34-35: Guard missing import map and JSON parse errors.

Directly indexing match()[1] will throw if absent; parsing can fail silently.

-let importMapJSON = fs.readFileSync(`${distDir}/index.html`, "utf-8").match(importMapPattern)[1];
-let externalModules = Object.keys(JSON.parse(importMapJSON)["imports"]);
+const htmlContent = fs.readFileSync(`${distDir}/index.html`, "utf-8");
+const importMapMatch = htmlContent.match(importMapPattern);
+if (!importMapMatch) {
+  throw new Error(`Import map not found in ${distDir}/index.html`);
+}
+const importMapJSON = importMapMatch[1];
+let externalModules = Object.keys(JSON.parse(importMapJSON)["imports"]);

127-137: HTML minification pipeline looks solid.

Inline JSON minification before HTML minify is a good touch; named ESM import for html-minifier-next is known to work here.


168-179: JS processing: prod toggles and optional export are correct.

Inlining processed HTML template literals is a nice optimization.


202-208: LGTM: import map augmentation.

Simple and effective mapping injection.


45-49: Keep using loader "copy"; esbuild v0.25.4 supports it
esbuild added the “copy” loader in v0.14.44 and you’re on ^0.25.4, so it’s safe—and preserves runtime imports better than “file.”

Comment thread pipeline/build.js
Comment thread pipeline/build.js
Comment thread src/data/materialListMappings.json

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/TextureAtlas.js (1)

433-436: Avoid clobbering transparency for deduped textures.

When a fragment is deduplicated we zero out its w/h, so #getImageFragmentTransparencies returns NaN (division by zero). Because Line 415 reuses the same UV object for the duplicate, assigning that NaN here overwrites the canonical fragment’s transparency too. Packs with repeated textures will therefore end up with NaN transparency. Guard the assignment for indices recorded in identicalFragmentIndices.

-		transparencies.forEach((transparency, i) => {
-			imageUvs[i]["transparency"] = transparency;
-		});
+		transparencies.forEach((transparency, i) => {
+			if(!identicalFragmentIndices.has(i)) {
+				imageUvs[i]["transparency"] = transparency;
+			}
+		});
🧹 Nitpick comments (2)
src/HoloPrint.js (2)

150-153: Avoid awaiting a possibly undefined promise for clarity

Awaiting an undefined value works (resolves to undefined) but is non-obvious. Guard the await for readability.

Apply this diff at Line 452:

-	let itemTags = await itemTagsPromise;
+	const itemTags = itemTagsPromise ? await itemTagsPromise : undefined;

Also applies to: 452-452


1526-1526: Ensure resized overlay texture size is an integer

If CONTROL_ITEM_TEXTURE_SCALE is non-integer, safeSize can be fractional, which may cause canvas sizing oddities.

Apply this diff:

-				let safeSize = lcm((await paddedTexturePromise).width, itemTextureSize) * config.CONTROL_ITEM_TEXTURE_SCALE;
+				let safeSize = Math.round(lcm((await paddedTexturePromise).width, itemTextureSize) * config.CONTROL_ITEM_TEXTURE_SCALE);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6082c86 and 7cc0f0f.

📒 Files selected for processing (2)
  • src/HoloPrint.js (10 hunks)
  • src/TextureAtlas.js (5 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.js

⚙️ CodeRabbit configuration file

**/*.js: Ignore the issue of using let instead of const for variables that aren't changed later.
Ignore the issue of importing native Node modules (e.g. http) without the node: protocol prefix.
Ignore the use of [^] (a negated character class with nothing in it) in regular expressions.

Files:

  • src/HoloPrint.js
  • src/TextureAtlas.js
🧠 Learnings (5)
📚 Learning: 2025-08-24T07:02:00.962Z
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#291
File: src/packTemplate/ui/holoprint_material_list.json:76-83
Timestamp: 2025-08-24T07:02:00.962Z
Learning: In the SuperLlama88888/holoprint repository, placeholder strings like "this gets put in by the JS" in JSON UI files are replaced by JavaScript during the pack generation/build process, before Minecraft loads the files. The replacement happens at build-time, not runtime.

Applied to files:

  • src/HoloPrint.js
📚 Learning: 2025-09-24T12:09:38.550Z
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#299
File: src/fetchers.js:33-36
Timestamp: 2025-09-24T12:09:38.550Z
Learning: In the HoloPrint project's fetchers.js, there will only ever be one cache of each type open at any given time, eliminating concerns about race conditions in cache cleanup logic during fetcher initialization.

Applied to files:

  • src/HoloPrint.js
📚 Learning: 2025-09-24T12:50:36.292Z
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#299
File: src/TextureAtlas.js:354-370
Timestamp: 2025-09-24T12:50:36.292Z
Learning: In the HoloPrint codebase, the maintainer prefers to keep hash collision handling simple in the texture atlas deduplication logic, accepting the extremely low probability of FNV-1a hash collisions over added code complexity.

Applied to files:

  • src/TextureAtlas.js
📚 Learning: 2025-09-24T12:15:53.300Z
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#299
File: src/fetchers.js:39-43
Timestamp: 2025-09-24T12:15:53.300Z
Learning: In the HoloPrint project, SuperLlama88888 prefers simpler code over defensive null checks when the consequences of edge cases are minimal and harmless (e.g., calling caches.delete(undefined) won't cause serious issues).

Applied to files:

  • src/TextureAtlas.js
📚 Learning: 2025-07-02T05:14:31.300Z
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#252
File: src/BlockGeoMaker.js:116-116
Timestamp: 2025-07-02T05:14:31.300Z
Learning: In the SuperLlama88888/holoprint repository, the user considers the performance impact of using the delete operator vs setting properties to undefined to be negligible and prefers to keep the existing delete usage rather than making changes for this performance optimization.

Applied to files:

  • src/TextureAtlas.js
🧬 Code graph analysis (1)
src/HoloPrint.js (3)
src/ResourcePackStack.js (1)
  • ResourcePackStack (10-56)
src/EntityGeoMaker.js (1)
  • EntityGeoMaker (3-62)
src/BlockUpdater.js (1)
  • BlockUpdater (6-239)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Test Sample Structures (Firefox)
  • GitHub Check: Test Sample Structures (Chrome)
🔇 Additional comments (8)
src/HoloPrint.js (8)

9-11: Imports refactor to fetchers + EntityGeoMaker looks solid

Shifts to centralized fetchers and adds EntityGeoMaker cleanly; no issues spotted with usage downstream.

Also applies to: 15-16


61-61: Default ResourcePackStack in makePack is a good simplification

The new default avoids extra boilerplate and aligns with the new ResourcePackStack behavior.


171-173: Updated call to retextureControlItems matches new signature

Parameter ordering (including resourcePackStack) matches the updated function. Looks correct.


192-196: EntityGeoMaker integration and async BlockGeoMaker pipeline LGTM

Instantiating EntityGeoMaker and awaiting makePolyMeshTemplates aligns with the new async flow.


216-217: Atlas dimensions wired into geometry description

Using textureAtlas.textureWidth/textureHeight is correct and keeps geo metadata in sync with the built atlas.


838-841: Bedrock metadata now fetched via fetchers.vanillaData

Consistent with the new data-loading layer; no issues noted.


881-881: BlockUpdater instantiation

No issues found; works with the refactored fetchers-based schema loading in BlockUpdater.


1410-1410: retextureControlItems: signature and legacy mapping fetch updated

  • Adds resourcePackStack param and uses fetchers.bedrockData for legacy mappings; both changes look correct.

Also applies to: 1459-1471

Thank you @Hellothere1234627284yehnsndnsnwnd for suggesting these UI changes!
The touch controls are now available via buttons at the top, rather than on the side, reducing the possibility that they would overlap the Minecraft touch controls.
This took super long to figure out..............
@SuperLlama88888 SuperLlama88888 changed the title Update to 1.21.110 Update to 1.21.110, improve touch UI Sep 26, 2025

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/HoloPrint.js (1)

61-66: Add compatibility shim for old makePack signature
No internal callers use the legacy (structureFiles, config, previewCont, previewLoadedCallback) order, but since makePack is a public API you must preserve back-compat. In src/HoloPrint.js, immediately after the function signature insert:

 export async function makePack(structureFiles, config, resourcePackStack = new ResourcePackStack(), previewCont, previewLoadedCallback) {
+  // Back-compat with previous signature: (structureFiles, config, previewCont, previewLoadedCallback)
+  if (resourcePackStack && typeof resourcePackStack.fetchResource !== 'function') {
+    previewLoadedCallback = previewCont;
+    previewCont = resourcePackStack;
+    resourcePackStack = new ResourcePackStack();
+  }
🧹 Nitpick comments (1)
src/HoloPrint.js (1)

1414-1545: Coerce legacy item variant indices to numbers.

Minor consistency improvement to avoid subtle string-index pitfalls when using array indices.

-                    Object.entries(updateMappings["complex"]).forEach(([oldName, newNames]) => { // complex mappings have indices, used in boats among others
-                        Object.entries(newNames).forEach(([index, newName]) => {
-                            legacyItemMappings.set(newName.slice(10), [oldName.slice(10), index]);
+                    Object.entries(updateMappings["complex"]).forEach(([oldName, newNames]) => { // complex mappings have indices, used in boats among others
+                        Object.entries(newNames).forEach(([index, newName]) => {
+                            legacyItemMappings.set(newName.slice(10), [oldName.slice(10), +index]);
                         });
                     });
...
-                let [oldItemName, legacyVariant] = legacyItemMappings.get(itemName);
-                variant = legacyVariant;
+                let [oldItemName, legacyVariant] = legacyItemMappings.get(itemName);
+                variant = +legacyVariant;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7cc0f0f and c861366.

⛔ Files ignored due to path filters (5)
  • src/packTemplate/textures/ui/material_list_button_pressed.png is excluded by !**/*.png
  • src/packTemplate/textures/ui/material_list_button_unpressed.png is excluded by !**/*.png
  • src/packTemplate/textures/ui/menu_button_pressed.png is excluded by !**/*.png
  • src/packTemplate/textures/ui/menu_button_unpressed.png is excluded by !**/*.png
  • src/packTemplate/textures/ui/menu_sliders_icon.png is excluded by !**/*.png
📒 Files selected for processing (4)
  • src/HoloPrint.js (11 hunks)
  • src/packTemplate/ui/_global_variables.json (1 hunks)
  • src/packTemplate/ui/holoprint_material_list.json (2 hunks)
  • src/packTemplate/ui/holoprint_touch_buttons.json (8 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.js

⚙️ CodeRabbit configuration file

**/*.js: Ignore the issue of using let instead of const for variables that aren't changed later.
Ignore the issue of importing native Node modules (e.g. http) without the node: protocol prefix.
Ignore the use of [^] (a negated character class with nothing in it) in regular expressions.

Files:

  • src/HoloPrint.js
🧠 Learnings (2)
📚 Learning: 2025-08-24T07:02:00.962Z
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#291
File: src/packTemplate/ui/holoprint_material_list.json:76-83
Timestamp: 2025-08-24T07:02:00.962Z
Learning: In the SuperLlama88888/holoprint repository, placeholder strings like "this gets put in by the JS" in JSON UI files are replaced by JavaScript during the pack generation/build process, before Minecraft loads the files. The replacement happens at build-time, not runtime.

Applied to files:

  • src/packTemplate/ui/holoprint_material_list.json
  • src/packTemplate/ui/_global_variables.json
  • src/HoloPrint.js
📚 Learning: 2025-09-24T12:09:38.550Z
Learnt from: SuperLlama88888
PR: SuperLlama88888/holoprint#299
File: src/fetchers.js:33-36
Timestamp: 2025-09-24T12:09:38.550Z
Learning: In the HoloPrint project's fetchers.js, there will only ever be one cache of each type open at any given time, eliminating concerns about race conditions in cache cleanup logic during fetcher initialization.

Applied to files:

  • src/HoloPrint.js
🧬 Code graph analysis (1)
src/HoloPrint.js (4)
src/index.js (3)
  • config (620-651)
  • resourcePackStack (661-661)
  • previewCont (653-653)
src/ResourcePackStack.js (1)
  • ResourcePackStack (10-56)
src/EntityGeoMaker.js (1)
  • EntityGeoMaker (3-62)
src/BlockGeoMaker.js (1)
  • BlockGeoMaker (10-1108)
🪛 Biome (2.1.2)
src/packTemplate/ui/holoprint_touch_buttons.json

[error] 72-72: Expected a property but instead found '// the material list needs to move down when the menu is visible. these padding elements are visible depending on which bits of the menu are showing. because they're all in a stack_panel, when they're visible, they push the material list lower down.'.

Expected a property here.

(parse)


[error] 73-73: expected , but instead found "type"

Remove "type"

(parse)


[error] 130-130: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 130-130: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 148-148: expected , but instead found // "$button_mappings": [

Remove // "$button_mappings": [

(parse)


[error] 130-147: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 167-185: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 203-203: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 203-203: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 203-203: Expected a property but instead found '// there are two backgrounds here, one for when the full menu is shown. that one has the bottom clipped so it looks like it's more connected to the stuff below'.

Expected a property here.

(parse)


[error] 204-204: expected , but instead found "type"

Remove "type"

(parse)


[error] 203-241: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 241-241: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 242-242: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 242-242: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 242-287: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 287-287: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 288-288: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 288-288: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 288-302: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 302-302: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 303-303: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 303-303: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 385-385: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 386-386: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 386-386: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 386-389: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 389-389: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 390-390: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 390-390: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Test Sample Structures (Firefox)
  • GitHub Check: Test Sample Structures (Chrome)
🔇 Additional comments (8)
src/packTemplate/ui/holoprint_material_list.json (1)

8-20: Dynamic offset + updated max size look consistent

Referencing the shared $holoprint:material_list_offset and tightening the max height matches the new touch layout changes.

src/HoloPrint.js (7)

9-17: Imports refactor looks good.

Switch to fetchers and the new EntityGeoMaker/ResourcePackStack aligns with the new architecture.


154-157: Good: centralizing item_tags.json fetch via fetchers.

This aligns with the new fetchers caching/versioning strategy.


196-200: Entity-aware BlockGeoMaker wiring + async template generation: LGTM.

Instantiating EntityGeoMaker and awaiting makePolyMeshTemplates matches the async pipeline in BlockGeoMaker.


843-845: Good: metadata via fetchers.vanillaData.

Leverages the unified fetch layer and its cache/version guarantees. Based on learnings.


884-884: BlockUpdater construction fix: LGTM.

Using new BlockUpdater() matches the class API.


176-177: All call sites updated to match the new retextureControlItems signature.


220-221: TextureAtlas property rename confirmed
textureWidth/textureHeight are correctly exposed and no atlasWidth/atlasHeight usages remain.

Comment thread src/HoloPrint.js
Comment on lines +125 to +129
[_._]: "textures/ui/menu_sliders_icon.png",
[_._]: "textures/ui/menu_button_unpressed.png",
[_._]: "textures/ui/menu_button_pressed.png",
[_._]: "textures/ui/material_list_button_unpressed.png",
[_._]: "textures/ui/material_list_button_pressed.png",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Confirm new UI assets exist in packTemplate.

These files must be present to avoid 404s at build:

  • textures/ui/menu_sliders_icon.png
  • textures/ui/menu_button_unpressed.png
  • textures/ui/menu_button_pressed.png
  • textures/ui/material_list_button_unpressed.png
  • textures/ui/material_list_button_pressed.png

🏁 Script executed:

#!/bin/bash
# Verify each file exists somewhere under packTemplate/
missing=0
for f in \
  textures/ui/menu_sliders_icon.png \
  textures/ui/menu_button_unpressed.png \
  textures/ui/menu_button_pressed.png \
  textures/ui/material_list_button_unpressed.png \
  textures/ui/material_list_button_pressed.png
do
  if ! fd -HI "$f" packTemplate >/dev/null; then
    echo "MISSING: packTemplate/$f"
    missing=1
  fi
done
exit $missing

Length of output: 204


🏁 Script executed:

#!/usr/bin/env bash
missing=0
files=(
  "textures/ui/menu_sliders_icon.png"
  "textures/ui/menu_button_unpressed.png"
  "textures/ui/menu_button_pressed.png"
  "textures/ui/material_list_button_unpressed.png"
  "textures/ui/material_list_button_pressed.png"
)
for f in "${files[@]}"; do
  fd -HI "^${f}$" packTemplate >/dev/null 2>&1
  if [ $? -ne 0 ]; then
    echo "MISSING: packTemplate/$f"
    missing=1
  fi
done
exit $missing

Length of output: 309


Add missing UI assets to packTemplate
The following assets referenced in HoloPrint.js (lines 125–129) are not present under packTemplate, which will cause 404s at build:

  • textures/ui/menu_sliders_icon.png
  • textures/ui/menu_button_unpressed.png
  • textures/ui/menu_button_pressed.png
  • textures/ui/material_list_button_unpressed.png
  • textures/ui/material_list_button_pressed.png
🤖 Prompt for AI Agents
In src/HoloPrint.js around lines 125 to 129, several UI asset paths referenced
in the array are not included in the packTemplate assets list, causing 404s; add
entries for textures/ui/menu_sliders_icon.png,
textures/ui/menu_button_unpressed.png, textures/ui/menu_button_pressed.png,
textures/ui/material_list_button_unpressed.png, and
textures/ui/material_list_button_pressed.png to the packTemplate (use the same
relative paths used in HoloPrint.js), ensure the files exist under that path in
the repo, and run the build to verify no missing-asset errors.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They do exist, look harder

Comment on lines +64 to 121
"material_list_wrapper": {
"type": "stack_panel",
"size": ["100%", "100% - 62px"],
"anchor_from": "top_middle",
"anchor_to": "top_middle",
"offset": "$holoprint:material_list_offset",
"controls": [
{
"binding_type": "view",
"source_control_name": "material_list_toggle",
"source_property_name": "#toggle_state",
"target_property_name": "#visible"
"condiditional_padding_1": { // the material list needs to move down when the menu is visible. these padding elements are visible depending on which bits of the menu are showing. because they're all in a stack_panel, when they're visible, they push the material list lower down.
"type": "panel",
"size": [0, 35],
"bindings": [
{
"binding_type": "view",
"source_control_name": "expand_menu_toggle",
"source_property_name": "#toggle_state",
"target_property_name": "#visible"
}
]
}
},
{
"condiditional_padding_2": {
"type": "panel",
"size": [0, 27],
"bindings": [
{
"binding_type": "view",
"source_control_name": "expand_menu_toggle",
"source_property_name": "#toggle_state",
"target_property_name": "#expand_menu_toggle_state"
},
{
"binding_type": "view",
"source_control_name": "show_full_menu_toggle",
"source_property_name": "#toggle_state",
"target_property_name": "#show_full_menu_toggle_state"
},
{
"binding_type": "view",
"source_property_name": "(#expand_menu_toggle_state and #show_full_menu_toggle_state)",
"target_property_name": "#visible"
}
]
}
},
{
"material_list@holoprint:material_list.material_list": {
"bindings": [
{
"binding_type": "view",
"source_control_name": "material_list_toggle",
"source_property_name": "#toggle_state",
"target_property_name": "#visible"
}
]
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid double-applying $holoprint:material_list_offset

material_list_wrapper now carries the same offset as the root material_list panel, but the imported template (see holoprint_material_list.json, Line 8) already applies $holoprint:material_list_offset. With both offsets active, the list is pushed down an extra 23px, and the additional padding makes the layout sit noticeably lower than intended even when the menu is collapsed. Please drop the wrapper offset (or the inner one) so the offset is applied only once.

-							"material_list_wrapper": {
+							"material_list_wrapper": {
 								"type": "stack_panel",
 								"size": ["100%", "100% - 62px"],
 								"anchor_from": "top_middle",
 								"anchor_to": "top_middle",
-								"offset": "$holoprint:material_list_offset",
+								"offset": [0, 0],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"material_list_wrapper": {
"type": "stack_panel",
"size": ["100%", "100% - 62px"],
"anchor_from": "top_middle",
"anchor_to": "top_middle",
"offset": "$holoprint:material_list_offset",
"controls": [
{
"binding_type": "view",
"source_control_name": "material_list_toggle",
"source_property_name": "#toggle_state",
"target_property_name": "#visible"
"condiditional_padding_1": { // the material list needs to move down when the menu is visible. these padding elements are visible depending on which bits of the menu are showing. because they're all in a stack_panel, when they're visible, they push the material list lower down.
"type": "panel",
"size": [0, 35],
"bindings": [
{
"binding_type": "view",
"source_control_name": "expand_menu_toggle",
"source_property_name": "#toggle_state",
"target_property_name": "#visible"
}
]
}
},
{
"condiditional_padding_2": {
"type": "panel",
"size": [0, 27],
"bindings": [
{
"binding_type": "view",
"source_control_name": "expand_menu_toggle",
"source_property_name": "#toggle_state",
"target_property_name": "#expand_menu_toggle_state"
},
{
"binding_type": "view",
"source_control_name": "show_full_menu_toggle",
"source_property_name": "#toggle_state",
"target_property_name": "#show_full_menu_toggle_state"
},
{
"binding_type": "view",
"source_property_name": "(#expand_menu_toggle_state and #show_full_menu_toggle_state)",
"target_property_name": "#visible"
}
]
}
},
{
"material_list@holoprint:material_list.material_list": {
"bindings": [
{
"binding_type": "view",
"source_control_name": "material_list_toggle",
"source_property_name": "#toggle_state",
"target_property_name": "#visible"
}
]
}
}
"material_list_wrapper": {
"type": "stack_panel",
"size": ["100%", "100% - 62px"],
"anchor_from": "top_middle",
"anchor_to": "top_middle",
"offset": [0, 0],
"controls": [
{
"condiditional_padding_1": {
"type": "panel",
"size": [0, 35],
"bindings": [
{
"binding_type": "view",
"source_control_name": "expand_menu_toggle",
"source_property_name": "#toggle_state",
"target_property_name": "#visible"
}
]
}
},
{
"condiditional_padding_2": {
"type": "panel",
"size": [0, 27],
"bindings": [
{
"binding_type": "view",
"source_control_name": "expand_menu_toggle",
"source_property_name": "#toggle_state",
"target_property_name": "#expand_menu_toggle_state"
},
{
"binding_type": "view",
"source_control_name": "show_full_menu_toggle",
"source_property_name": "#toggle_state",
"target_property_name": "#show_full_menu_toggle_state"
},
{
"binding_type": "view",
"source_property_name": "(#expand_menu_toggle_state and #show_full_menu_toggle_state)",
"target_property_name": "#visible"
}
]
}
},
{
"material_list@holoprint:material_list.material_list": {
"bindings": [
{
"binding_type": "view",
"source_control_name": "material_list_toggle",
"source_property_name": "#toggle_state",
"target_property_name": "#visible"
}
]
}
}
🧰 Tools
🪛 Biome (2.1.2)

[error] 72-72: Expected a property but instead found '// the material list needs to move down when the menu is visible. these padding elements are visible depending on which bits of the menu are showing. because they're all in a stack_panel, when they're visible, they push the material list lower down.'.

Expected a property here.

(parse)


[error] 73-73: expected , but instead found "type"

Remove "type"

(parse)

🤖 Prompt for AI Agents
In src/packTemplate/ui/holoprint_touch_buttons.json around lines 64 to 121, the
material_list_wrapper is applying $holoprint:material_list_offset while the
imported holoprint_material_list template already applies that same offset,
causing a double 23px shift; remove the duplicate offset by deleting the
"offset": "$holoprint:material_list_offset" entry from the material_list_wrapper
(or alternatively remove the offset from the inner imported material_list) so
the offset is applied only once.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I already told you, don't say ANYTHING about ANYTHING in the packTemplate/ui folder.

@SuperLlama88888 SuperLlama88888 marked this pull request as ready for review September 27, 2025 00:03
@SuperLlama88888 SuperLlama88888 merged commit 960e06a into main Sep 27, 2025
5 checks passed
@SuperLlama88888 SuperLlama88888 deleted the dev branch September 27, 2025 00:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant