JSPM

  • Created
  • Published
  • Downloads 135
  • Score
    100M100P100Q133782F
  • License MIT

Package Exports

    This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (minecraft-inventory) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

    Readme

    minecraft-inventory

    A flexible, scalable React library for rendering Minecraft inventory GUIs. Designed for integration into real Minecraft clients (e.g., mineflayer bots, web clients) without using CSS transform: scale — all sizing is driven by CSS custom properties.

    Features

    • All standard inventory types (chest, furnace, crafting table, villager trades, enchanting table, brewing stand, anvil, smithing table, horse, beacon, and more)
    • CSS variable–based scaling — no layout jank, no transform: scale
    • <img>-rendered item textures with automatic items/blocks/ fallback (via PrismarineJS asset mirror by default)
    • Tooltips that follow the cursor, matching the original Minecraft style
    • Full keyboard support: number keys (1-9) to swap hotbar, Q to drop, double-click to collect, scroll wheel to pick/place
    • Mobile support: tap-to-focus inventory interactions plus long-press slot actions (pick half / custom amount / drop one / drop all)
    • Optional JEI (item browser) panel on the left or right
    • Bot connector layer — plugs into a mineflayer bot or any custom server connection
    • Demo connector with action logging for local development
    • Extendable registry — add new inventory types in one place

    Quick Start (React)

    import React from 'react'
    import { createRoot } from 'react-dom/client'
    import { InventoryGUI, createDemoConnector } from 'minecraft-inventory'
    
    const connector = createDemoConnector({
      windowType: 'chest',
      windowTitle: 'My Chest',
      slots: [
        { index: 0, item: { type: 265, name: 'iron_ingot', count: 32, displayName: 'Iron Ingot' } },
        // ...more slots
      ],
    })
    
    function App() {
      return (
        <InventoryGUI
          type="chest"
          connector={connector}
          scale={2}
          showJEI
          jeiItems={[
            { type: 1, name: 'stone', displayName: 'Stone', count: 1 },
            { type: 4, name: 'cobblestone', displayName: 'Cobblestone', count: 1 },
          ]}
        />
      )
    }
    
    // Mount into the DOM
    createRoot(document.getElementById('root')!).render(<App />)

    Quick Start (No React — programmatic)

    If you don't use React, use the mountInventory helper:

    <div id="inventory"></div>
    <script type="module">
      import { mountInventory, createDemoConnector } from 'minecraft-inventory'
    
      const connector = createDemoConnector({
        windowType: 'chest',
        windowTitle: 'My Chest',
        slots: [
          { index: 0, item: { type: 265, name: 'iron_ingot', count: 32, displayName: 'Iron Ingot' } },
        ],
      })
    
      const inv = mountInventory(document.getElementById('inventory'), {
        type: 'chest',
        connector,
        scale: 2,
      })
    
      // Update props later
      inv.update({ scale: 3 })
    
      // Destroy when done
      inv.destroy()
    </script>

    Installation

    npm install minecraft-inventory
    # or
    pnpm add minecraft-inventory

    Core API

    <InventoryGUI> — All-in-one component

    <InventoryGUI
      type="chest"                    // Inventory type name (see registry)
      title="My Chest"                // Override window title
      slots={[...]}                   // Optional: provide slots directly
      properties={{ litTime: 100 }}   // Optional: progress bar data
      connector={connector}           // Optional: bot connector
      scale={2}                       // Scale multiplier (default: 2)
      showJEI={false}                 // Show item browser panel
      jeiItems={[...]}                // Items to show in JEI
      jeiPosition="right"             // 'left' | 'right'
      textureBaseUrl="/assets/mc"     // Override texture base URL
      enableKeyboardShortcuts={true}
      onClose={() => {}}
    />

    <InventoryOverlay> — Full-screen overlay with backdrop

    Renders the inventory centered on screen with a semi-transparent backdrop. Clicking outside drops the held item or closes the window.

    import { InventoryOverlay, InventoryProvider, ScaleProvider, TextureProvider } from 'minecraft-inventory'
    
    <TextureProvider>
      <ScaleProvider scale={2}>
        <InventoryProvider connector={connector}>
          <InventoryOverlay
            type="chest"
            showBackdrop            // default: true (50% black)
            backdropColor="rgba(0,0,0,0.5)"
            showJEI
            jeiItems={items}
            jeiPosition="right"
            onClose={() => console.log('closed')}
          >
            {/* Extra children (e.g. notes panel) are rendered inside the centered anchor */}
          </InventoryOverlay>
        </InventoryProvider>
      </ScaleProvider>
    </TextureProvider>

    Manual composition

    For full control, use the provider components and compose manually:

    import {
      TextureProvider, ScaleProvider, InventoryProvider,
      InventoryWindow, Hotbar, HUD, CursorItem, JEI,
    } from 'minecraft-inventory'
    
    function MyInventory({ connector }) {
      return (
        <TextureProvider baseUrl="https://example.com/assets">
          <ScaleProvider scale={2}>
            <InventoryProvider connector={connector}>
              <div style={{ display: 'flex', gap: 8 }}>
                <InventoryWindow type="furnace" />
                <JEI items={allItems} position="right" />
              </div>
              <Hotbar />
              <HUD />
              <CursorItem />
            </InventoryProvider>
          </ScaleProvider>
        </TextureProvider>
      )
    }

    Scaling

    Scale is driven entirely by CSS custom properties — no transform: scale. Changing the scale prop recalculates all size tokens:

    Variable Default (scale=2)
    --mc-scale 2
    --mc-slot-size 36px (18 × scale)
    --mc-font-size 14px (7 × scale)
    --mc-pixel 2px
    // Changing scale re-renders the whole GUI at new dimensions
    <InventoryGUI type="chest" scale={3} />

    Textures

    By default, item textures are fetched from the PrismarineJS Minecraft assets mirror on GitHub:

    https://raw.githubusercontent.com/PrismarineJS/minecraft-assets/master/data/1.21.4/item/{name}.png

    Provide name on each ItemStack to use the correct asset:

    { type: 276, name: 'diamond_sword', count: 1, displayName: 'Diamond Sword' }

    To use local textures (e.g., from a resource pack server):

    <InventoryGUI
      type="chest"
      textureBaseUrl="/assets/minecraft"
      // → /assets/minecraft/item/diamond_sword.png
    />

    For full control, pass a custom texture config overriding any or all URL resolvers:

    import { TextureProvider } from 'minecraft-inventory'
    
    // Option A — simple base URL (all textures served from your CDN/server)
    <TextureProvider baseUrl="https://yourserver.com/mc-assets/1.21.4">
      {/* item textures: /mc-assets/1.21.4/items/{name}.png           */}
      {/* block textures: /mc-assets/1.21.4/blocks/{name}.png         */}
      {/* GUI textures: /mc-assets/1.21.4/textures/{path}.png         */}
      <InventoryWindow type="chest" />
    </TextureProvider>
    
    // Option B — replace individual resolvers (config is merged with defaults)
    <TextureProvider config={{
      getItemTextureUrl: ({ type, name }) =>
        `https://cdn.example.com/items/${name ?? type}.png`,
    
      getBlockTextureUrl: ({ type, name }) =>
        `https://cdn.example.com/blocks/${name ?? type}.png`,
    
      // version param lets you vary GUI textures per container (1.16.4, 1.21.4, etc.)
      getGuiTextureUrl: (path, version = '1.16.4') =>
        `https://cdn.example.com/gui/${version}/${path}.png`,
    }}>
      <InventoryWindow type="furnace" />
    </TextureProvider>
    
    // Option C — completely static local assets (no CDN)
    <TextureProvider config={{
      baseUrl: '/assets',
      getItemTextureUrl: ({ name, type }) => `/assets/items/${name ?? type}.png`,
      getBlockTextureUrl: ({ name, type }) => `/assets/blocks/${name ?? type}.png`,
      getGuiTextureUrl: (path) => `/assets/gui/${path}.png`,
    }}>
      <InventoryWindow type="chest" />
    </TextureProvider>

    Bundled GUI textures (mc-assets) — Use the optional mc-assets package to bundle container backgrounds locally and avoid remote requests. Generate the texture map from your inventory registry, then pass the config to TextureProvider:

    pnpm add mc-assets   # optional peer dependency
    import { TextureProvider, InventoryOverlay } from 'minecraft-inventory'
    import { localBundledTexturesConfig } from './bundledTexturesConfig'
    
    // GUI container backgrounds (chest, furnace, etc.) load from bundled mc-assets;
    // item/block textures still use the default remote URLs unless you override them.
    <TextureProvider config={localBundledTexturesConfig}>
      <InventoryOverlay type="chest" ... />
    </TextureProvider>

    The generated file (src/generated/localTextures.ts) exports bundledTextureMap, allTexturePaths, and allContainerPaths. Use createBundledTexturesConfig({ remoteFallback: true, bundledTextureMap? }) to get a config with getGuiTextureUrl, setOverride(path, image), clearOverrides(), setRemoteFallback(enabled), and resetRenderedSlots(). Pass short paths to setOverride (e.g. gui/sprites/container/anvil/text_field_disabled.png) — the version prefix exists only for the texture import generator. Call resetRenderedSlots() after multiple setOverride calls to invalidate cached textures so slots re-request them. localBundledTexturesConfig is the default instance. Re-run pnpm gen:textures after changing inventory types.


    Connector

    The connector is the bridge between the GUI and a Minecraft server or bot.

    Mineflayer connector

    Use createMineflayerConnector(bot) to plug the GUI into a mineflayer bot. The connector turns slot clicks, drags, and drops into bot.clickWindow() (and plugin APIs for villager trades, enchantment table, anvil, beacon), and subscribes to mineflayer inventory events so the UI stays in sync.

    Example — overlay when a container opens:

    import React, { useState, useEffect } from 'react'
    import { createRoot } from 'react-dom/client'
    import mineflayer from 'mineflayer'
    import {
      InventoryProvider,
      ScaleProvider,
      TextureProvider,
      InventoryOverlay,
      createMineflayerConnector,
    } from 'minecraft-inventory'
    
    const bot = mineflayer.createBot({
      host: 'localhost',
      port: 25565,
      username: 'InventoryViewer',
    })
    
    function App() {
      const [connector, setConnector] = useState(null)
      const [windowType, setWindowType] = useState(null)
      const [open, setOpen] = useState(false)
    
      useEffect(() => {
        const onOpen = () => {
          setConnector(createMineflayerConnector(bot))
          setWindowType(bot.currentWindow?.type ?? 'generic_9x1')
          setOpen(true)
        }
        const onClose = () => {
          setOpen(false)
          setConnector(null)
        }
    
        bot.on('windowOpen', onOpen)
        bot.on('windowClose', onClose)
        return () => {
          bot.removeListener('windowOpen', onOpen)
          bot.removeListener('windowClose', onClose)
        }
      }, [])
    
      if (!open || !connector || !windowType) return null
    
      return (
        <TextureProvider>
          <ScaleProvider scale={2}>
            <InventoryProvider connector={connector}>
              <InventoryOverlay
                type={windowType}
                onClose={() => bot.closeWindow(bot.currentWindow)}
                showJEI
                jeiItems={[]}
              />
            </InventoryProvider>
          </ScaleProvider>
        </TextureProvider>
      )
    }
    
    createRoot(document.getElementById('root')).render(<App />)

    Hotbar “open inventory” button: If you render a hotbar with the container option (e.g. mobile open-inventory button), the connector handles the open-inventory action: it calls openPlayerInventory(), which opens the player inventory GUI, or the ridden entity’s inventory (e.g. llama) when mounted. No extra wiring needed once the connector is passed to InventoryProvider.

    Demo connector (for local testing)

    import { createDemoConnector } from 'minecraft-inventory'
    
    const connector = createDemoConnector({
      windowType: 'crafting_table',
      windowTitle: 'Crafting',
      slots: [...],
      onAction: (entry) => {
        console.log(entry.description, entry.action)
      },
    })
    
    // Update slots programmatically:
    connector.updateSlots(newSlots)
    connector.setHeldItem({ type: 264, name: 'diamond', count: 1 })
    connector.openWindow('furnace', 'Furnace', furnaceSlots)
    connector.closeWindowExternal()

    Custom connector

    Implement the InventoryConnector interface for any other backend:

    import type { InventoryConnector } from 'minecraft-inventory'
    
    const myConnector: InventoryConnector = {
      getWindowState: () => ({ windowId: 1, type: 'chest', slots: [...], heldItem: null }),
      getPlayerState: () => null,
      sendAction: async (action) => {
        // send action to server
        console.log('action', action)
      },
      closeWindow: () => {},
      subscribe: (listener) => {
        // call listener({ type: 'windowUpdate', state }) on changes
        return () => {} // cleanup
      },
    }

    Keyboard Shortcuts (Desktop)

    Key Action
    Left click Pick up all / place all
    Right click Pick up half / place one
    Shift + Left click Transfer to/from container
    Double click Collect all of same item type
    1–9 (while hovering) Swap slot with hotbar slot N
    Q (while hovering) Drop one item
    Ctrl+Q Drop entire stack
    Scroll up Pick up one more (right-click equivalent)
    Scroll down Put one back
    Esc Close window (onClose callback)

    Mobile Support

    On touch devices, tapping a slot uses the mobile focus/swap flow. Long-pressing a populated slot with no held item opens a context menu. The menu appears to the side in landscape or below in portrait.

    Context menu options:

    • Pick Half — picks up half and highlights the selected slot
    • Pick Amount…window.prompt to enter a custom quantity, remembering the last value entered
    • Drop One — drops a single item from the slot (same as Q)
    • Drop All — drops the full stack from the slot (same as Ctrl+Q)
    • Cancel

    When you have a held item, tapping a slot places/transfers it (same as left-click on desktop).


    JEI — Item Browser

    import { JEI } from 'minecraft-inventory'
    
    const items = [
      { type: 264, name: 'diamond', displayName: 'Diamond' },
      { type: 265, name: 'iron_ingot', displayName: 'Iron Ingot' },
      // ...
    ]
    
    <JEI
      items={items}
      position="right"           // 'left' | 'right'
      onItemClick={(item) => {}} // left-click handler
      onItemMiddleClick={(item) => {}} // middle-click handler
    />

    Use the search bar to filter by display name or item name. Scroll wheel or arrow buttons to paginate.


    Adding New Inventory Types

    All inventory types live in a single registry file:

    src/registry/inventories.ts

    Add a new entry to the inventoryDefinitions object:

    export const inventoryDefinitions: Record<string, InventoryTypeDefinition> = {
      // ... existing types ...
    
      my_custom_chest: {
        name: 'my_custom_chest',
        title: 'Custom Chest',
        backgroundTexture: 'gui/container/my_custom_chest',  // path under textures/
        backgroundWidth: 176,
        backgroundHeight: 166,
        includesPlayerInventory: true,
        includesHotbar: true,
        slots: [
          // Custom container slots (indices 0-N)
          ...gridSlots(0, 9, 3, 8, 18, 'container'),
          // Player inventory (adjust indices to match server protocol)
          ...playerInv(84, 27, 54),
        ],
      },
    }

    Slot index conventions:

    • Container slots always start at 0
    • Player inventory and hotbar indices vary by window type (match the server protocol)
    • Use playerInv(yPos, invStartIndex, hotbarStartIndex) for the standard 3×9 + hotbar layout

    Progress bars (for furnaces, brewing stands, etc.):

    progressBars: [
      {
        id: 'cook_arrow',
        x: 79, y: 34, width: 24, height: 16,
        direction: 'right',            // 'right' | 'up' | 'down' | 'left'
        textureX: 176, textureY: 14,   // source coords in background texture
        getValue: (props) => props.cookingProgress ?? 0,
        getMax: (props) => props.totalCookTime || 200,
      },
    ],

    Properties (data slots from server):

    properties: {
      cookingProgress: { dataSlot: 2, description: 'Cook progress (0-200)' },
      totalCookTime: { dataSlot: 3, description: 'Total cook time' },
    },

    Registering at runtime (optional, for plugins/mods):

    import { registerInventoryType } from 'minecraft-inventory'
    
    registerInventoryType({
      name: 'my_mod_inventory',
      title: 'Mod Inventory',
      backgroundTexture: 'gui/container/my_mod',
      backgroundWidth: 176,
      backgroundHeight: 166,
      slots: [...],
    })

    Then use it anywhere:

    <InventoryGUI type="my_mod_inventory" connector={connector} />

    ItemStack Type

    interface ItemStack {
      type: number        // Numeric item ID
      name?: string       // Snake_case name, e.g. 'diamond_sword' (used for texture URL)
      count: number
      metadata?: number
      nbt?: Record<string, unknown>
      displayName?: string
      enchantments?: Array<{ name: string; level: number }>
      lore?: string[]
      durability?: number        // Current durability value
      maxDurability?: number     // Max durability (renders bar when < max)
      textureKey?: string        // Override texture path for getItemTextureUrl
      texture?: string | HTMLImageElement  // Direct texture (bypasses URL lookup)
      blockTexture?: BlockTextureRender   // Isometric block icon from face slices
      debugKey?: string
    }

    Texture overrides (mineflayer itemMapper):

    • texture: string — URL or data URL, fetched and cached.
    • texture: HTMLImageElement — Preloaded image, used directly.
    • blockTexture: { source, top, left, right } — Composite an isometric block icon from three face slices. Each face has slice: [x, y, w, h] in source texture pixels. Uses a pool of aux canvases (not recreated per slot).

    Project Structure

    src/
      index.tsx                  # Library entry point / exports
      types.ts                   # Core TypeScript types
      registry/
        index.ts                 # registerInventoryType / getInventoryType
        inventories.ts           # All built-in inventory type definitions ← ADD NEW TYPES HERE
      components/
        InventoryWindow/         # Main window renderer
        Slot/                    # Individual slot (click/drag/keyboard/mobile)
        ItemCanvas/              # Canvas-based item texture rendering
        Tooltip/                 # Cursor-following item tooltip
        JEI/                     # Item browser panel
        Hotbar/                  # Hotbar with active slot indicator
        HUD/                     # XP bar
        CursorItem/              # Floating item following mouse cursor
      context/
        InventoryContext.tsx      # Shared state (held item, hover, drag)
        ScaleContext.tsx          # CSS variable scale provider
        TextureContext.tsx        # Texture URL resolver
      connector/
        types.ts                 # InventoryConnector interface
        mineflayer.ts            # Mineflayer bot adapter
        demo.ts                  # Demo connector with action log
      hooks/
        useMobile.ts
        useKeyboardShortcuts.ts
      styles/
        tokens.css               # CSS custom property definitions
    playground/
      src/
        App.tsx                  # Interactive playground / demo
        mockItems.ts             # Sample items for testing

    Contributing

    Running the playground

    pnpm install
    pnpm dev
    # Opens at http://localhost:3200

    Adding a new inventory type

    1. Open src/registry/inventories.ts
    2. Add a new key to inventoryDefinitions (see template above)
    3. Test it in the playground by adding it to INVENTORY_TYPES in playground/src/App.tsx
    4. Submit a PR with the new type and sample mock slots in playground/src/mockItems.ts

    Slot index reference

    Each Minecraft window type has a fixed slot layout defined by the server protocol. The indices must match prismarine-windows / mineflayer slot numbering. Cross-reference with:

    • prismarine-windows for JS slot maps
    • Minecraft source: net/minecraft/world/inventory/ for the canonical layout

    Memory leak note (Tooltip / MessageFormattedString)

    If opening tooltips increases memory without returning to baseline, see docs/MEMORY_LEAK_ANALYSIS.md. The likely cause is filter:blur(2px) on obfuscated (§k) text creating retained compositor layers.

    Connector protocol

    When implementing a custom connector, actions have these shapes:

    Action type Fields
    click slotIndex, button ('left'/'right'/'middle'), mode ('normal'/'shift'/'double'/'number'/'drop'), numberKey?
    drop slotIndex, all (boolean)
    drag slots (number[]), button
    trade tradeIndex
    rename text
    enchant enchantIndex (0/1/2)
    beacon primaryEffect, secondaryEffect
    hotbar-swap slotIndex, hotbarSlot (0-8)
    close

    License

    MIT