minecraft-inventory 0.1.0

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.
Files changed (51) hide show
  1. package/README.md +566 -0
  2. package/package.json +37 -0
  3. package/src/InventoryGUI.tsx +108 -0
  4. package/src/cache/textureCache.ts +95 -0
  5. package/src/components/CursorItem/CursorItem.tsx +94 -0
  6. package/src/components/CursorItem/index.ts +1 -0
  7. package/src/components/HUD/HUD.tsx +11 -0
  8. package/src/components/HUD/index.ts +1 -0
  9. package/src/components/Hotbar/Hotbar.tsx +180 -0
  10. package/src/components/Hotbar/index.ts +1 -0
  11. package/src/components/InventoryOverlay/InventoryOverlay.tsx +196 -0
  12. package/src/components/InventoryOverlay/index.ts +2 -0
  13. package/src/components/InventoryWindow/EnchantmentOptions.tsx +109 -0
  14. package/src/components/InventoryWindow/InventoryBackground.tsx +110 -0
  15. package/src/components/InventoryWindow/InventoryWindow.tsx +120 -0
  16. package/src/components/InventoryWindow/ProgressBar.tsx +78 -0
  17. package/src/components/InventoryWindow/VillagerTradeList.tsx +136 -0
  18. package/src/components/InventoryWindow/index.ts +5 -0
  19. package/src/components/ItemCanvas/ItemCanvas.tsx +154 -0
  20. package/src/components/ItemCanvas/index.ts +1 -0
  21. package/src/components/JEI/JEI.module.css +37 -0
  22. package/src/components/JEI/JEI.tsx +303 -0
  23. package/src/components/JEI/index.ts +2 -0
  24. package/src/components/RecipeGuide/RecipeInventoryView.tsx +293 -0
  25. package/src/components/RecipeGuide/index.ts +1 -0
  26. package/src/components/Slot/Slot.module.css +111 -0
  27. package/src/components/Slot/Slot.tsx +363 -0
  28. package/src/components/Slot/index.ts +1 -0
  29. package/src/components/Text/MessageFormatted.css +5 -0
  30. package/src/components/Text/MessageFormatted.tsx +79 -0
  31. package/src/components/Text/MessageFormattedString.tsx +74 -0
  32. package/src/components/Text/chatUtils.ts +172 -0
  33. package/src/components/Tooltip/Tooltip.module.css +56 -0
  34. package/src/components/Tooltip/Tooltip.tsx +130 -0
  35. package/src/components/Tooltip/index.ts +1 -0
  36. package/src/connector/demo.ts +213 -0
  37. package/src/connector/index.ts +4 -0
  38. package/src/connector/mineflayer.ts +113 -0
  39. package/src/connector/types.ts +41 -0
  40. package/src/context/InventoryContext.tsx +157 -0
  41. package/src/context/ScaleContext.tsx +73 -0
  42. package/src/context/TextureContext.tsx +70 -0
  43. package/src/globals.d.ts +4 -0
  44. package/src/hooks/useKeyboardShortcuts.ts +41 -0
  45. package/src/hooks/useMobile.ts +28 -0
  46. package/src/index.tsx +65 -0
  47. package/src/mount.tsx +52 -0
  48. package/src/registry/index.ts +21 -0
  49. package/src/registry/inventories.ts +612 -0
  50. package/src/styles/tokens.css +47 -0
  51. package/src/types.ts +176 -0
