Skip to content

[4k] REWORK inventory GUI : Spread, Anvil, Enchant etc#518

Open
zardoy wants to merge 16 commits intonextfrom
new-inventory
Open

[4k] REWORK inventory GUI : Spread, Anvil, Enchant etc#518
zardoy wants to merge 16 commits intonextfrom
new-inventory

Conversation

@zardoy
Copy link
Copy Markdown
Owner

@zardoy zardoy commented Mar 15, 2026

A lot of code to be reviewed/finished and maybe simplified on integration part (here)

CHECKLIST:

  • All inventory types
  • Furnace has progress bar
  • Custom block model works
  • Player model in inventory integrated
  • Hotbar integrated (with same offhand/mobile button managment)
  • Recipes / usages integrated. JEI with full blocks & items display integrated
  • Spread operations work on play.mcraft.fun
  • Enchantment / anvil / beacon / cartography work on play.mcraft.fun (ask me if needs packet logs from vanilla)

Summary by CodeRabbit

  • New Features

    • Full-screen in-game inventory overlay with JEI-style recipe/usages browsing, improved item/block rendering, and per-instance model viewer stacking.
    • Hotbar and in-game UI now render via the new inventory components; legacy GUI plumbing replaced.
    • Inventory visuals auto-refresh when resource packs or textures change.
  • Chores

    • Updated dependency to the new inventory rendering package to enable these changes.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 15, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a React-based Inventory overlay using the minecraft-inventory package, replaces legacy canvas/window inventory plumbing with modal-driven React components, implements atlas sprite/block-face extraction and caching, plugs resource-pack texture overrides, and exposes JEI recipe/usages helpers.

Changes

Cohort / File(s) Summary
Dependency manifest
package.json
Add minecraft-inventory (^0.1.7); remove minecraft-inventory-gui from devDependencies.
React inventory UI
src/react/inventory/Inventory.tsx
New Inventory component (portal overlay), mounts mineflayer connector, provides Texture/Scale/Inventory contexts, handles JEI callbacks, listens to asset updates to clear caches and bump textureVersion, and re-exports clearInventoryCaches.
Inventory shared setup
src/react/inventory/sharedConnectorSetup.ts
New item/texture mapper, sprite cache, clearInventoryCaches(), buildItemMapper(version), textureConfig, and formatWindowTitle() for mapping slots to textures/block faces and formatting titles.
Textures config
src/react/inventory/inventoryTexturesConfig.ts
New singleton inventoryBundledConfig from minecraft-inventory for overrides and rendered-slot control.
Resource-pack handling
src/resourcePack.ts
Apply inventory GUI texture overrides from active resource-pack into inventoryBundledConfig (base64 data URLs), clear prior overrides, and reset rendered slots.
Inventory registry & JEI helpers
src/inventoryWindows.ts
Remove legacy minecraft-inventory-gui window/canvas logic; switch to modal-driven showModal({ reactType: ... }); add exports getItemRecipes, getItemUsages, getJeiItems; openPlayerInventory now shows player_win:player.
Hotbar rendering
src/react/HotbarRenderApp.tsx
Refactor hotbar to use minecraft-inventory components/providers, clear caches on asset updates, update connector recreation logic, and fix bot game handler install/remove.
UI integration
src/reactUi.tsx
Render <Inventory /> within in-game UI boundary.
Replacable resources cleanup
src/resourcesSource.ts
Remove legacy container GUI texture entries from appReplacableResources.
Overlay model viewer
src/react/OverlayModelViewer.tsx
Add optional zIndex?: number to modelViewerState.model and use it for overlay stacking order.

Sequence Diagram(s)

sequenceDiagram
    participant App as App/UI
    participant InventoryComp as Inventory Component
    participant Connector as Mineflayer Connector
    participant Providers as Texture/Scale/Inventory Providers
    participant Overlay as Inventory Overlay (JEI)
    participant ResourcePack as ResourcePack Manager

    App->>InventoryComp: render <Inventory/>
    InventoryComp->>ResourcePack: read textures / subscribe assets events
    InventoryComp->>Connector: createMineflayerConnector(bot, itemMapper) [memoized]
    Connector-->>InventoryComp: connector instance
    InventoryComp->>Providers: wrap overlay with texture & scale contexts
    InventoryComp->>Overlay: render overlay (type from modal stack)
    Overlay->>InventoryComp: request item recipes/usages
    InventoryComp->>InventoryComp: call getItemRecipes / getItemUsages
    ResourcePack->>InventoryComp: assetsTexturesUpdated / assetsInventoryReady
    InventoryComp->>InventoryComp: clearInventoryCaches(), bump textureVersion, recreate connector if needed
    Overlay-->>App: UI rendered / user interactions (close)
    Overlay->>InventoryComp: handleClose -> connector close + hide modal
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

Review effort 4/5

Poem

🐇 I nibble pixels, stitch each slot,
New inventory springs from a joyful plot.
Textures hum and JEI tips in tow,
Overlays pop and models glow.
Hop—this rabbit crowns the show! 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'REWORK inventory GUI : Spread, Anvil, Enchant etc' accurately reflects the main change: a comprehensive rewrite of the inventory GUI system and related components, supported by extensive modifications across multiple files including the addition of the minecraft-inventory package and migration from legacy GUI.
Docstring Coverage ✅ Passed Docstring coverage is 85.71% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch new-inventory

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.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/react/inventory/Inventory.tsx (2)

