minecraft-inventory 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,25 +1,25 @@
1
1
  .tooltip {
2
2
  background: #100010;
3
3
  border: 1px solid #100010;
4
- border-radius: 5px;
4
+ border-radius: 3px;
5
5
  display: flex;
6
6
  flex-direction: column;
7
7
  font-family: 'Minecraftia', 'Minecraft', monospace;
8
8
  image-rendering: pixelated;
9
9
  line-height: 1.2;
10
- /* MC tooltip: 2-layer purple gradient inner glow */
10
+ /* Gray inner border instead of classic purple */
11
11
  box-shadow:
12
- inset 0 1px 0 0 #5000a0,
13
- inset 1px 0 0 0 #5000a0,
14
- inset -1px 0 0 0 #5000a0,
15
- inset 0 -1px 0 0 #28007f,
12
+ inset 0 1px 0 0 #555555,
13
+ inset 1px 0 0 0 #555555,
14
+ inset -1px 0 0 0 #555555,
15
+ inset 0 -1px 0 0 #444444,
16
16
  0 0 0 1px #100010;
17
17
  }
18
18
 
19
19
  .name {
20
20
  font-weight: bold;
21
21
  white-space: nowrap;
22
- margin-bottom: 2px;
22
+ margin-bottom: 1px;
23
23
  }
24
24
 
25
25
  .section {
@@ -37,20 +37,20 @@
37
37
  flex-direction: column;
38
38
  color: #6b29a4;
39
39
  font-style: italic;
40
- border-top: 1px solid #2d0069;
41
- margin-top: 3px;
42
- padding-top: 3px;
40
+ border-top: 1px solid #333333;
41
+ margin-top: 2px;
42
+ padding-top: 2px;
43
43
  }
44
44
 
45
45
  .durability {
46
46
  color: #dddddd;
47
- border-top: 1px solid #2d0069;
48
- margin-top: 3px;
49
- padding-top: 3px;
47
+ border-top: 1px solid #333333;
48
+ margin-top: 2px;
49
+ padding-top: 2px;
50
50
  }
51
51
 
52
52
  .typeId {
53
53
  color: #444455;
54
54
  font-size: 0.8em;
55
- margin-top: 2px;
55
+ margin-top: 1px;
56
56
  }
@@ -1,14 +1,13 @@
1
- import React, { useRef, useState, useEffect, useLayoutEffect } from 'react'
1
+ import React, { useRef, useEffect } from 'react'
2
2
  import { createPortal } from 'react-dom'
3
3
  import type { ItemStack } from '../../types'
4
4
  import { useScale } from '../../context/ScaleContext'
5
5
  import { MessageFormattedString } from '../Text/MessageFormattedString'
6
+ import { globalMouse } from '../../utils/globalMouse'
6
7
  import styles from './Tooltip.module.css'
7
8
 
8
9
  interface TooltipProps {
9
10
  item: ItemStack
10
- mouseX: number
11
- mouseY: number
12
11
  visible: boolean
13
12
  }
14
13
 
@@ -22,16 +21,15 @@ function formatEnchantment(e: { name: string; level: number }): string {
22
21
  return e.level <= 10 ? `${e.name} ${roman[e.level]}` : `${e.name} ${e.level}`
23
22
  }
24
23
 
25
- export function Tooltip({ item, mouseX, mouseY, visible }: TooltipProps) {
24
+ export function Tooltip({ item, visible }: TooltipProps) {
26
25
  const { scale } = useScale()
27
26
  const ref = useRef<HTMLDivElement>(null)
28
- const [pos, setPos] = useState({ x: -9999, y: -9999 })
29
- const [ready, setReady] = useState(false)
30
27
 
31
- // Recompute position every time mouse moves or item changes
32
- useLayoutEffect(() => {
33
- if (!visible || !ref.current) return
28
+ // Position the tooltip directly via DOM, bypassing React state updates.
29
+ // Called on every mousemove and whenever visible/item/scale changes.
30
+ const applyPosition = () => {
34
31
  const el = ref.current
32
+ if (!el) return
35
33
  const tw = el.offsetWidth
36
34
  const th = el.offsetHeight
37
35
  const vw = window.innerWidth
@@ -39,21 +37,29 @@ export function Tooltip({ item, mouseX, mouseY, visible }: TooltipProps) {
39
37
  const gapX = 12
40
38
  const gapY = 2
41
39
 
42
- let x = mouseX + gapX
43
- let y = mouseY - th - gapY // above cursor by default
40
+ let x = globalMouse.x + gapX
41
+ let y = globalMouse.y - th - gapY // above cursor by default
44
42
 
45
- if (x + tw > vw - 4) x = mouseX - tw - gapX
46
- if (y < 4) y = mouseY + gapY // flip below if no space above
43
+ if (x + tw > vw - 4) x = globalMouse.x - tw - gapX
44
+ if (y < 4) y = globalMouse.y + gapY // flip below if no space above
47
45
  if (y + th > vh - 4) y = vh - th - 4
48
46
 
49
- setPos({ x, y })
50
- setReady(true)
51
- }, [mouseX, mouseY, visible, item, scale])
47
+ el.style.left = `${x}px`
48
+ el.style.top = `${y}px`
49
+ el.style.visibility = 'visible'
50
+ }
52
51
 
53
- // Reset ready when tooltip becomes hidden
54
52
  useEffect(() => {
55
- if (!visible) setReady(false)
56
- }, [visible])
53
+ if (!visible) return
54
+
55
+ // Initial position immediately after mounting / becoming visible
56
+ applyPosition()
57
+
58
+ const onMove = () => applyPosition()
59
+ window.addEventListener('mousemove', onMove, { passive: true })
60
+ return () => window.removeEventListener('mousemove', onMove)
61
+ // eslint-disable-next-line react-hooks/exhaustive-deps
62
+ }, [visible, item, scale])
57
63
 
58
64
  if (!visible) return null
59
65
 
@@ -64,8 +70,8 @@ export function Tooltip({ item, mouseX, mouseY, visible }: TooltipProps) {
64
70
  item.durability < item.maxDurability
65
71
 
66
72
  const fs = Math.round(8 * scale)
67
- const pad = Math.round(4 * scale)
68
- const gap2 = Math.round(1 * scale)
73
+ const pad = Math.round(3 * scale)
74
+ const gap2 = Math.round(0.5 * scale)
69
75
 
70
76
  const tooltip = (
71
77
  <div
@@ -73,15 +79,16 @@ export function Tooltip({ item, mouseX, mouseY, visible }: TooltipProps) {
73
79
  className={['mc-inv-tooltip', styles.tooltip].join(' ')}
74
80
  style={{
75
81
  position: 'fixed',
76
- left: pos.x,
77
- top: pos.y,
82
+ left: -9999,
83
+ top: -9999,
78
84
  zIndex: 10000,
79
85
  fontSize: fs,
80
86
  padding: pad,
81
87
  gap: gap2,
82
88
  minWidth: Math.round(80 * scale),
83
89
  maxWidth: Math.round(220 * scale),
84
- visibility: ready ? 'visible' : 'hidden',
90
+ // Start invisible; applyPosition sets visibility after measuring dimensions
91
+ visibility: 'hidden',
85
92
  pointerEvents: 'none',
86
93
  }}
87
94
  >
@@ -181,6 +181,10 @@ export function createDemoConnector(options: DemoConnectorOptions): InventoryCon
181
181
  emit({ type: 'windowClose' })
182
182
  },
183
183
 
184
+ openPlayerInventory: () => {
185
+ // Demo: nothing to ride, just a no-op (player inventory lives in playerState)
186
+ },
187
+
184
188
  subscribe: (listener: ConnectorListener) => {
185
189
  listeners.add(listener)
186
190
  return () => listeners.delete(listener)
@@ -29,8 +29,47 @@ function modeFromAction(action: InventoryAction): [number, number] {
29
29
  return [action.button === 'left' ? 0 : 1, 0]
30
30
  }
31
31
 
32
+ /** Extended bot API from mineflayer plugins (villager, enchantment_table, anvil, beacon). */
33
+ interface MineflayerBotExtended extends MineflayerBot {
34
+ trade?(villager: unknown, tradeIndex: number, count?: number): Promise<void>
35
+ supportFeature?(name: string): boolean
36
+ _client?: {
37
+ write(packet: string, data: unknown): void
38
+ writeChannel?(channel: string, data: unknown): void
39
+ registerChannel?(channel: string, schema: unknown): void
40
+ }
41
+ }
42
+
43
+
44
+ function isVillagerWindow(win: unknown): win is { trade: (i: number, c?: number) => Promise<void> } {
45
+ return (
46
+ win != null &&
47
+ typeof (win as Record<string, unknown>).trade === 'function' &&
48
+ Array.isArray((win as Record<string, unknown>).trades)
49
+ )
50
+ }
51
+
52
+ function isEnchantmentTableWindow(win: unknown): win is { enchant: (choice: number) => Promise<void> } {
53
+ return win != null && typeof (win as Record<string, unknown>).enchant === 'function'
54
+ }
55
+
56
+ function isAnvilWindow(
57
+ win: unknown
58
+ ): win is {
59
+ rename: (item: unknown, name: string) => Promise<void>
60
+ findInventoryItem?: (id: number) => unknown
61
+ slots?: Array<{ type?: number; metadata?: number; count?: number; nbt?: unknown } | null>
62
+ } {
63
+ return win != null && typeof (win as Record<string, unknown>).rename === 'function'
64
+ }
65
+
66
+ function isBeaconWindow(win: unknown): win is { setBeaconEffects?: (primary: number, secondary: number) => Promise<void> } {
67
+ return win != null && (typeof (win as Record<string, unknown>).setBeaconEffects === 'function' || /beacon/i.test(String((win as Record<string, unknown>).type)))
68
+ }
69
+
32
70
  export function createMineflayerConnector(bot: MineflayerBot): InventoryConnector {
33
71
  const listeners = new Set<ConnectorListener>()
72
+ const ext = bot as MineflayerBotExtended
34
73
 
35
74
  function emit(event: ConnectorEvent) {
36
75
  listeners.forEach((l) => l(event))
@@ -77,18 +116,123 @@ export function createMineflayerConnector(bot: MineflayerBot): InventoryConnecto
77
116
  bot.on('windowClose', onWindowClose)
78
117
  bot.on('setSlot', onSetSlot)
79
118
 
119
+ async function openPlayerInventory() {
120
+ const vehicle = bot.vehicle
121
+ let inventoryType = 'player'
122
+
123
+ if (vehicle) {
124
+ const entityName = String(vehicle.name ?? vehicle.entityType ?? '').toLowerCase()
125
+ // Check if riding a llama (includes trader_llama)
126
+ if (/llama/i.test(entityName)) {
127
+ inventoryType = 'llama'
128
+ }
129
+ }
130
+
131
+ // Build a window state for the player/llama inventory
132
+ const slots: SlotState[] = []
133
+
134
+ if (inventoryType === 'llama') {
135
+ // Llama inventory structure (per registry):
136
+ // Slot 0: Carpet (saddle) - empty since we don't have entity data
137
+ slots.push({ index: 0, item: null })
138
+ // Slot 1: skipped (not used)
139
+ // Slots 2-16: Llama chest (5×3 grid = 15 slots) - empty since we don't have entity data
140
+ for (let i = 2; i <= 16; i++) {
141
+ slots.push({ index: i, item: null })
142
+ }
143
+ // Slots 17-43: Player inventory (bot.inventory.slots indices 9-35 map to window slots 17-43)
144
+ for (let i = 9; i <= 35; i++) {
145
+ slots.push({ index: i + 8, item: botSlotToItemStack(bot.inventory.slots[i]) })
146
+ }
147
+ // Slots 44-52: Hotbar (bot.inventory.slots indices 36-44 map to window slots 44-52)
148
+ for (let i = 36; i <= 44; i++) {
149
+ slots.push({ index: i + 8, item: botSlotToItemStack(bot.inventory.slots[i]) })
150
+ }
151
+ // Slot 53: Offhand (bot.inventory slot 45 maps to window slot 53)
152
+ slots.push({ index: 53, item: botSlotToItemStack(bot.inventory.slots[45]) })
153
+ } else {
154
+ // Player inventory window structure (per registry):
155
+ // Slots 0-8: Crafting result + grid + armor (not in bot.inventory.slots, leave empty)
156
+ for (let i = 0; i < 9; i++) {
157
+ slots.push({ index: i, item: null })
158
+ }
159
+ // Slots 9-35: Player inventory (bot.inventory.slots indices 9-35)
160
+ for (let i = 9; i <= 35; i++) {
161
+ slots.push({ index: i, item: botSlotToItemStack(bot.inventory.slots[i]) })
162
+ }
163
+ // Slots 36-44: Hotbar (bot.inventory.slots indices 36-44)
164
+ for (let i = 36; i <= 44; i++) {
165
+ slots.push({ index: i, item: botSlotToItemStack(bot.inventory.slots[i]) })
166
+ }
167
+ // Slot 45: Offhand (bot.inventory slot 45)
168
+ slots.push({ index: 45, item: botSlotToItemStack(bot.inventory.slots[45]) })
169
+ }
170
+
171
+ const windowState: InventoryWindowState = {
172
+ windowId: -1, // Use -1 to indicate a synthetic/UI-only window
173
+ type: inventoryType,
174
+ title: inventoryType === 'llama' ? 'Llama' : undefined,
175
+ slots,
176
+ heldItem: botSlotToItemStack(bot.heldItem),
177
+ }
178
+
179
+ emit({ type: 'windowOpen', state: windowState })
180
+ }
181
+
80
182
  return {
81
183
  getWindowState: buildWindowState,
82
184
  getPlayerState: buildPlayerState,
185
+ openPlayerInventory,
83
186
 
84
187
  sendAction: async (action: InventoryAction) => {
188
+ // Hotbar "open inventory" button — delegates to openPlayerInventory()
189
+ if (action.type === 'open-inventory') {
190
+ await openPlayerInventory()
191
+ return
192
+ }
193
+
194
+ const win = bot.currentWindow
195
+
196
+ if (action.type === 'trade' && win) {
197
+ if (ext.trade && isVillagerWindow(win)) {
198
+ await ext.trade(win, action.tradeIndex, 1)
199
+ } else if (isVillagerWindow(win)) {
200
+ await win.trade(action.tradeIndex, 1)
201
+ }
202
+ return
203
+ }
204
+
205
+ if (action.type === 'enchant' && win && isEnchantmentTableWindow(win)) {
206
+ await win.enchant(action.enchantIndex)
207
+ return
208
+ }
209
+
210
+ if (action.type === 'rename' && win && isAnvilWindow(win)) {
211
+ const w = win as { slots?: unknown[]; findInventoryItem?: (id: number) => unknown; rename: (item: unknown, name: string) => Promise<void> }
212
+ const inputSlot = w.slots?.[0] as { type?: number; metadata?: number; count?: number; nbt?: unknown } | null
213
+ const item = inputSlot?.type ? (w.findInventoryItem?.(inputSlot.type) ?? inputSlot) : null
214
+ if (item) await w.rename(item, action.text)
215
+ return
216
+ }
217
+
218
+ if (action.type === 'beacon' && win && isBeaconWindow(win)) {
219
+ if (typeof win.setBeaconEffects === 'function') {
220
+ await win.setBeaconEffects(action.primaryEffect, action.secondaryEffect)
221
+ } else if (ext._client) {
222
+ ext._client.write('beacon_effect', {
223
+ primaryEffect: action.primaryEffect,
224
+ secondaryEffect: action.secondaryEffect,
225
+ })
226
+ }
227
+ return
228
+ }
229
+
85
230
  if (action.type === 'click') {
86
231
  const [mouseButton, mode] = modeFromAction(action)
87
232
  await bot.clickWindow(action.slotIndex, mouseButton, mode)
88
233
  } else if (action.type === 'drop') {
89
234
  await bot.clickWindow(action.slotIndex, action.all ? 1 : 0, 4)
90
235
  } else if (action.type === 'close') {
91
- const win = bot.currentWindow
92
236
  if (win) bot.closeWindow(win)
93
237
  } else if (action.type === 'hotbar-swap') {
94
238
  await bot.clickWindow(action.slotIndex, action.hotbarSlot, 2)
@@ -6,6 +6,12 @@ export interface InventoryConnector {
6
6
  sendAction(action: InventoryAction): void | Promise<void>
7
7
  closeWindow(): void | Promise<void>
8
8
  subscribe(listener: ConnectorListener): () => void
9
+ /**
10
+ * Open the player inventory, taking into account the entity the player is riding.
11
+ * For horse/llama/donkey/etc this activates the entity so the server sends a windowOpen.
12
+ * For unmounted players it emits a playerUpdate with the current inventory.
13
+ */
14
+ openPlayerInventory(): void | Promise<void>
9
15
  }
10
16
 
11
17
  export type ConnectorEvent =
@@ -17,25 +23,49 @@ export type ConnectorEvent =
17
23
 
18
24
  export type ConnectorListener = (event: ConnectorEvent) => void
19
25
 
26
+ export interface MineflayerBotWindow {
27
+ id: number
28
+ type: string
29
+ title?: string
30
+ slots: Array<{ type: number; count: number; metadata?: number; nbt?: unknown } | null>
31
+ }
32
+
33
+ /** Villager window from bot.openVillager() — has trade() */
34
+ export interface MineflayerVillagerWindow extends MineflayerBotWindow {
35
+ trade: (index: number, count?: number) => Promise<void>
36
+ trades: unknown[] | null
37
+ }
38
+
39
+ /** Enchantment table from bot.openEnchantmentTable() — has enchant() */
40
+ export interface MineflayerEnchantmentTableWindow extends MineflayerBotWindow {
41
+ enchant: (choice: number) => Promise<void>
42
+ }
43
+
44
+ /** Anvil from bot.openAnvil() — has rename() and combine() */
45
+ export interface MineflayerAnvilWindow extends MineflayerBotWindow {
46
+ rename: (item: { type: number; metadata?: number; count: number; nbt?: unknown }, name: string) => Promise<void>
47
+ combine: (itemOne: unknown, itemTwo: unknown, name?: string) => Promise<void>
48
+ findInventoryItem: (itemId: number) => unknown | null
49
+ }
50
+
51
+ /** Beacon — mineflayer has no dedicated plugin; uses packets. */
52
+ export interface MineflayerBeaconWindow extends MineflayerBotWindow {}
53
+
20
54
  export interface MineflayerBot {
21
55
  inventory: {
22
56
  slots: Array<{ type: number; count: number; metadata?: number; nbt?: unknown } | null>
23
57
  }
24
- currentWindow: {
25
- id: number
26
- type: string
27
- title?: string
28
- slots: Array<{ type: number; count: number; metadata?: number; nbt?: unknown } | null>
29
- } | null
58
+ currentWindow: MineflayerBotWindow | null
30
59
  heldItem: { type: number; count: number; metadata?: number; nbt?: unknown } | null
31
- health: number
32
- food: number
33
- foodSaturation: number
34
60
  experience: { level: number; progress: number; points: number }
35
61
  quickBarSlot: number
62
+ /** Entity currently being ridden, if any. Name is the mineflayer entity type string. */
63
+ vehicle?: { name?: string; entityType?: string; id?: number; [key: string]: unknown } | null
36
64
  clickWindow(slot: number, mouseButton: number, mode: number): Promise<void>
37
65
  closeWindow(window: unknown): void
38
66
  on(event: string, listener: (...args: unknown[]) => void): void
39
67
  off(event: string, listener: (...args: unknown[]) => void): void
40
68
  removeListener(event: string, listener: (...args: unknown[]) => void): void
69
+ /** For beacon/special packets; optional. */
70
+ _client?: { write: (name: string, data: unknown) => void }
41
71
  }
@@ -1,22 +1,23 @@
1
1
  import React, { createContext, useContext } from 'react'
2
2
 
3
- // Default: 1.16.4 for GUI textures (most containers existed here; 1.21.4 for items)
3
+ // Default texture source: zardoy/mc-assets gh-pages
4
+ export const MC_ASSETS_BASE =
5
+ 'https://raw.githubusercontent.com/zardoy/mc-assets/refs/heads/gh-pages'
4
6
  export const MC_ITEMS_BASE =
5
- 'https://raw.githubusercontent.com/PrismarineJS/minecraft-assets/master/data/1.21.4'
6
- export const MC_GUI_BASE =
7
- 'https://raw.githubusercontent.com/PrismarineJS/minecraft-assets/master/data'
7
+ `${MC_ASSETS_BASE}/1.21.11/textures`
8
+ export const MC_GUI_BASE = MC_ASSETS_BASE
8
9
 
9
10
  export interface TextureConfig {
10
11
  baseUrl: string
11
12
  getItemTextureUrl(item: { type: number; name?: string }): string
12
13
  getBlockTextureUrl(item: { type: number; name?: string }): string
13
- /** version defaults to '1.16.4' for GUI textures unless overridden per-container */
14
+ /** Supports full mc-assets paths (e.g. "1.21.11/textures/gui/container/anvil.png") */
14
15
  getGuiTextureUrl(path: string, version?: string): string
15
16
  }
16
17
 
17
18
  function buildDefault(base: string): TextureConfig {
18
19
  const isRemote = base.startsWith('http')
19
- // For remote default: use the same base for items, separate versioned base for GUI
20
+ // For remote default: use standardized versioned paths from mc-assets.
20
21
  const guiBase = isRemote ? MC_GUI_BASE : base
21
22
 
22
23
  return {
@@ -24,19 +25,24 @@ function buildDefault(base: string): TextureConfig {
24
25
 
25
26
  getItemTextureUrl({ type, name }) {
26
27
  const root = isRemote ? MC_ITEMS_BASE : base
27
- if (name) return `${root}/items/${name}.png`
28
- return `${root}/items/${type}.png`
28
+ if (name) return `${root}/item/${name}.png`
29
+ return `${root}/item/${type}.png`
29
30
  },
30
31
 
31
32
  getBlockTextureUrl({ type, name }) {
32
33
  const root = isRemote ? MC_ITEMS_BASE : base
33
- if (name) return `${root}/blocks/${name}.png`
34
- return `${root}/blocks/${type}.png`
34
+ if (name) return `${root}/block/${name}.png`
35
+ return `${root}/block/${type}.png`
35
36
  },
36
37
 
37
38
  getGuiTextureUrl(path: string, version = '1.16.4') {
38
39
  if (isRemote) {
39
- return `${guiBase}/${version}/${path}.png`
40
+ // New format: "1.21.11/textures/gui/container/anvil.png"
41
+ if (path.endsWith('.png') && path.includes('/textures/')) {
42
+ return `${guiBase}/${path}`
43
+ }
44
+ // Backward compatibility with legacy "gui/container/anvil" + version fields.
45
+ return `${guiBase}/${version}/textures/${path}.png`
40
46
  }
41
47
  return `${base}/textures/${path}.png`
42
48
  },
@@ -0,0 +1,112 @@
1
+ // Auto-generated by scripts/generate-texture-imports.mjs — do not edit manually
2
+ // Re-run `node scripts/generate-texture-imports.mjs` after changing inventories.
3
+
4
+ import _gui_container_inventory from 'mc-assets/dist/other-textures/latest/gui/container/inventory.png'
5
+ import _gui_container_shulker_box from 'mc-assets/dist/other-textures/latest/gui/container/shulker_box.png'
6
+ import _gui_container_generic_54 from 'mc-assets/dist/other-textures/latest/gui/container/generic_54.png'
7
+ import _gui_container_crafting_table from 'mc-assets/dist/other-textures/latest/gui/container/crafting_table.png'
8
+ import _gui_container_furnace from 'mc-assets/dist/other-textures/latest/gui/container/furnace.png'
9
+ import _gui_container_blast_furnace from 'mc-assets/dist/other-textures/latest/gui/container/blast_furnace.png'
10
+ import _gui_container_smoker from 'mc-assets/dist/other-textures/latest/gui/container/smoker.png'
11
+ import _gui_container_brewing_stand from 'mc-assets/dist/other-textures/latest/gui/container/brewing_stand.png'
12
+ import _gui_container_anvil from 'mc-assets/dist/other-textures/latest/gui/container/anvil.png'
13
+ import _gui_container_grindstone from 'mc-assets/dist/other-textures/latest/gui/container/grindstone.png'
14
+ import _gui_container_enchanting_table from 'mc-assets/dist/other-textures/latest/gui/container/enchanting_table.png'
15
+ import _gui_container_smithing from 'mc-assets/dist/other-textures/latest/gui/container/smithing.png'
16
+ import _gui_container_hopper from 'mc-assets/dist/other-textures/latest/gui/container/hopper.png'
17
+ import _gui_container_dispenser from 'mc-assets/dist/other-textures/latest/gui/container/dispenser.png'
18
+ import _gui_container_beacon from 'mc-assets/dist/other-textures/latest/gui/container/beacon.png'
19
+ import _gui_container_horse from 'mc-assets/dist/other-textures/latest/gui/container/horse.png'
20
+ import _gui_container_villager2 from 'mc-assets/dist/other-textures/latest/gui/container/villager2.png'
21
+ import _gui_container_cartography_table from 'mc-assets/dist/other-textures/latest/gui/container/cartography_table.png'
22
+ import _gui_container_loom from 'mc-assets/dist/other-textures/latest/gui/container/loom.png'
23
+ import _gui_container_stonecutter from 'mc-assets/dist/other-textures/latest/gui/container/stonecutter.png'
24
+ import _gui_container_crafter from 'mc-assets/dist/other-textures/latest/gui/container/crafter.png'
25
+ import _gui_container_creative_inventory_tab_items from 'mc-assets/dist/other-textures/latest/gui/container/creative_inventory/tab_items.png'
26
+ import _gui_widgets from 'mc-assets/dist/other-textures/1.15/gui/widgets.png'
27
+
28
+ /**
29
+ * Maps each inventory type name to its texture path (version prefix stripped).
30
+ * Useful for building custom texture resolvers or inspecting the full texture list.
31
+ */
32
+ export const allContainerPaths: Record<string, string> = {
33
+ player: 'gui/container/inventory.png',
34
+ chest: 'gui/container/shulker_box.png',
35
+ generic_9x1: 'gui/container/shulker_box.png',
36
+ large_chest: 'gui/container/generic_54.png',
37
+ crafting_table: 'gui/container/crafting_table.png',
38
+ furnace: 'gui/container/furnace.png',
39
+ blast_furnace: 'gui/container/blast_furnace.png',
40
+ smoker: 'gui/container/smoker.png',
41
+ brewing_stand: 'gui/container/brewing_stand.png',
42
+ anvil: 'gui/container/anvil.png',
43
+ grindstone: 'gui/container/grindstone.png',
44
+ enchanting_table: 'gui/container/enchanting_table.png',
45
+ smithing_table: 'gui/container/smithing.png',
46
+ smithing_table_legacy: 'gui/container/smithing.png',
47
+ hopper: 'gui/container/hopper.png',
48
+ dispenser: 'gui/container/dispenser.png',
49
+ dropper: 'gui/container/dispenser.png',
50
+ beacon: 'gui/container/beacon.png',
51
+ horse: 'gui/container/horse.png',
52
+ donkey: 'gui/container/horse.png',
53
+ llama: 'gui/container/horse.png',
54
+ villager: 'gui/container/villager2.png',
55
+ shulker_box: 'gui/container/shulker_box.png',
56
+ barrel: 'gui/container/shulker_box.png',
57
+ cartography_table: 'gui/container/cartography_table.png',
58
+ loom: 'gui/container/loom.png',
59
+ stonecutter: 'gui/container/stonecutter.png',
60
+ crafter: 'gui/container/crafter.png',
61
+ creative: 'gui/container/creative_inventory/tab_items.png',
62
+ hotbar: 'gui/widgets.png',
63
+ }
64
+
65
+ // Internal: full versioned texture path → bundled asset URL (or undefined = not found)
66
+ const _map: Record<string, string | undefined> = {
67
+ '1.21.11/textures/gui/container/inventory.png': _gui_container_inventory,
68
+ '1.21.11/textures/gui/container/shulker_box.png': _gui_container_shulker_box,
69
+ '1.21.11/textures/gui/container/generic_54.png': _gui_container_generic_54,
70
+ '1.21.11/textures/gui/container/crafting_table.png': _gui_container_crafting_table,
71
+ '1.21.11/textures/gui/container/furnace.png': _gui_container_furnace,
72
+ '1.21.11/textures/gui/container/blast_furnace.png': _gui_container_blast_furnace,
73
+ '1.21.11/textures/gui/container/smoker.png': _gui_container_smoker,
74
+ '1.21.11/textures/gui/container/brewing_stand.png': _gui_container_brewing_stand,
75
+ '1.21.11/textures/gui/container/anvil.png': _gui_container_anvil,
76
+ '1.21.11/textures/gui/container/grindstone.png': _gui_container_grindstone,
77
+ '1.21.11/textures/gui/container/enchanting_table.png': _gui_container_enchanting_table,
78
+ '1.21.11/textures/gui/container/smithing.png': _gui_container_smithing,
79
+ '1.16.4/textures/gui/container/smithing.png': _gui_container_smithing,
80
+ '1.21.11/textures/gui/container/hopper.png': _gui_container_hopper,
81
+ '1.21.11/textures/gui/container/dispenser.png': _gui_container_dispenser,
82
+ '1.21.11/textures/gui/container/beacon.png': _gui_container_beacon,
83
+ '1.21.11/textures/gui/container/horse.png': _gui_container_horse,
84
+ '1.14/textures/gui/container/villager2.png': _gui_container_villager2,
85
+ '1.21.11/textures/gui/container/cartography_table.png': _gui_container_cartography_table,
86
+ '1.21.11/textures/gui/container/loom.png': _gui_container_loom,
87
+ '1.21.11/textures/gui/container/stonecutter.png': _gui_container_stonecutter,
88
+ '1.21.11/textures/gui/container/crafter.png': _gui_container_crafter,
89
+ '1.21.11/textures/gui/container/creative_inventory/tab_items.png': _gui_container_creative_inventory_tab_items,
90
+ '1.15/textures/gui/widgets.png': _gui_widgets,
91
+ }
92
+
93
+ /**
94
+ * Partial TextureConfig that resolves inventory GUI textures from locally bundled
95
+ * mc-assets assets instead of remote GitHub URLs.
96
+ *
97
+ * Pass to `<TextureProvider config={localTexturesConfig}>` to use offline/bundled assets:
98
+ *
99
+ * ```tsx
100
+ * import { localTexturesConfig } from './generated/localTextures'
101
+ * <TextureProvider config={localTexturesConfig}>
102
+ * <InventoryOverlay type="chest" ... />
103
+ * </TextureProvider>
104
+ * ```
105
+ */
106
+ export const localTexturesConfig = {
107
+ getGuiTextureUrl(path: string): string {
108
+ // _map keys are the full versioned paths used in inventories.ts
109
+ // e.g. '1.21.11/textures/gui/container/anvil.png'
110
+ return (_map[path] as string | undefined) ?? path
111
+ },
112
+ }
package/src/globals.d.ts CHANGED
@@ -2,3 +2,7 @@ declare module '*.module.css' {
2
2
  const content: Record<string, string>
3
3
  export default content
4
4
  }
5
+ declare module '*.png' {
6
+ const content: string
7
+ export default content
8
+ }
package/src/index.tsx CHANGED
@@ -14,7 +14,8 @@ export { ItemCanvas } from './components/ItemCanvas'
14
14
  export { Tooltip } from './components/Tooltip'
15
15
  export { JEI } from './components/JEI'
16
16
  export type { JEIItem } from './components/JEI'
17
- export { Hotbar } from './components/Hotbar'
17
+ export { Notes } from './components/Notes'
18
+ export type { Note } from './components/Notes'
18
19
  export { HUD } from './components/HUD'
19
20
  export { CursorItem } from './components/CursorItem'
20
21
  export { InventoryOverlay } from './components/InventoryOverlay'
@@ -46,7 +47,7 @@ export type { ActionLogEntry, DemoConnectorOptions } from './connector/demo'
46
47
 
47
48
  // Registry
48
49
  export { registerInventoryType, getInventoryType, getAllInventoryTypes } from './registry'
49
- export type { InventoryTypeDefinition } from './registry'
50
+ export type { InventoryTypeDefinition, WindowType } from './registry'
50
51
 
51
52
  // Types
52
53
  export type {
@@ -19,3 +19,4 @@ export function getAllInventoryTypes(): InventoryTypeDefinition[] {
19
19
 
20
20
  export { inventoryDefinitions }
21
21
  export type { InventoryTypeDefinition }
22
+ export type WindowType = keyof typeof inventoryDefinitions