package/README.md ADDED
@@ -0,0 +1,566 @@
1
+ # minecraft-inventory
2
+
3
+ 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.
4
+
5
+ ## Features
6
+
7
+ - All standard inventory types (chest, furnace, crafting table, villager trades, enchanting table, brewing stand, anvil, smithing table, horse, beacon, and more)
8
+ - CSS variable–based scaling — no layout jank, no `transform: scale`
9
+ - `<img>`-rendered item textures with automatic `items/` → `blocks/` fallback (via PrismarineJS asset mirror by default)
10
+ - Tooltips that follow the cursor, matching the original Minecraft style
11
+ - Full keyboard support: number keys (1-9) to swap hotbar, Q to drop, double-click to collect, scroll wheel to pick/place
12
+ - Mobile support: tap to open a context menu (take all / half / custom amount / drop)
13
+ - Optional JEI (item browser) panel on the left or right
14
+ - Bot connector layer — plugs into a mineflayer bot or any custom server connection
15
+ - Demo connector with action logging for local development
16
+ - Extendable registry — add new inventory types in one place
17
+
18
+ ---
19
+
20
+ ## Quick Start (React)
21
+
22
+ ```tsx
23
+ import React from 'react'
24
+ import { createRoot } from 'react-dom/client'
25
+ import { InventoryGUI, createDemoConnector } from 'minecraft-inventory'
26
+
27
+ const connector = createDemoConnector({
28
+ windowType: 'chest',
29
+ windowTitle: 'My Chest',
30
+ slots: [
31
+ { index: 0, item: { type: 265, name: 'iron_ingot', count: 32, displayName: 'Iron Ingot' } },
32
+ // ...more slots
33
+ ],
34
+ })
35
+
36
+ function App() {
37
+ return (
38
+ <InventoryGUI
39
+ type="chest"
40
+ connector={connector}
41
+ scale={2}
42
+ showJEI
43
+ jeiItems={[
44
+ { type: 1, name: 'stone', displayName: 'Stone', count: 1 },
45
+ { type: 4, name: 'cobblestone', displayName: 'Cobblestone', count: 1 },
46
+ ]}
47
+ />
48
+ )
49
+ }
50
+
51
+ // Mount into the DOM
52
+ createRoot(document.getElementById('root')!).render(<App />)
53
+ ```
54
+
55
+ ## Quick Start (No React — programmatic)
56
+
57
+ If you don't use React, use the `mountInventory` helper:
58
+
59
+ ```html
60
+ <div id="inventory"></div>
61
+ <script type="module">
62
+ import { mountInventory, createDemoConnector } from 'minecraft-inventory'
63
+
64
+ const connector = createDemoConnector({
65
+ windowType: 'chest',
66
+ windowTitle: 'My Chest',
67
+ slots: [
68
+ { index: 0, item: { type: 265, name: 'iron_ingot', count: 32, displayName: 'Iron Ingot' } },
69
+ ],
70
+ })
71
+
72
+ const inv = mountInventory(document.getElementById('inventory'), {
73
+ type: 'chest',
74
+ connector,
75
+ scale: 2,
76
+ })
77
+
78
+ // Update props later
79
+ inv.update({ scale: 3 })
80
+
81
+ // Destroy when done
82
+ inv.destroy()
83
+ </script>
84
+ ```
85
+
86
+ ---
87
+
88
+ ## Installation
89
+
90
+ ```bash
91
+ npm install minecraft-inventory
92
+ # or
93
+ pnpm add minecraft-inventory
94
+ ```
95
+
96
+ ---
97
+
98
+ ## Core API
99
+
100
+ ### `<InventoryGUI>` — All-in-one component
101
+
102
+ ```tsx
103
+ <InventoryGUI
104
+ type="chest" // Inventory type name (see registry)
105
+ title="My Chest" // Override window title
106
+ slots={[...]} // Optional: provide slots directly
107
+ properties={{ litTime: 100 }} // Optional: progress bar data
108
+ connector={connector} // Optional: bot connector
109
+ scale={2} // Scale multiplier (default: 2)
110
+ showJEI={false} // Show item browser panel
111
+ jeiItems={[...]} // Items to show in JEI
112
+ jeiPosition="right" // 'left' | 'right'
113
+ textureBaseUrl="/assets/mc" // Override texture base URL
114
+ enableKeyboardShortcuts={true}
115
+ onClose={() => {}}
116
+ />
117
+ ```
118
+
119
+ ### `<InventoryOverlay>` — Full-screen overlay with backdrop
120
+
121
+ Renders the inventory centered on screen with a semi-transparent backdrop. Clicking outside drops the held item or closes the window.
122
+
123
+ ```tsx
124
+ import { InventoryOverlay, InventoryProvider, ScaleProvider, TextureProvider } from 'minecraft-inventory'
125
+
126
+ <TextureProvider>
127
+ <ScaleProvider scale={2}>
128
+ <InventoryProvider connector={connector}>
129
+ <InventoryOverlay
130
+ type="chest"
131
+ showBackdrop // default: true (50% black)
132
+ backdropColor="rgba(0,0,0,0.5)"
133
+ showHotbar // default: true
134
+ showJEI
135
+ jeiItems={items}
136
+ jeiPosition="right"
137
+ onClose={() => console.log('closed')}
138
+ >
139
+ {/* Extra children (e.g. notes panel) are rendered inside the centered anchor */}
140
+ </InventoryOverlay>
141
+ </InventoryProvider>
142
+ </ScaleProvider>
143
+ </TextureProvider>
144
+ ```
145
+
146
+ ### Manual composition
147
+
148
+ For full control, use the provider components and compose manually:
149
+
150
+ ```tsx
151
+ import {
152
+ TextureProvider, ScaleProvider, InventoryProvider,
153
+ InventoryWindow, Hotbar, HUD, CursorItem, JEI,
154
+ } from 'minecraft-inventory'
155
+
156
+ function MyInventory({ connector }) {
157
+ return (
158
+ <TextureProvider baseUrl="https://example.com/assets">
159
+ <ScaleProvider scale={2}>
160
+ <InventoryProvider connector={connector}>
161
+ <div style={{ display: 'flex', gap: 8 }}>
162
+ <InventoryWindow type="furnace" />
163
+ <JEI items={allItems} position="right" />
164
+ </div>
165
+ <Hotbar />
166
+ <HUD />
167
+ <CursorItem />
168
+ </InventoryProvider>
169
+ </ScaleProvider>
170
+ </TextureProvider>
171
+ )
172
+ }
173
+ ```
174
+
175
+ ---
176
+
177
+ ## Scaling
178
+
179
+ Scale is driven entirely by CSS custom properties — no `transform: scale`. Changing the scale prop recalculates all size tokens:
180
+
181
+ | Variable | Default (scale=2) |
182
+ |---|---|
183
+ | `--mc-scale` | `2` |
184
+ | `--mc-slot-size` | `36px` (18 × scale) |
185
+ | `--mc-font-size` | `14px` (7 × scale) |
186
+ | `--mc-pixel` | `2px` |
187
+
188
+ ```tsx
189
+ // Changing scale re-renders the whole GUI at new dimensions
190
+ <InventoryGUI type="chest" scale={3} />
191
+ ```
192
+
193
+ ---
194
+
195
+ ## Textures
196
+
197
+ By default, item textures are fetched from the PrismarineJS Minecraft assets mirror on GitHub:
198
+
199
+ ```
200
+ https://raw.githubusercontent.com/PrismarineJS/minecraft-assets/master/data/1.21.4/item/{name}.png
201
+ ```
202
+
203
+ Provide `name` on each `ItemStack` to use the correct asset:
204
+
205
+ ```ts
206
+ { type: 276, name: 'diamond_sword', count: 1, displayName: 'Diamond Sword' }
207
+ ```
208
+
209
+ To use local textures (e.g., from a resource pack server):
210
+
211
+ ```tsx
212
+ <InventoryGUI
213
+ type="chest"
214
+ textureBaseUrl="/assets/minecraft"
215
+ // → /assets/minecraft/item/diamond_sword.png
216
+ />
217
+ ```
218
+
219
+ For full control, pass a custom texture config overriding any or all URL resolvers:
220
+
221
+ ```tsx
222
+ import { TextureProvider } from 'minecraft-inventory'
223
+
224
+ // Option A — simple base URL (all textures served from your CDN/server)
225
+ <TextureProvider baseUrl="https://yourserver.com/mc-assets/1.21.4">
226
+ {/* item textures: /mc-assets/1.21.4/items/{name}.png */}
227
+ {/* block textures: /mc-assets/1.21.4/blocks/{name}.png */}
228
+ {/* GUI textures: /mc-assets/1.21.4/textures/{path}.png */}
229
+ <InventoryWindow type="chest" />
230
+ </TextureProvider>
231
+
232
+ // Option B — replace individual resolvers (config is merged with defaults)
233
+ <TextureProvider config={{
234
+ getItemTextureUrl: ({ type, name }) =>
235
+ `https://cdn.example.com/items/${name ?? type}.png`,
236
+
237
+ getBlockTextureUrl: ({ type, name }) =>
238
+ `https://cdn.example.com/blocks/${name ?? type}.png`,
239
+
240
+ // version param lets you vary GUI textures per container (1.16.4, 1.21.4, etc.)
241
+ getGuiTextureUrl: (path, version = '1.16.4') =>
242
+ `https://cdn.example.com/gui/${version}/${path}.png`,
243
+ }}>
244
+ <InventoryWindow type="furnace" />
245
+ </TextureProvider>
246
+
247
+ // Option C — completely static local assets (no CDN)
248
+ <TextureProvider config={{
249
+ baseUrl: '/assets',
250
+ getItemTextureUrl: ({ name, type }) => `/assets/items/${name ?? type}.png`,
251
+ getBlockTextureUrl: ({ name, type }) => `/assets/blocks/${name ?? type}.png`,
252
+ getGuiTextureUrl: (path) => `/assets/gui/${path}.png`,
253
+ }}>
254
+ <InventoryWindow type="chest" />
255
+ </TextureProvider>
256
+ ```
257
+
258
+ > **Per-container texture version:** Each inventory definition can carry a `guiTextureVersion` string (e.g. `'1.21.4'`) that is passed to `getGuiTextureUrl` as the second argument. Older containers default to `'1.16.4'`; newer ones (crafter, new smithing table) use `'1.21.4'`. Specify it when [registering custom containers](#adding-new-inventory-types).
259
+
260
+ ---
261
+
262
+ ## Connector
263
+
264
+ The connector is the bridge between the GUI and a Minecraft server or bot.
265
+
266
+ ### Mineflayer connector
267
+
268
+ ```ts
269
+ import { createMineflayerConnector } from 'minecraft-inventory'
270
+ import mineflayer from 'mineflayer'
271
+
272
+ const bot = mineflayer.createBot({ ... })
273
+
274
+ bot.on('windowOpen', (window) => {
275
+ const connector = createMineflayerConnector(bot)
276
+
277
+ // Mount <InventoryGUI connector={connector} type={window.type} />
278
+ })
279
+ ```
280
+
281
+ The connector translates GUI actions (slot clicks, drag, drop, etc.) into `bot.clickWindow()` calls and subscribes to mineflayer inventory events to keep the GUI in sync.
282
+
283
+ ### Demo connector (for local testing)
284
+
285
+ ```ts
286
+ import { createDemoConnector } from 'minecraft-inventory'
287
+
288
+ const connector = createDemoConnector({
289
+ windowType: 'crafting_table',
290
+ windowTitle: 'Crafting',
291
+ slots: [...],
292
+ onAction: (entry) => {
293
+ console.log(entry.description, entry.action)
294
+ },
295
+ })
296
+
297
+ // Update slots programmatically:
298
+ connector.updateSlots(newSlots)
299
+ connector.setHeldItem({ type: 264, name: 'diamond', count: 1 })
300
+ connector.openWindow('furnace', 'Furnace', furnaceSlots)
301
+ connector.closeWindowExternal()
302
+ ```
303
+
304
+ ### Custom connector
305
+
306
+ Implement the `InventoryConnector` interface for any other backend:
307
+
308
+ ```ts
309
+ import type { InventoryConnector } from 'minecraft-inventory'
310
+
311
+ const myConnector: InventoryConnector = {
312
+ getWindowState: () => ({ windowId: 1, type: 'chest', slots: [...], heldItem: null }),
313
+ getPlayerState: () => null,
314
+ sendAction: async (action) => {
315
+ // send action to server
316
+ console.log('action', action)
317
+ },
318
+ closeWindow: () => {},
319
+ subscribe: (listener) => {
320
+ // call listener({ type: 'windowUpdate', state }) on changes
321
+ return () => {} // cleanup
322
+ },
323
+ }
324
+ ```
325
+
326
+ ---
327
+
328
+ ## Keyboard Shortcuts (Desktop)
329
+
330
+ | Key | Action |
331
+ |---|---|
332
+ | Left click | Pick up all / place all |
333
+ | Right click | Pick up half / place one |
334
+ | Shift + Left click | Transfer to/from container |
335
+ | Double click | Collect all of same item type |
336
+ | 1–9 (while hovering) | Swap slot with hotbar slot N |
337
+ | Q (while hovering) | Drop one item |
338
+ | Ctrl+Q | Drop entire stack |
339
+ | Scroll up | Pick up one more (right-click equivalent) |
340
+ | Scroll down | Put one back |
341
+ | Esc | Close window (`onClose` callback) |
342
+
343
+ ---
344
+
345
+ ## Mobile Support
346
+
347
+ On touch devices, tapping a slot with no held item opens a context menu instead of immediately picking up the item. The menu appears to the side in landscape or below in portrait.
348
+
349
+ Context menu options:
350
+ - **Take All** — picks up the entire stack
351
+ - **Take Half** — picks up half
352
+ - **Take Amount…** — `window.prompt` to enter a custom quantity
353
+ - **Drop** — drops the stack from the slot
354
+ - **Cancel**
355
+
356
+ When you have a held item, tapping a slot places/transfers it (same as left-click on desktop).
357
+
358
+ ---
359
+
360
+ ## JEI — Item Browser
361
+
362
+ ```tsx
363
+ import { JEI } from 'minecraft-inventory'
364
+
365
+ const items = [
366
+ { type: 264, name: 'diamond', displayName: 'Diamond' },
367
+ { type: 265, name: 'iron_ingot', displayName: 'Iron Ingot' },
368
+ // ...
369
+ ]
370
+
371
+ <JEI
372
+ items={items}
373
+ position="right" // 'left' | 'right'
374
+ onItemClick={(item) => {}} // left-click handler
375
+ onItemMiddleClick={(item) => {}} // middle-click handler
376
+ />
377
+ ```
378
+
379
+ Use the search bar to filter by display name or item name. Scroll wheel or arrow buttons to paginate.
380
+
381
+ ---
382
+
383
+ ## Adding New Inventory Types
384
+
385
+ All inventory types live in a single registry file:
386
+
387
+ **`src/registry/inventories.ts`**
388
+
389
+ Add a new entry to the `inventoryDefinitions` object:
390
+
391
+ ```ts
392
+ export const inventoryDefinitions: Record<string, InventoryTypeDefinition> = {
393
+ // ... existing types ...
394
+
395
+ my_custom_chest: {
396
+ name: 'my_custom_chest',
397
+ title: 'Custom Chest',
398
+ backgroundTexture: 'gui/container/my_custom_chest', // path under textures/
399
+ backgroundWidth: 176,
400
+ backgroundHeight: 166,
401
+ includesPlayerInventory: true,
402
+ includesHotbar: true,
403
+ slots: [
404
+ // Custom container slots (indices 0-N)
405
+ ...gridSlots(0, 9, 3, 8, 18, 'container'),
406
+ // Player inventory (adjust indices to match server protocol)
407
+ ...playerInv(84, 27, 54),
408
+ ],
409
+ },
410
+ }
411
+ ```
412
+
413
+ **Slot index conventions:**
414
+ - Container slots always start at `0`
415
+ - Player inventory and hotbar indices vary by window type (match the server protocol)
416
+ - Use `playerInv(yPos, invStartIndex, hotbarStartIndex)` for the standard 3×9 + hotbar layout
417
+
418
+ **Progress bars** (for furnaces, brewing stands, etc.):
419
+
420
+ ```ts
421
+ progressBars: [
422
+ {
423
+ id: 'cook_arrow',
424
+ x: 79, y: 34, width: 24, height: 16,
425
+ direction: 'right', // 'right' | 'up' | 'down' | 'left'
426
+ textureX: 176, textureY: 14, // source coords in background texture
427
+ getValue: (props) => props.cookingProgress ?? 0,
428
+ getMax: (props) => props.totalCookTime || 200,
429
+ },
430
+ ],
431
+ ```
432
+
433
+ **Properties** (data slots from server):
434
+
435
+ ```ts
436
+ properties: {
437
+ cookingProgress: { dataSlot: 2, description: 'Cook progress (0-200)' },
438
+ totalCookTime: { dataSlot: 3, description: 'Total cook time' },
439
+ },
440
+ ```
441
+
442
+ **Registering at runtime** (optional, for plugins/mods):
443
+
444
+ ```ts
445
+ import { registerInventoryType } from 'minecraft-inventory'
446
+
447
+ registerInventoryType({
448
+ name: 'my_mod_inventory',
449
+ title: 'Mod Inventory',
450
+ backgroundTexture: 'gui/container/my_mod',
451
+ backgroundWidth: 176,
452
+ backgroundHeight: 166,
453
+ slots: [...],
454
+ })
455
+ ```
456
+
457
+ Then use it anywhere:
458
+
459
+ ```tsx
460
+ <InventoryGUI type="my_mod_inventory" connector={connector} />
461
+ ```
462
+
463
+ ---
464
+
465
+ ## ItemStack Type
466
+
467
+ ```ts
468
+ interface ItemStack {
469
+ type: number // Numeric item ID
470
+ name?: string // Snake_case name, e.g. 'diamond_sword' (used for texture URL)
471
+ count: number
472
+ metadata?: number
473
+ nbt?: Record<string, unknown>
474
+ displayName?: string
475
+ enchantments?: Array<{ name: string; level: number }>
476
+ lore?: string[]
477
+ durability?: number // Current durability value
478
+ maxDurability?: number // Max durability (renders bar when < max)
479
+ }
480
+ ```
481
+
482
+ ---
483
+
484
+ ## Project Structure
485
+
486
+ ```
487
+ src/
488
+ index.tsx # Library entry point / exports
489
+ types.ts # Core TypeScript types
490
+ registry/
491
+ index.ts # registerInventoryType / getInventoryType
492
+ inventories.ts # All built-in inventory type definitions ← ADD NEW TYPES HERE
493
+ components/
494
+ InventoryWindow/ # Main window renderer
495
+ Slot/ # Individual slot (click/drag/keyboard/mobile)
496
+ ItemCanvas/ # Canvas-based item texture rendering
497
+ Tooltip/ # Cursor-following item tooltip
498
+ JEI/ # Item browser panel
499
+ Hotbar/ # Hotbar with active slot indicator
500
+ HUD/ # XP bar
501
+ CursorItem/ # Floating item following mouse cursor
502
+ context/
503
+ InventoryContext.tsx # Shared state (held item, hover, drag)
504
+ ScaleContext.tsx # CSS variable scale provider
505
+ TextureContext.tsx # Texture URL resolver
506
+ connector/
507
+ types.ts # InventoryConnector interface
508
+ mineflayer.ts # Mineflayer bot adapter
509
+ demo.ts # Demo connector with action log
510
+ hooks/
511
+ useMobile.ts
512
+ useKeyboardShortcuts.ts
513
+ styles/
514
+ tokens.css # CSS custom property definitions
515
+ playground/
516
+ src/
517
+ App.tsx # Interactive playground / demo
518
+ mockItems.ts # Sample items for testing
519
+ ```
520
+
521
+ ---
522
+
523
+ ## Contributing
524
+
525
+ ### Running the playground
526
+
527
+ ```bash
528
+ pnpm install
529
+ pnpm dev
530
+ # Opens at http://localhost:3200
531
+ ```
532
+
533
+ ### Adding a new inventory type
534
+
535
+ 1. Open `src/registry/inventories.ts`
536
+ 2. Add a new key to `inventoryDefinitions` (see template above)
537
+ 3. Test it in the playground by adding it to `INVENTORY_TYPES` in `playground/src/App.tsx`
538
+ 4. Submit a PR with the new type and sample mock slots in `playground/src/mockItems.ts`
539
+
540
+ ### Slot index reference
541
+
542
+ 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:
543
+ - [`prismarine-windows`](https://github.com/PrismarineJS/prismarine-windows) for JS slot maps
544
+ - Minecraft source: `net/minecraft/world/inventory/` for the canonical layout
545
+
546
+ ### Connector protocol
547
+
548
+ When implementing a custom connector, actions have these shapes:
549
+
550
+ | Action type | Fields |
551
+ |---|---|
552
+ | `click` | `slotIndex`, `button` ('left'/'right'/'middle'), `mode` ('normal'/'shift'/'double'/'number'/'drop'), `numberKey?` |
553
+ | `drop` | `slotIndex`, `all` (boolean) |
554
+ | `drag` | `slots` (number[]), `button` |
555
+ | `trade` | `tradeIndex` |
556
+ | `rename` | `text` |
557
+ | `enchant` | `enchantIndex` (0/1/2) |
558
+ | `beacon` | `primaryEffect`, `secondaryEffect` |
559
+ | `hotbar-swap` | `slotIndex`, `hotbarSlot` (0-8) |
560
+ | `close` | — |
561
+
562
+ ---
563
+
564
+ ## License
565
+
566
+ MIT
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "minecraft-inventory",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "license": "MIT",
6
+ "release": {
7
+ "initialVersion": {
8
+ "version": "0.1.0"
9
+ }
10
+ },
11
+ "files": [
12
+ "src"
13
+ ],
14
+ "devDependencies": {
15
+ "@rsbuild/core": "^1.7.3",
16
+ "@rsbuild/plugin-react": "^1.4.6",
17
+ "@types/node": "^25.4.0",
18
+ "@types/react": "^19.2.14",
19
+ "@types/react-dom": "^19.2.3",
20
+ "minecraft-data": "^3.105.0",
21
+ "typescript": "^5.9.3"
22
+ },
23
+ "dependencies": {
24
+ "@xmcl/text-component": "^2.1.3",
25
+ "react": "^19.2.4",
26
+ "react-dom": "^19.2.4"
27
+ },
28
+ "repository": "https://github.com/zardoy/minecraft-inventory",
29
+ "engines": {
30
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
31
+ },
32
+ "scripts": {
33
+ "dev": "rsbuild dev",
34
+ "build": "rsbuild build",
35
+ "preview": "rsbuild preview"
36
+ }
37
+ }
@@ -0,0 +1,108 @@
1
+ import React, { useCallback, useEffect, useRef } from 'react'
2
+ import type { InventoryConnector } from './connector/types'
3
+ import type { SlotState } from './types'
4
+ import { InventoryProvider } from './context/InventoryContext'
5
+ import { ScaleProvider } from './context/ScaleContext'
6
+ import { TextureProvider } from './context/TextureContext'
7
+ import { InventoryWindow } from './components/InventoryWindow'
8
+ import { CursorItem } from './components/CursorItem'
9
+ import { JEI } from './components/JEI'
10
+ import type { JEIItem } from './components/JEI'
11
+ import type { TextureConfig } from './context/TextureContext'
12
+ import './styles/tokens.css'
13
+
14
+ export interface InventoryGUIProps {
15
+ type: string
16
+ title?: string
17
+ slots?: SlotState[]
18
+ properties?: Record<string, number>
19
+ connector?: InventoryConnector | null
20
+ scale?: number
21
+ showJEI?: boolean
22
+ jeiItems?: JEIItem[]
23
+ jeiPosition?: 'left' | 'right'
24
+ textureBaseUrl?: string
25
+ textureConfig?: Partial<TextureConfig>
26
+ enableKeyboardShortcuts?: boolean
27
+ onClose?: () => void
28
+ className?: string
29
+ style?: React.CSSProperties
30
+ }
31
+
32
+ export function InventoryGUI({
33
+ type,
34
+ title,
35
+ slots,
36
+ properties,
37
+ connector = null,
38
+ scale = 2,
39
+ showJEI = false,
40
+ jeiItems = [],
41
+ jeiPosition = 'right',
42
+ textureBaseUrl,
43
+ textureConfig,
44
+ enableKeyboardShortcuts = true,
45
+ onClose,
46
+ className,
47
+ style,
48
+ }: InventoryGUIProps) {
49
+ const containerRef = useRef<HTMLDivElement>(null)
50
+
51
+ const handleClose = useCallback(
52
+ (e: KeyboardEvent) => {
53
+ if (e.code === 'Escape') onClose?.()
54
+ },
55
+ [onClose],
56
+ )
57
+
58
+ useEffect(() => {
59
+ if (!onClose) return
60
+ window.addEventListener('keydown', handleClose)
61
+ return () => window.removeEventListener('keydown', handleClose)
62
+ }, [handleClose, onClose])
63
+
64
+ return (
65
+ <TextureProvider baseUrl={textureBaseUrl} config={textureConfig}>
66
+ <ScaleProvider scale={scale}>
67
+ <InventoryProvider connector={connector}>
68
+ <div
69
+ ref={containerRef}
70
+ className={className}
71
+ style={{
72
+ display: 'flex',
73
+ alignItems: 'flex-start',
74
+ gap: 4 * scale,
75
+ ...style,
76
+ }}
77
+ >
78
+ {showJEI && jeiPosition === 'left' && (
79
+ <JEI
80
+ items={jeiItems}
81
+ position="left"
82
+ style={{ alignSelf: 'stretch' }}
83
+ />
84
+ )}
85
+
86
+ <InventoryWindow
87
+ type={type}
88
+ title={title}
89
+ slots={slots}
90
+ properties={properties}
91
+ enableKeyboardShortcuts={enableKeyboardShortcuts}
92
+ />
93
+
94
+ {showJEI && jeiPosition === 'right' && (
95
+ <JEI
96
+ items={jeiItems}
97
+ position="right"
98
+ style={{ alignSelf: 'stretch' }}
99
+ />
100
+ )}
101
+ </div>
102
+
103
+ <CursorItem />
104
+ </InventoryProvider>
105
+ </ScaleProvider>
106
+ </TextureProvider>
107
+ )
108
+ }