11-11: Remove unused windowType variable.

The windowType constant is declared but never used in this component.

♻️ Proposed fix
 export const Inventory = () => {
-  const windowType = 'player'
   const connector = useMemo(() => createMineflayerConnector(bot as MineflayerBot), [bot])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/react/inventory/Inventory.tsx` at line 11, The Inventory component
declares an unused constant windowType = 'player'; remove this dead variable
from the component (delete the windowType declaration) to clean up unused code
and avoid linter warnings; search for windowType in the Inventory function to
ensure it's not referenced elsewhere before removing.

1-1: Consider importing from the package root instead of /src.

Importing directly from 'minecraft-inventory/src' couples this code to the package's internal structure. If the package has proper exports configured, prefer importing from 'minecraft-inventory'. This reduces brittleness if the package's internal structure changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/react/inventory/Inventory.tsx` at line 1, The import at the top currently
pulls internal modules from 'minecraft-inventory/src'; change it to import the
same symbols (TextureProvider, ScaleProvider, InventoryProvider,
InventoryOverlay, createMineflayerConnector, MineflayerBot,
InventoryOverlayProps) from the package root 'minecraft-inventory' so the code
depends on the public API rather than internal paths; update the import
statement to reference 'minecraft-inventory' and keep the same named imports to
avoid breaking usages elsewhere in Inventory.tsx.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/react/inventory/Inventory.tsx`:
- Line 12: Guard against bot being undefined by only calling
createMineflayerConnector when bot is present and return a safe fallback (e.g.,
null) otherwise; update the useMemo for connector (the const connector =
useMemo(...) line) to check the global bot before invoking
createMineflayerConnector and ensure connector is typed/handled for null. Also
stop relying solely on the global bot in the dependency array: wire the
Inventory component to a React-tracked source (e.g., local state or context that
holds the bot) or subscribe to bot changes in useEffect so re-renders occur when
a new bot is assigned, and use that tracked value in the useMemo dependency
instead of the raw global.

---

Nitpick comments:
In `@src/react/inventory/Inventory.tsx`:
- Line 11: The Inventory component declares an unused constant windowType =
'player'; remove this dead variable from the component (delete the windowType
declaration) to clean up unused code and avoid linter warnings; search for
windowType in the Inventory function to ensure it's not referenced elsewhere
before removing.
- Line 1: The import at the top currently pulls internal modules from
'minecraft-inventory/src'; change it to import the same symbols
(TextureProvider, ScaleProvider, InventoryProvider, InventoryOverlay,
createMineflayerConnector, MineflayerBot, InventoryOverlayProps) from the
package root 'minecraft-inventory' so the code depends on the public API rather
than internal paths; update the import statement to reference
'minecraft-inventory' and keep the same named imports to avoid breaking usages
elsewhere in Inventory.tsx.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 23a521d2-6614-4c89-b632-ac26097127dc

📥 Commits

Reviewing files that changed from the base of the PR and between 3877d83 and daf4709.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (3)
  • package.json
  • src/react/inventory/Inventory.tsx
  • src/reactUi.tsx


export const Inventory = () => {
const windowType = 'player'
const connector = useMemo(() => createMineflayerConnector(bot as MineflayerBot), [bot])
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

Guard against bot being undefined to prevent runtime crash.

The global bot variable can be undefined during disconnect or before connection (see src/index.ts where bot = undefined and src/globals.js where it starts as undefined). Calling createMineflayerConnector(bot as MineflayerBot) when bot is undefined will likely crash.

Additionally, useMemo with [bot] as a dependency may not re-trigger correctly since bot is a global variable that React doesn't track. When a new bot instance is assigned, this component won't automatically re-render.

🛡️ Proposed fix for null safety
 export const Inventory = () => {
   const windowType = 'player'
+  const { inventory } = useSnapshot(openInventoryProxy) as typeof openInventoryProxy
+
+  // Early return before creating connector if no bot or inventory
+  if (!inventory || !bot) return null
+
   const connector = useMemo(() => createMineflayerConnector(bot as MineflayerBot), [bot])
   const appScale = useAppScale()
-  const { inventory } = useSnapshot(openInventoryProxy) as typeof openInventoryProxy
-
-  if (!inventory) return null

Note: The useMemo dependency on global bot may still not work as expected for re-renders. Consider using a state or context pattern to track the bot instance if reconnection scenarios need to be supported.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/react/inventory/Inventory.tsx` at line 12, Guard against bot being
undefined by only calling createMineflayerConnector when bot is present and
return a safe fallback (e.g., null) otherwise; update the useMemo for connector
(the const connector = useMemo(...) line) to check the global bot before
invoking createMineflayerConnector and ensure connector is typed/handled for
null. Also stop relying solely on the global bot in the dependency array: wire
the Inventory component to a React-tracked source (e.g., local state or context
that holds the bot) or subscribe to bot changes in useEffect so re-renders occur
when a new bot is assigned, and use that tracked value in the useMemo dependency
instead of the raw global.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

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 (2)
src/inventoryWindows.ts (2)

49-84: Remove the large commented-out block in showInventoryPlayer.

This block now hides behavior intent and increases noise. Prefer deleting it (or moving rationale to a short comment / issue link).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/inventoryWindows.ts` around lines 49 - 84, Remove the large commented-out
block inside showInventoryPlayer that contains the old
openInventoryProxy.inventory/modelViewerState.model setup and the
remotePlayerSkin loading logic; delete those commented lines (or replace them
with a single short comment or an issue/PR link explaining why the code was
removed) and keep any needed behavior implemented in the active code paths
(references: showInventoryPlayer, openInventoryProxy.inventory,
modelViewerState.model, remotePlayerSkin, loadSkinFromUsername,
appViewer.playerState.reactive.playerSkin, lastWindowType).

28-28: Decouple proxy state from the UI component module.

Importing openInventoryProxy from src/react/inventory/Inventory.tsx couples this core file to the React/UI stack. Move proxy state to a small shared module (state-only) and import from both sides.

♻️ Proposed refactor
- import { openInventoryProxy } from './react/inventory/Inventory'
+ import { openInventoryProxy } from './react/inventory/state'
// src/react/inventory/state.ts
import { proxy } from 'valtio'
import type { InventoryOverlayProps } from 'minecraft-inventory/src'

export const openInventoryProxy = proxy({
  inventory: undefined as InventoryOverlayProps | undefined,
})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/inventoryWindows.ts` at line 28, Create a small shared state module that
exports the Valtio proxy and stop importing openInventoryProxy from the React
component module: move the proxy declaration into a new module (e.g., state.ts)
that creates and exports openInventoryProxy = proxy({ inventory: undefined as
InventoryOverlayProps | undefined }), update both the UI component (where
openInventoryProxy was defined) and the core file that currently imports it to
import openInventoryProxy from this new shared module, and remove the direct
dependency on the React component (so symbols to change are openInventoryProxy,
the proxy definition location, and the imports in the modules that currently
reference it).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/react/inventory/Inventory.tsx`:
- Around line 1-4: The file is deep-importing internals (TextureProvider,
ScaleProvider, InventoryProvider, InventoryOverlay, createMineflayerConnector,
MineflayerBot, InventoryOverlayProps and localTexturesConfig) from
'minecraft-inventory/src' and 'minecraft-inventory/src/generated/localTextures';
replace those usages by either importing the same symbols from the package's
documented public entry (e.g., 'minecraft-inventory') or create a local adapter
module (e.g., src/lib/minecraftInventoryAdapter.ts) that re-exports only the
stable API your code needs and/or provides a local copy/wrapper for
localTexturesConfig; update Inventory.tsx to import from that public package or
your adapter so you no longer rely on private/internal paths.
- Around line 29-33: openPlayerInventory() sets openInventoryProxy.inventory = {
type: 'player' } but there is no cleanup to reset it when the modal closes;
update the modal close path (e.g., the hideCurrentModal() call or the close
callback used by InventoryOverlay) to clear openInventoryProxy.inventory =
undefined, and also ensure InventoryOverlay/unmount cleanup resets it (use a
component unmount handler or the onClose prop) so the proxy doesn't persist
after windowClose.

---

Nitpick comments:
In `@src/inventoryWindows.ts`:
- Around line 49-84: Remove the large commented-out block inside
showInventoryPlayer that contains the old
openInventoryProxy.inventory/modelViewerState.model setup and the
remotePlayerSkin loading logic; delete those commented lines (or replace them
with a single short comment or an issue/PR link explaining why the code was
removed) and keep any needed behavior implemented in the active code paths
(references: showInventoryPlayer, openInventoryProxy.inventory,
modelViewerState.model, remotePlayerSkin, loadSkinFromUsername,
appViewer.playerState.reactive.playerSkin, lastWindowType).
- Line 28: Create a small shared state module that exports the Valtio proxy and
stop importing openInventoryProxy from the React component module: move the
proxy declaration into a new module (e.g., state.ts) that creates and exports
openInventoryProxy = proxy({ inventory: undefined as InventoryOverlayProps |
undefined }), update both the UI component (where openInventoryProxy was
defined) and the core file that currently imports it to import
openInventoryProxy from this new shared module, and remove the direct dependency
on the React component (so symbols to change are openInventoryProxy, the proxy
definition location, and the imports in the modules that currently reference
it).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a78e14d7-fd00-45ae-9f6a-7f804f369c79

📥 Commits

Reviewing files that changed from the base of the PR and between daf4709 and c105fd3.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (3)
  • package.json
  • src/inventoryWindows.ts
  • src/react/inventory/Inventory.tsx

Comment on lines +1 to +4
import { TextureProvider, ScaleProvider, InventoryProvider, InventoryOverlay, createMineflayerConnector, MineflayerBot, InventoryOverlayProps } from 'minecraft-inventory/src'
import { useMemo } from 'react'
import { proxy, useSnapshot } from 'valtio'
import { localTexturesConfig } from 'minecraft-inventory/src/generated/localTextures'
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

🧩 Analysis chain

🌐 Web query:

For npm package minecraft-inventory@0.1.4, are minecraft-inventory/srcandminecraft-inventory/src/generated/localTextures documented public exports, or internal/private paths?

💡 Result:

The npm package minecraft-inventory@0.1.4 exists, as listed on libraries.io with a recent publication date (2026-03-14). It is described as "A flexible, scalable React library for rendering Minecraft inventory GUIs. Designed for integration into real Minecraft clients (e.g., ...". No official documentation, package.json exports, or README details the public API or mention minecraft-inventory/src or minecraft-inventory/src/generated/localTextures as documented exports. These paths appear to be internal/private source directories, typical for non-exported implementation files in npm packages (e.g., src folders are not public exports unless explicitly mapped in package.json "exports" or "files"). No sources indicate they are public or documented.

Citations:


Avoid deep-importing minecraft-inventory internals.

Lines 1 and 4 import from minecraft-inventory/src and minecraft-inventory/src/generated/localTextures, which are internal package paths with no documented public API. These imports couple the code to private implementation details and risk breaking on package updates. Use only public exports from the package, or wrap unstable dependencies in a local adapter module.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/react/inventory/Inventory.tsx` around lines 1 - 4, The file is
deep-importing internals (TextureProvider, ScaleProvider, InventoryProvider,
InventoryOverlay, createMineflayerConnector, MineflayerBot,
InventoryOverlayProps and localTexturesConfig) from 'minecraft-inventory/src'
and 'minecraft-inventory/src/generated/localTextures'; replace those usages by
either importing the same symbols from the package's documented public entry
(e.g., 'minecraft-inventory') or create a local adapter module (e.g.,
src/lib/minecraftInventoryAdapter.ts) that re-exports only the stable API your
code needs and/or provides a local copy/wrapper for localTexturesConfig; update
Inventory.tsx to import from that public package or your adapter so you no
longer rely on private/internal paths.

@zardoy zardoy changed the title New inventory REWORK inventory GUI : Spread, Anvil, Enchant etc Mar 30, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/react/inventory/Inventory.tsx (2)

170-175: ⚠️ Potential issue | 🟠 Major

Guard against bot being undefined.

The bot global can be undefined during disconnect or before connection. Accessing bot as MineflayerBot and bot.version without a guard will crash. This concern was raised in a previous review and the suggested fix pattern applies here.

🛡️ Proposed fix
+  // Early return if bot is not available
+  if (!bot) return null
+
   const connector = useMemo(() => {
     if (!inventoryType) return null
-    return createMineflayerConnector(bot as MineflayerBot, {
+    return createMineflayerConnector(bot, {
       itemMapper: buildItemMapper(bot.version),
     })
   }, [textureVersion, !!inventoryType])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/react/inventory/Inventory.tsx` around lines 170 - 175, The connector
useMemo must guard against bot being undefined to avoid crashes; inside the
useMemo for connector (the createMineflayerConnector call in Inventory.tsx)
return null immediately if !bot or !inventoryType, and only call
createMineflayerConnector with bot (typed as MineflayerBot) and
buildItemMapper(bot.version) when bot is defined; also update the dependency
array to include bot (and keep textureVersion and inventoryType) so the memo
recalculates when bot changes.

208-211: ⚠️ Potential issue | 🟡 Minor

Add null check for bot.currentWindow.

While bot.currentWindow?.close?.() uses optional chaining, accessing bot itself could fail if undefined. Consider guarding or ensuring this callback is only invoked when bot is available.

🛡️ Proposed fix
   const handleClose = useCallback(() => {
-    if (bot.currentWindow) (bot.currentWindow as any).close?.()
+    bot?.currentWindow?.close?.()
     hideCurrentModal()
   }, [])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/react/inventory/Inventory.tsx` around lines 208 - 211, The handleClose
callback can throw if bot is undefined; update it to guard that bot exists
before accessing bot.currentWindow (e.g., if (!bot) return), then safely call
(bot.currentWindow as any)?.close?.(); also include bot and hideCurrentModal in
the useCallback dependency array so the memoized handler stays correct;
reference the handleClose function, bot.currentWindow access, and
hideCurrentModal when making these changes.
🧹 Nitpick comments (3)
src/inventoryWindows.ts (1)

177-245: Consider removing commented-out legacy code.

The commented-out legacy functions (getAllItemRecipesLegacy, getAllItemUsagesLegacy) span 70+ lines. If these are no longer needed, removing them would improve code clarity. If they're kept for reference, consider moving to a separate file or documentation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/inventoryWindows.ts` around lines 177 - 245, Remove the large
commented-out legacy blocks for getAllItemRecipesLegacy and
getAllItemUsagesLegacy from src/inventoryWindows.ts (they start near the
ingredientToItem helper) or move them into a dedicated legacy/archival file if
you want to keep them for reference; ensure any referenced helper symbols used
only by those blocks (e.g., ingredientToItem, PrismarineItem, mapSlots) remain
used elsewhere or are cleaned up too, and run a quick compile/check after
deletion to confirm no unused symbol errors.
src/react/inventory/Inventory.tsx (1)

178-189: Duplicate event listeners with inventoryWindows.ts.

Both this component (lines 183-184) and inventoryWindows.ts (lines 117-118) register listeners for assetsInventoryReady and assetsTexturesUpdated that call clearInventoryCaches(). This causes resetRenderedSlots() to be called multiple times per event.

Consider centralizing the listener registration in one location to avoid redundant work.

♻️ Option: Remove listeners here and rely on inventoryWindows.ts

The listeners in inventoryWindows.ts already call clearInventoryCaches(). You could remove them here and just use textureVersion bump via a different mechanism, or consolidate all refresh logic in one place.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/react/inventory/Inventory.tsx` around lines 178 - 189, The Inventory
component's useEffect in Inventory.tsx is registering the same
'assetsTexturesUpdated' and 'assetsInventoryReady' listeners that
inventoryWindows.ts also registers, causing duplicate clearInventoryCaches()
calls; remove the listener registration and teardown from Inventory.tsx's
useEffect (the refresh callback and the appViewer.resourcesManager.on/off calls)
and rely on the centralized registration in inventoryWindows.ts (or consolidate
the refresh logic into a new shared function), and if Inventory still needs to
update its textureVersion state, have inventoryWindows.ts call a shared updater
or emit a single consolidated event so Inventory can bump setTextureVersion
without causing duplicate clearInventoryCaches() invocations.
src/resourcePack.ts (1)

568-580: Consider parallelizing texture override loading for faster resource pack application.

The sequential await in the loop works correctly but could be optimized for resource packs with many GUI textures. Additionally, resetRenderedSlots() is called here and also triggered by event listeners in Inventory.tsx (via clearInventoryCaches()), which may cause redundant re-renders during resource pack installation.

♻️ Optional: Parallelize texture loading
   // Apply resource-pack overrides for inventory GUI textures via the bundled config
   inventoryBundledConfig.clearOverrides()
   if (basePath) {
-    for (const texPath of allTexturePaths) {
-      const fsPath = `${basePath}/assets/minecraft/textures/${texPath}`
-      if (await existsAsync(fsPath)) {
-        const file = await fs.promises.readFile(fsPath, 'base64')
-        inventoryBundledConfig.setOverride(texPath, `data:image/png;base64,${file}`)
-      }
-    }
+    await Promise.all(allTexturePaths.map(async (texPath) => {
+      const fsPath = `${basePath}/assets/minecraft/textures/${texPath}`
+      if (await existsAsync(fsPath)) {
+        const file = await fs.promises.readFile(fsPath, 'base64')
+        inventoryBundledConfig.setOverride(texPath, `data:image/png;base64,${file}`)
+      }
+    }))
   }
   inventoryBundledConfig.resetRenderedSlots()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/resourcePack.ts` around lines 568 - 580, The current loop in
resourcePack.ts sequentially awaits existsAsync and fs.promises.readFile for
each path (allTexturePaths) which is slow for many textures and then calls
inventoryBundledConfig.resetRenderedSlots(), duplicating resets already
triggered by Inventory.tsx via clearInventoryCaches(); change to run existence
checks and reads in parallel (e.g., map allTexturePaths to Promises and await
Promise.all, then call inventoryBundledConfig.setOverride for successful
results) and remove the immediate inventoryBundledConfig.resetRenderedSlots()
here so the existing clearInventoryCaches()/Inventory.tsx listener handles
rendering updates (or debounce/emit a single event) to avoid redundant
re-renders.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/react/HotbarRenderApp.tsx`:
- Around line 238-239: HotbarInner is now dead code but still references removed
functions openItemsCanvas and upInventoryItems; either delete the HotbarInner
component entirely from HotbarRenderApp (and any exports/usages) if the hotbar
is deprecated, or update HotbarInner to call the new inventory API: replace
openItemsCanvas and upInventoryItems calls inside HotbarInner with the
corresponding new functions from the current inventory system (or prop
callbacks) and adjust imports/props accordingly so HotbarInner no longer
references nonexistent symbols; also re-enable the original return in
HotbarRenderApp only after these references are fixed.

---

Duplicate comments:
In `@src/react/inventory/Inventory.tsx`:
- Around line 170-175: The connector useMemo must guard against bot being
undefined to avoid crashes; inside the useMemo for connector (the
createMineflayerConnector call in Inventory.tsx) return null immediately if !bot
or !inventoryType, and only call createMineflayerConnector with bot (typed as
MineflayerBot) and buildItemMapper(bot.version) when bot is defined; also update
the dependency array to include bot (and keep textureVersion and inventoryType)
so the memo recalculates when bot changes.
- Around line 208-211: The handleClose callback can throw if bot is undefined;
update it to guard that bot exists before accessing bot.currentWindow (e.g., if
(!bot) return), then safely call (bot.currentWindow as any)?.close?.(); also
include bot and hideCurrentModal in the useCallback dependency array so the
memoized handler stays correct; reference the handleClose function,
bot.currentWindow access, and hideCurrentModal when making these changes.

---

Nitpick comments:
In `@src/inventoryWindows.ts`:
- Around line 177-245: Remove the large commented-out legacy blocks for
getAllItemRecipesLegacy and getAllItemUsagesLegacy from src/inventoryWindows.ts
(they start near the ingredientToItem helper) or move them into a dedicated
legacy/archival file if you want to keep them for reference; ensure any
referenced helper symbols used only by those blocks (e.g., ingredientToItem,
PrismarineItem, mapSlots) remain used elsewhere or are cleaned up too, and run a
quick compile/check after deletion to confirm no unused symbol errors.

In `@src/react/inventory/Inventory.tsx`:
- Around line 178-189: The Inventory component's useEffect in Inventory.tsx is
registering the same 'assetsTexturesUpdated' and 'assetsInventoryReady'
listeners that inventoryWindows.ts also registers, causing duplicate
clearInventoryCaches() calls; remove the listener registration and teardown from
Inventory.tsx's useEffect (the refresh callback and the
appViewer.resourcesManager.on/off calls) and rely on the centralized
registration in inventoryWindows.ts (or consolidate the refresh logic into a new
shared function), and if Inventory still needs to update its textureVersion
state, have inventoryWindows.ts call a shared updater or emit a single
consolidated event so Inventory can bump setTextureVersion without causing
duplicate clearInventoryCaches() invocations.

In `@src/resourcePack.ts`:
- Around line 568-580: The current loop in resourcePack.ts sequentially awaits
existsAsync and fs.promises.readFile for each path (allTexturePaths) which is
slow for many textures and then calls
inventoryBundledConfig.resetRenderedSlots(), duplicating resets already
triggered by Inventory.tsx via clearInventoryCaches(); change to run existence
checks and reads in parallel (e.g., map allTexturePaths to Promises and await
Promise.all, then call inventoryBundledConfig.setOverride for successful
results) and remove the immediate inventoryBundledConfig.resetRenderedSlots()
here so the existing clearInventoryCaches()/Inventory.tsx listener handles
rendering updates (or debounce/emit a single event) to avoid redundant
re-renders.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8f64164a-fbe5-4eba-8187-0e01cb2b88f4

📥 Commits

Reviewing files that changed from the base of the PR and between c105fd3 and c8da2a1.

⛔ Files ignored due to path filters (6)
  • assets/generic_91.png is excluded by !**/*.png
  • assets/generic_92.png is excluded by !**/*.png
  • assets/generic_93.png is excluded by !**/*.png
  • assets/generic_94.png is excluded by !**/*.png
  • assets/generic_95.png is excluded by !**/*.png
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (7)
  • package.json
  • src/inventoryWindows.ts
  • src/react/HotbarRenderApp.tsx
  • src/react/inventory/Inventory.tsx
  • src/react/inventory/inventoryTexturesConfig.ts
  • src/resourcePack.ts
  • src/resourcesSource.ts
💤 Files with no reviewable changes (1)
  • src/resourcesSource.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • package.json

@zardoy zardoy changed the title REWORK inventory GUI : Spread, Anvil, Enchant etc [3k] REWORK inventory GUI : Spread, Anvil, Enchant etc Apr 1, 2026
@zardoy zardoy changed the title [3k] REWORK inventory GUI : Spread, Anvil, Enchant etc [4k] REWORK inventory GUI : Spread, Anvil, Enchant etc Apr 3, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/react/inventory/Inventory.tsx (1)

75-81: ⚠️ Potential issue | 🟡 Minor

Guard against bot being undefined during disconnect.

While the connector is only created when inventoryType is truthy (which implies an active game session), there's a potential race condition if the player disconnects while the inventory modal is open. The bot global can become undefined during disconnect.

The risk is low since the modal would typically close before or during disconnect, but adding a defensive check would make the code more robust.

🛡️ Proposed defensive fix
   const connector = useMemo(() => {
-    if (!inventoryType) return null
+    if (!inventoryType || !bot) return null
     return createMineflayerConnector(bot as MineflayerBot, {
       itemMapper: buildItemMapper(bot.version),
       formatTitle: formatWindowTitle,
     })
   }, [textureVersion, !!inventoryType])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/react/inventory/Inventory.tsx` around lines 75 - 81, The connector
creation in the useMemo for connector can run when bot becomes undefined during
a disconnect; update the useMemo guard to also check that bot is defined before
calling createMineflayerConnector so you only create the connector when
inventoryType is truthy and bot (typed as MineflayerBot) is non-null; keep the
same deps (textureVersion, !!inventoryType) but reference bot in the condition
and avoid calling buildItemMapper or formatWindowTitle if bot is undefined to
prevent runtime errors.
🧹 Nitpick comments (3)
src/react/inventory/sharedConnectorSetup.ts (1)

36-48: Consider adding error context to the catch block.

The empty catch block silently swallows all errors during canvas extraction. While returning undefined is a reasonable fallback, logging the error (at debug level) would help diagnose rendering issues when debugging.

🔧 Optional: Add debug logging
   try {
     const canvas = document.createElement('canvas')
     canvas.width = w
     canvas.height = h
     const ctx = canvas.getContext('2d')!
     ctx.drawImage(atlas, x, y, w, h, 0, 0, w, h)
     const url = canvas.toDataURL()
     spriteCache.set(cacheKey, url)
     return url
-  } catch {
+  } catch (err) {
+    console.debug('[sharedConnectorSetup] sprite extraction failed:', texture, slice, err)
     return undefined
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/react/inventory/sharedConnectorSetup.ts` around lines 36 - 48, The catch
block in the canvas extraction silently swallows errors—modify the try/catch in
sharedConnectorSetup.ts (the block that creates a canvas, draws the atlas and
returns url, referencing variables atlas, cacheKey, spriteCache) to catch the
error as a variable and log it at debug level before returning undefined;
include contextual info (cacheKey and sprite coordinates/dimensions x,y,w,h) in
the debug message so failures are traceable (use the project logger if
available, otherwise console.debug).
src/react/HotbarRenderApp.tsx (2)

147-152: Minor inefficiency: Multiple clearInventoryCaches() subscriptions.

Both HotbarRenderApp and Inventory subscribe to the same assetsTexturesUpdated / assetsInventoryReady events and call clearInventoryCaches(). Additionally, inventoryWindows.ts (lines 115-120) has a global subscription with a guard flag.

When both components are mounted, clearInventoryCaches() may be invoked 2-3 times per event. Since the operations are idempotent (clearing a Map and resetting state), this is functionally correct but slightly inefficient.

Consider centralizing this subscription in inventoryWindows.ts and using a reactive pattern (like a version counter in a shared state) that components can observe instead of each component independently subscribing.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/react/HotbarRenderApp.tsx` around lines 147 - 152, HotbarRenderApp and
Inventory both subscribe to appViewer.resourcesManager events and call
clearInventoryCaches(), causing duplicate work; instead centralize the
subscriptions in the existing inventoryWindows module: move the
appViewer.resourcesManager.on('assetsTexturesUpdated'|'assetsInventoryReady',
...) handlers (the ones currently calling clearInventoryCaches and incrementing
texture/version) into inventoryWindows.ts (use or extend its existing guard
flag), expose a single shared reactive version counter or getter (e.g.,
inventoryTextureVersion or a subscribe/observable) that components
(HotbarRenderApp uses setTextureVersion and Inventory) read or subscribe to;
then remove the duplicate appViewer.resourcesManager.on(...) calls from
HotbarRenderApp and Inventory so clearing happens once and components react to
the shared version.

5-12: Deep imports from minecraft-inventory/src are consistent across the codebase, but acceptable for this private dependency.

The imports access internal package paths (/src/bundledTexturesConfig, /src/types, /src/registry, /src/components/JEI/JEI) rather than a documented public entry point. However, since minecraft-inventory is maintained by the same author (zardoy) as this project and is not publicly published on npm, coordinated updates between the consumer and dependency mitigate the typical risk of deep imports breaking on package updates.

The pattern is used consistently across multiple files (Inventory.tsx, inventoryWindows.ts, sharedConnectorSetup.ts, resourcePack.ts), indicating an intentional architectural decision.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/react/HotbarRenderApp.tsx` around lines 5 - 12, Deep imports from
'minecraft-inventory/src' are used here (importing TextureProvider,
ScaleProvider, InventoryProvider, InventoryWindow, createMineflayerConnector,
MineflayerBot) and are intentional for this private dependency; add a short
explanatory comment above these imports stating that deep imports are deliberate
because 'minecraft-inventory' is a private, in-repo dependency maintained by the
same author and may not expose a public entry point, and mention that any future
public entry point should replace these deep imports when available; do not
change the actual import lines.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/react/inventory/sharedConnectorSetup.ts`:
- Around line 142-177: The null/undefined check in formatWindowTitle uses loose
equality (`rawTitle == null`); change it to an explicit check for both null and
undefined (i.e., test rawTitle === null || rawTitle === undefined) to satisfy
ESLint and make intent clear; update the guard at the top of formatWindowTitle
accordingly and leave all other logic untouched.

---

Duplicate comments:
In `@src/react/inventory/Inventory.tsx`:
- Around line 75-81: The connector creation in the useMemo for connector can run
when bot becomes undefined during a disconnect; update the useMemo guard to also
check that bot is defined before calling createMineflayerConnector so you only
create the connector when inventoryType is truthy and bot (typed as
MineflayerBot) is non-null; keep the same deps (textureVersion, !!inventoryType)
but reference bot in the condition and avoid calling buildItemMapper or
formatWindowTitle if bot is undefined to prevent runtime errors.

---

Nitpick comments:
In `@src/react/HotbarRenderApp.tsx`:
- Around line 147-152: HotbarRenderApp and Inventory both subscribe to
appViewer.resourcesManager events and call clearInventoryCaches(), causing
duplicate work; instead centralize the subscriptions in the existing
inventoryWindows module: move the
appViewer.resourcesManager.on('assetsTexturesUpdated'|'assetsInventoryReady',
...) handlers (the ones currently calling clearInventoryCaches and incrementing
texture/version) into inventoryWindows.ts (use or extend its existing guard
flag), expose a single shared reactive version counter or getter (e.g.,
inventoryTextureVersion or a subscribe/observable) that components
(HotbarRenderApp uses setTextureVersion and Inventory) read or subscribe to;
then remove the duplicate appViewer.resourcesManager.on(...) calls from
HotbarRenderApp and Inventory so clearing happens once and components react to
the shared version.
- Around line 5-12: Deep imports from 'minecraft-inventory/src' are used here
(importing TextureProvider, ScaleProvider, InventoryProvider, InventoryWindow,
createMineflayerConnector, MineflayerBot) and are intentional for this private
dependency; add a short explanatory comment above these imports stating that
deep imports are deliberate because 'minecraft-inventory' is a private, in-repo
dependency maintained by the same author and may not expose a public entry
point, and mention that any future public entry point should replace these deep
imports when available; do not change the actual import lines.

In `@src/react/inventory/sharedConnectorSetup.ts`:
- Around line 36-48: The catch block in the canvas extraction silently swallows
errors—modify the try/catch in sharedConnectorSetup.ts (the block that creates a
canvas, draws the atlas and returns url, referencing variables atlas, cacheKey,
spriteCache) to catch the error as a variable and log it at debug level before
returning undefined; include contextual info (cacheKey and sprite
coordinates/dimensions x,y,w,h) in the debug message so failures are traceable
(use the project logger if available, otherwise console.debug).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fd9b52b3-4a46-4a5e-9de2-e298f9e1f1bc

📥 Commits

Reviewing files that changed from the base of the PR and between c8da2a1 and f219eae.

📒 Files selected for processing (4)
  • src/react/HotbarRenderApp.tsx
  • src/react/OverlayModelViewer.tsx
  • src/react/inventory/Inventory.tsx
  • src/react/inventory/sharedConnectorSetup.ts

Comment on lines +142 to +177
export function formatWindowTitle (rawTitle: any): string {
if (rawTitle == null) return ''
if (typeof rawTitle === 'string') {
// Try to parse JSON text component
if (rawTitle.startsWith('{') || rawTitle.startsWith('"')) {
try {
return formatWindowTitle(JSON.parse(rawTitle))
} catch {
// Not valid JSON — treat as plain text
}
}
return rawTitle
}
if (typeof rawTitle === 'object') {
// Handle NBT-simplified format: { value: "...", type: "string" }
if ('value' in rawTitle && rawTitle.type === 'string') {
return formatWindowTitle(rawTitle.value)
}
// Handle translate key: { translate: "container.chestDouble" }
if (rawTitle.translate) {
const lang = (globalThis as any).loadedData?.language
return lang?.[rawTitle.translate] ?? rawTitle.translate
}
// Handle text key: { text: "Custom Name" }
if (typeof rawTitle.text === 'string') {
return rawTitle.text
}
// Handle extra/with arrays by joining
if (rawTitle.extra) {
return (rawTitle.extra as any[]).map(formatWindowTitle).join('')
}
// Fallback: stringify for non-empty objects
if (typeof rawTitle[''] === 'string') return rawTitle['']
}
return String(rawTitle)
}
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 | 🟡 Minor

Fix strict equality comparison per ESLint.

Line 143 uses == which ESLint flags. While == null is a common pattern to catch both null and undefined, changing to explicit checks satisfies the linter and makes intent clearer.

🔧 Proposed fix
 export function formatWindowTitle (rawTitle: any): string {
-  if (rawTitle == null) return ''
+  if (rawTitle === null || rawTitle === undefined) return ''
   if (typeof rawTitle === 'string') {
📝 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
export function formatWindowTitle (rawTitle: any): string {
if (rawTitle == null) return ''
if (typeof rawTitle === 'string') {
// Try to parse JSON text component
if (rawTitle.startsWith('{') || rawTitle.startsWith('"')) {
try {
return formatWindowTitle(JSON.parse(rawTitle))
} catch {
// Not valid JSON — treat as plain text
}
}
return rawTitle
}
if (typeof rawTitle === 'object') {
// Handle NBT-simplified format: { value: "...", type: "string" }
if ('value' in rawTitle && rawTitle.type === 'string') {
return formatWindowTitle(rawTitle.value)
}
// Handle translate key: { translate: "container.chestDouble" }
if (rawTitle.translate) {
const lang = (globalThis as any).loadedData?.language
return lang?.[rawTitle.translate] ?? rawTitle.translate
}
// Handle text key: { text: "Custom Name" }
if (typeof rawTitle.text === 'string') {
return rawTitle.text
}
// Handle extra/with arrays by joining
if (rawTitle.extra) {
return (rawTitle.extra as any[]).map(formatWindowTitle).join('')
}
// Fallback: stringify for non-empty objects
if (typeof rawTitle[''] === 'string') return rawTitle['']
}
return String(rawTitle)
}
export function formatWindowTitle (rawTitle: any): string {
if (rawTitle === null || rawTitle === undefined) return ''
if (typeof rawTitle === 'string') {
// Try to parse JSON text component
if (rawTitle.startsWith('{') || rawTitle.startsWith('"')) {
try {
return formatWindowTitle(JSON.parse(rawTitle))
} catch {
// Not valid JSON — treat as plain text
}
}
return rawTitle
}
if (typeof rawTitle === 'object') {
// Handle NBT-simplified format: { value: "...", type: "string" }
if ('value' in rawTitle && rawTitle.type === 'string') {
return formatWindowTitle(rawTitle.value)
}
// Handle translate key: { translate: "container.chestDouble" }
if (rawTitle.translate) {
const lang = (globalThis as any).loadedData?.language
return lang?.[rawTitle.translate] ?? rawTitle.translate
}
// Handle text key: { text: "Custom Name" }
if (typeof rawTitle.text === 'string') {
return rawTitle.text
}
// Handle extra/with arrays by joining
if (rawTitle.extra) {
return (rawTitle.extra as any[]).map(formatWindowTitle).join('')
}
// Fallback: stringify for non-empty objects
if (typeof rawTitle[''] === 'string') return rawTitle['']
}
return String(rawTitle)
}
🧰 Tools
🪛 ESLint

[error] 143-143: Use '===' to compare with null.

(no-eq-null)


[error] 143-143: Expected '===' and instead saw '=='.

(eqeqeq)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/react/inventory/sharedConnectorSetup.ts` around lines 142 - 177, The
null/undefined check in formatWindowTitle uses loose equality (`rawTitle ==
null`); change it to an explicit check for both null and undefined (i.e., test
rawTitle === null || rawTitle === undefined) to satisfy ESLint and make intent
clear; update the guard at the top of formatWindowTitle accordingly and leave
all other logic untouched.

@zardoy
Copy link
Copy Markdown
Owner Author

zardoy commented Apr 8, 2026

/deploy

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 8, 2026

Deployed to Vercel Preview: https://prismarine-e99qoinmo-zaro.vercel.app
Playground
Storybook

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant