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.
- package/README.md +566 -0
- package/package.json +37 -0
- package/src/InventoryGUI.tsx +108 -0
- package/src/cache/textureCache.ts +95 -0
- package/src/components/CursorItem/CursorItem.tsx +94 -0
- package/src/components/CursorItem/index.ts +1 -0
- package/src/components/HUD/HUD.tsx +11 -0
- package/src/components/HUD/index.ts +1 -0
- package/src/components/Hotbar/Hotbar.tsx +180 -0
- package/src/components/Hotbar/index.ts +1 -0
- package/src/components/InventoryOverlay/InventoryOverlay.tsx +196 -0
- package/src/components/InventoryOverlay/index.ts +2 -0
- package/src/components/InventoryWindow/EnchantmentOptions.tsx +109 -0
- package/src/components/InventoryWindow/InventoryBackground.tsx +110 -0
- package/src/components/InventoryWindow/InventoryWindow.tsx +120 -0
- package/src/components/InventoryWindow/ProgressBar.tsx +78 -0
- package/src/components/InventoryWindow/VillagerTradeList.tsx +136 -0
- package/src/components/InventoryWindow/index.ts +5 -0
- package/src/components/ItemCanvas/ItemCanvas.tsx +154 -0
- package/src/components/ItemCanvas/index.ts +1 -0
- package/src/components/JEI/JEI.module.css +37 -0
- package/src/components/JEI/JEI.tsx +303 -0
- package/src/components/JEI/index.ts +2 -0
- package/src/components/RecipeGuide/RecipeInventoryView.tsx +293 -0
- package/src/components/RecipeGuide/index.ts +1 -0
- package/src/components/Slot/Slot.module.css +111 -0
- package/src/components/Slot/Slot.tsx +363 -0
- package/src/components/Slot/index.ts +1 -0
- package/src/components/Text/MessageFormatted.css +5 -0
- package/src/components/Text/MessageFormatted.tsx +79 -0
- package/src/components/Text/MessageFormattedString.tsx +74 -0
- package/src/components/Text/chatUtils.ts +172 -0
- package/src/components/Tooltip/Tooltip.module.css +56 -0
- package/src/components/Tooltip/Tooltip.tsx +130 -0
- package/src/components/Tooltip/index.ts +1 -0
- package/src/connector/demo.ts +213 -0
- package/src/connector/index.ts +4 -0
- package/src/connector/mineflayer.ts +113 -0
- package/src/connector/types.ts +41 -0
- package/src/context/InventoryContext.tsx +157 -0
- package/src/context/ScaleContext.tsx +73 -0
- package/src/context/TextureContext.tsx +70 -0
- package/src/globals.d.ts +4 -0
- package/src/hooks/useKeyboardShortcuts.ts +41 -0
- package/src/hooks/useMobile.ts +28 -0
- package/src/index.tsx +65 -0
- package/src/mount.tsx +52 -0
- package/src/registry/index.ts +21 -0
- package/src/registry/inventories.ts +612 -0
- package/src/styles/tokens.css +47 -0
- package/src/types.ts +176 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import React, { createContext, useContext, useEffect, useRef, useState, useCallback } from 'react'
|
|
2
|
+
import type { InventoryWindowState, PlayerState, ItemStack, SlotState } from '../types'
|
|
3
|
+
import type { InventoryConnector } from '../connector/types'
|
|
4
|
+
|
|
5
|
+
export interface InventoryContextValue {
|
|
6
|
+
windowState: InventoryWindowState | null
|
|
7
|
+
playerState: PlayerState | null
|
|
8
|
+
heldItem: ItemStack | null
|
|
9
|
+
setHeldItem: (item: ItemStack | null) => void
|
|
10
|
+
connector: InventoryConnector | null
|
|
11
|
+
sendAction: InventoryConnector['sendAction']
|
|
12
|
+
isDragging: boolean
|
|
13
|
+
dragSlots: number[]
|
|
14
|
+
dragButton: 'left' | 'right' | null
|
|
15
|
+
startDrag: (slotIndex: number, button: 'left' | 'right') => void
|
|
16
|
+
addDragSlot: (slotIndex: number) => void
|
|
17
|
+
endDrag: () => void
|
|
18
|
+
cancelDrag: () => void
|
|
19
|
+
hoveredSlot: number | null
|
|
20
|
+
setHoveredSlot: (slot: number | null) => void
|
|
21
|
+
activeNumberKey: number | null
|
|
22
|
+
setActiveNumberKey: (key: number | null) => void
|
|
23
|
+
getSlot: (index: number) => SlotState | undefined
|
|
24
|
+
/** Pixel offset from the cursor item's top-left to the grab point, preserving pick-up position */
|
|
25
|
+
grabOffset: { x: number; y: number }
|
|
26
|
+
setGrabOffset: (offset: { x: number; y: number }) => void
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const InventoryContext = createContext<InventoryContextValue | null>(null)
|
|
30
|
+
|
|
31
|
+
export function useInventoryContext(): InventoryContextValue {
|
|
32
|
+
const ctx = useContext(InventoryContext)
|
|
33
|
+
if (!ctx) throw new Error('useInventoryContext must be used within InventoryProvider')
|
|
34
|
+
return ctx
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface InventoryProviderProps {
|
|
38
|
+
connector: InventoryConnector | null
|
|
39
|
+
children: React.ReactNode
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function InventoryProvider({ connector, children }: InventoryProviderProps) {
|
|
43
|
+
const [windowState, setWindowState] = useState<InventoryWindowState | null>(
|
|
44
|
+
() => connector?.getWindowState() ?? null,
|
|
45
|
+
)
|
|
46
|
+
const [playerState, setPlayerState] = useState<PlayerState | null>(
|
|
47
|
+
() => connector?.getPlayerState() ?? null,
|
|
48
|
+
)
|
|
49
|
+
const [heldItem, setHeldItemState] = useState<ItemStack | null>(
|
|
50
|
+
() => connector?.getWindowState()?.heldItem ?? null,
|
|
51
|
+
)
|
|
52
|
+
const [hoveredSlot, setHoveredSlot] = useState<number | null>(null)
|
|
53
|
+
const [isDragging, setIsDragging] = useState(false)
|
|
54
|
+
const [dragSlots, setDragSlots] = useState<number[]>([])
|
|
55
|
+
const [dragButton, setDragButton] = useState<'left' | 'right' | null>(null)
|
|
56
|
+
const [activeNumberKey, setActiveNumberKey] = useState<number | null>(null)
|
|
57
|
+
const [grabOffset, setGrabOffset] = useState<{ x: number; y: number }>({ x: 0, y: 0 })
|
|
58
|
+
|
|
59
|
+
const connectorRef = useRef(connector)
|
|
60
|
+
connectorRef.current = connector
|
|
61
|
+
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
if (!connector) return
|
|
64
|
+
setWindowState(connector.getWindowState())
|
|
65
|
+
setPlayerState(connector.getPlayerState())
|
|
66
|
+
|
|
67
|
+
return connector.subscribe((event) => {
|
|
68
|
+
if (event.type === 'windowOpen' || event.type === 'windowUpdate') {
|
|
69
|
+
setWindowState({ ...event.state })
|
|
70
|
+
setHeldItemState(event.state.heldItem)
|
|
71
|
+
} else if (event.type === 'windowClose') {
|
|
72
|
+
setWindowState(null)
|
|
73
|
+
setHeldItemState(null)
|
|
74
|
+
setIsDragging(false)
|
|
75
|
+
setDragSlots([])
|
|
76
|
+
} else if (event.type === 'playerUpdate') {
|
|
77
|
+
setPlayerState(event.state)
|
|
78
|
+
} else if (event.type === 'heldItemChange') {
|
|
79
|
+
setHeldItemState(event.item)
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
}, [connector])
|
|
83
|
+
|
|
84
|
+
const setHeldItem = useCallback((item: ItemStack | null) => {
|
|
85
|
+
setHeldItemState(item)
|
|
86
|
+
}, [])
|
|
87
|
+
|
|
88
|
+
const sendAction = useCallback<InventoryConnector['sendAction']>(
|
|
89
|
+
(action) => {
|
|
90
|
+
return connectorRef.current?.sendAction(action)
|
|
91
|
+
},
|
|
92
|
+
[],
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
const startDrag = useCallback((slotIndex: number, button: 'left' | 'right') => {
|
|
96
|
+
setIsDragging(true)
|
|
97
|
+
setDragButton(button)
|
|
98
|
+
setDragSlots([slotIndex])
|
|
99
|
+
connectorRef.current?.sendAction({ type: 'drag', slots: [], button })
|
|
100
|
+
}, [])
|
|
101
|
+
|
|
102
|
+
const addDragSlot = useCallback((slotIndex: number) => {
|
|
103
|
+
setDragSlots((prev) => {
|
|
104
|
+
if (prev.includes(slotIndex)) return prev
|
|
105
|
+
return [...prev, slotIndex]
|
|
106
|
+
})
|
|
107
|
+
}, [])
|
|
108
|
+
|
|
109
|
+
const endDrag = useCallback(() => {
|
|
110
|
+
setDragSlots((slots) => {
|
|
111
|
+
if (slots.length > 0 && dragButton) {
|
|
112
|
+
connectorRef.current?.sendAction({ type: 'drag', slots, button: dragButton })
|
|
113
|
+
}
|
|
114
|
+
return []
|
|
115
|
+
})
|
|
116
|
+
setIsDragging(false)
|
|
117
|
+
setDragButton(null)
|
|
118
|
+
}, [dragButton])
|
|
119
|
+
|
|
120
|
+
const cancelDrag = useCallback(() => {
|
|
121
|
+
setIsDragging(false)
|
|
122
|
+
setDragSlots([])
|
|
123
|
+
setDragButton(null)
|
|
124
|
+
}, [])
|
|
125
|
+
|
|
126
|
+
const getSlot = useCallback(
|
|
127
|
+
(index: number) => {
|
|
128
|
+
return windowState?.slots.find((s) => s.index === index)
|
|
129
|
+
},
|
|
130
|
+
[windowState],
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
const value: InventoryContextValue = {
|
|
134
|
+
windowState,
|
|
135
|
+
playerState,
|
|
136
|
+
heldItem,
|
|
137
|
+
setHeldItem,
|
|
138
|
+
connector,
|
|
139
|
+
sendAction,
|
|
140
|
+
isDragging,
|
|
141
|
+
dragSlots,
|
|
142
|
+
dragButton,
|
|
143
|
+
startDrag,
|
|
144
|
+
addDragSlot,
|
|
145
|
+
endDrag,
|
|
146
|
+
cancelDrag,
|
|
147
|
+
hoveredSlot,
|
|
148
|
+
setHoveredSlot,
|
|
149
|
+
activeNumberKey,
|
|
150
|
+
setActiveNumberKey,
|
|
151
|
+
getSlot,
|
|
152
|
+
grabOffset,
|
|
153
|
+
setGrabOffset,
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return <InventoryContext.Provider value={value}>{children}</InventoryContext.Provider>
|
|
157
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import React, { createContext, useContext, useMemo } from 'react'
|
|
2
|
+
|
|
3
|
+
export interface ScaleContextValue {
|
|
4
|
+
scale: number
|
|
5
|
+
/** Full grid cell size: 18 × scale. Used for layout/positioning. */
|
|
6
|
+
slotSize: number
|
|
7
|
+
/** Item content area size: slotSize - 2 × borderPx. This is the slot div size. */
|
|
8
|
+
contentSize: number
|
|
9
|
+
/** 1 unscaled pixel, Math.max(1, Math.round(scale)). Offset from grid cell to content area. */
|
|
10
|
+
borderPx: number
|
|
11
|
+
fontSize: number
|
|
12
|
+
borderRadius: number
|
|
13
|
+
pixelSize: number
|
|
14
|
+
getCSSVars(): React.CSSProperties
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const BASE_SLOT_SIZE = 18
|
|
18
|
+
|
|
19
|
+
const ScaleContext = createContext<ScaleContextValue>({
|
|
20
|
+
scale: 2,
|
|
21
|
+
slotSize: BASE_SLOT_SIZE * 2,
|
|
22
|
+
contentSize: BASE_SLOT_SIZE * 2 - 4,
|
|
23
|
+
borderPx: 2,
|
|
24
|
+
fontSize: 12,
|
|
25
|
+
borderRadius: 0,
|
|
26
|
+
pixelSize: 2,
|
|
27
|
+
getCSSVars: () => ({}),
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
export function useScale(): ScaleContextValue {
|
|
31
|
+
return useContext(ScaleContext)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface ScaleProviderProps {
|
|
35
|
+
scale?: number
|
|
36
|
+
children: React.ReactNode
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function ScaleProvider({ scale = 2, children }: ScaleProviderProps) {
|
|
40
|
+
const value = useMemo<ScaleContextValue>(() => {
|
|
41
|
+
const slotSize = BASE_SLOT_SIZE * scale
|
|
42
|
+
const fontSize = 7 * scale
|
|
43
|
+
const pixelSize = scale
|
|
44
|
+
const borderPx = Math.max(1, Math.round(scale))
|
|
45
|
+
const contentSize = slotSize - 2 * borderPx
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
scale,
|
|
49
|
+
slotSize,
|
|
50
|
+
contentSize,
|
|
51
|
+
borderPx,
|
|
52
|
+
fontSize,
|
|
53
|
+
borderRadius: 0,
|
|
54
|
+
pixelSize,
|
|
55
|
+
getCSSVars: () => ({
|
|
56
|
+
'--mc-scale': scale,
|
|
57
|
+
'--mc-slot-size': `${slotSize}px`,
|
|
58
|
+
'--mc-font-size': `${fontSize}px`,
|
|
59
|
+
'--mc-pixel': `${pixelSize}px`,
|
|
60
|
+
'--mc-border': `${borderPx}px`,
|
|
61
|
+
'--mc-gap': `${2 * pixelSize}px`,
|
|
62
|
+
'--mc-padding': `${4 * pixelSize}px`,
|
|
63
|
+
'--mc-count-font': `${Math.round(6 * scale)}px`,
|
|
64
|
+
} as React.CSSProperties),
|
|
65
|
+
}
|
|
66
|
+
}, [scale])
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<ScaleContext.Provider value={value}>
|
|
70
|
+
<div style={value.getCSSVars()}>{children}</div>
|
|
71
|
+
</ScaleContext.Provider>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import React, { createContext, useContext } from 'react'
|
|
2
|
+
|
|
3
|
+
// Default: 1.16.4 for GUI textures (most containers existed here; 1.21.4 for items)
|
|
4
|
+
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'
|
|
8
|
+
|
|
9
|
+
export interface TextureConfig {
|
|
10
|
+
baseUrl: string
|
|
11
|
+
getItemTextureUrl(item: { type: number; name?: string }): string
|
|
12
|
+
getBlockTextureUrl(item: { type: number; name?: string }): string
|
|
13
|
+
/** version defaults to '1.16.4' for GUI textures unless overridden per-container */
|
|
14
|
+
getGuiTextureUrl(path: string, version?: string): string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function buildDefault(base: string): TextureConfig {
|
|
18
|
+
const isRemote = base.startsWith('http')
|
|
19
|
+
// For remote default: use the same base for items, separate versioned base for GUI
|
|
20
|
+
const guiBase = isRemote ? MC_GUI_BASE : base
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
baseUrl: base,
|
|
24
|
+
|
|
25
|
+
getItemTextureUrl({ type, name }) {
|
|
26
|
+
const root = isRemote ? MC_ITEMS_BASE : base
|
|
27
|
+
if (name) return `${root}/items/${name}.png`
|
|
28
|
+
return `${root}/items/${type}.png`
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
getBlockTextureUrl({ type, name }) {
|
|
32
|
+
const root = isRemote ? MC_ITEMS_BASE : base
|
|
33
|
+
if (name) return `${root}/blocks/${name}.png`
|
|
34
|
+
return `${root}/blocks/${type}.png`
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
getGuiTextureUrl(path: string, version = '1.16.4') {
|
|
38
|
+
if (isRemote) {
|
|
39
|
+
return `${guiBase}/${version}/${path}.png`
|
|
40
|
+
}
|
|
41
|
+
return `${base}/textures/${path}.png`
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const defaultTextureConfig = buildDefault(MC_ITEMS_BASE)
|
|
47
|
+
|
|
48
|
+
const TextureContext = createContext<TextureConfig>(defaultTextureConfig)
|
|
49
|
+
|
|
50
|
+
export function useTextures(): TextureConfig {
|
|
51
|
+
return useContext(TextureContext)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
interface TextureProviderProps {
|
|
55
|
+
config?: Partial<TextureConfig>
|
|
56
|
+
baseUrl?: string
|
|
57
|
+
children: React.ReactNode
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function TextureProvider({ config, baseUrl, children }: TextureProviderProps) {
|
|
61
|
+
const base = baseUrl ?? config?.baseUrl ?? MC_ITEMS_BASE
|
|
62
|
+
const merged: TextureConfig = {
|
|
63
|
+
...buildDefault(base),
|
|
64
|
+
...config,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return <TextureContext.Provider value={merged}>{children}</TextureContext.Provider>
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export { defaultTextureConfig }
|
package/src/globals.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { useEffect } from 'react'
|
|
2
|
+
import { useInventoryContext } from '../context/InventoryContext'
|
|
3
|
+
|
|
4
|
+
export function useKeyboardShortcuts(enabled = true) {
|
|
5
|
+
const { setActiveNumberKey, hoveredSlot, sendAction } = useInventoryContext()
|
|
6
|
+
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
if (!enabled) return
|
|
9
|
+
|
|
10
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
11
|
+
// Digit1–Digit9 → hotbar swap (works regardless of keyboard layout)
|
|
12
|
+
if (e.code >= 'Digit1' && e.code <= 'Digit9') {
|
|
13
|
+
setActiveNumberKey(parseInt(e.code.replace('Digit', ''), 10) - 1)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Q to drop
|
|
17
|
+
if (e.code === 'KeyQ') {
|
|
18
|
+
if (hoveredSlot !== null) {
|
|
19
|
+
sendAction({
|
|
20
|
+
type: 'drop',
|
|
21
|
+
slotIndex: hoveredSlot,
|
|
22
|
+
all: e.ctrlKey || e.metaKey,
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const handleKeyUp = (e: KeyboardEvent) => {
|
|
29
|
+
if (e.code >= 'Digit1' && e.code <= 'Digit9') {
|
|
30
|
+
setActiveNumberKey(null)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
window.addEventListener('keydown', handleKeyDown)
|
|
35
|
+
window.addEventListener('keyup', handleKeyUp)
|
|
36
|
+
return () => {
|
|
37
|
+
window.removeEventListener('keydown', handleKeyDown)
|
|
38
|
+
window.removeEventListener('keyup', handleKeyUp)
|
|
39
|
+
}
|
|
40
|
+
}, [enabled, hoveredSlot, sendAction, setActiveNumberKey])
|
|
41
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react'
|
|
2
|
+
|
|
3
|
+
let cachedIsMobile: boolean | null = null
|
|
4
|
+
|
|
5
|
+
function detectMobile(): boolean {
|
|
6
|
+
if (typeof window === 'undefined') return false
|
|
7
|
+
return (
|
|
8
|
+
'ontouchstart' in window ||
|
|
9
|
+
navigator.maxTouchPoints > 0 ||
|
|
10
|
+
window.matchMedia('(pointer: coarse)').matches
|
|
11
|
+
)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function useMobile(): boolean {
|
|
15
|
+
const [isMobile, setIsMobile] = useState<boolean>(() => {
|
|
16
|
+
if (cachedIsMobile !== null) return cachedIsMobile
|
|
17
|
+
return detectMobile()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
const mq = window.matchMedia('(pointer: coarse)')
|
|
22
|
+
const update = (e: MediaQueryListEvent) => setIsMobile(e.matches)
|
|
23
|
+
mq.addEventListener('change', update)
|
|
24
|
+
return () => mq.removeEventListener('change', update)
|
|
25
|
+
}, [])
|
|
26
|
+
|
|
27
|
+
return isMobile
|
|
28
|
+
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export { InventoryGUI } from './InventoryGUI'
|
|
2
|
+
export type { InventoryGUIProps } from './InventoryGUI'
|
|
3
|
+
|
|
4
|
+
// Contexts & Providers
|
|
5
|
+
export { InventoryProvider, useInventoryContext } from './context/InventoryContext'
|
|
6
|
+
export { ScaleProvider, useScale } from './context/ScaleContext'
|
|
7
|
+
export { TextureProvider, useTextures, defaultTextureConfig } from './context/TextureContext'
|
|
8
|
+
|
|
9
|
+
// Components
|
|
10
|
+
export { InventoryWindow } from './components/InventoryWindow'
|
|
11
|
+
export { InventoryBackground } from './components/InventoryWindow'
|
|
12
|
+
export { Slot } from './components/Slot'
|
|
13
|
+
export { ItemCanvas } from './components/ItemCanvas'
|
|
14
|
+
export { Tooltip } from './components/Tooltip'
|
|
15
|
+
export { JEI } from './components/JEI'
|
|
16
|
+
export type { JEIItem } from './components/JEI'
|
|
17
|
+
export { Hotbar } from './components/Hotbar'
|
|
18
|
+
export { HUD } from './components/HUD'
|
|
19
|
+
export { CursorItem } from './components/CursorItem'
|
|
20
|
+
export { InventoryOverlay } from './components/InventoryOverlay'
|
|
21
|
+
export type { InventoryOverlayProps } from './components/InventoryOverlay'
|
|
22
|
+
export { RecipeInventoryView } from './components/RecipeGuide'
|
|
23
|
+
|
|
24
|
+
// Text components
|
|
25
|
+
export { MessageFormattedString } from './components/Text/MessageFormattedString'
|
|
26
|
+
export { default as MessageFormatted, MessagePart } from './components/Text/MessageFormatted'
|
|
27
|
+
|
|
28
|
+
// Programmatic mount (no React needed)
|
|
29
|
+
export { mountInventory } from './mount'
|
|
30
|
+
export type { MountedInventory } from './mount'
|
|
31
|
+
|
|
32
|
+
// Hooks
|
|
33
|
+
export { useMobile } from './hooks/useMobile'
|
|
34
|
+
export { useKeyboardShortcuts } from './hooks/useKeyboardShortcuts'
|
|
35
|
+
|
|
36
|
+
// Connector
|
|
37
|
+
export { createMineflayerConnector } from './connector/mineflayer'
|
|
38
|
+
export { createDemoConnector } from './connector/demo'
|
|
39
|
+
export type {
|
|
40
|
+
InventoryConnector,
|
|
41
|
+
ConnectorEvent,
|
|
42
|
+
ConnectorListener,
|
|
43
|
+
MineflayerBot,
|
|
44
|
+
} from './connector/types'
|
|
45
|
+
export type { ActionLogEntry, DemoConnectorOptions } from './connector/demo'
|
|
46
|
+
|
|
47
|
+
// Registry
|
|
48
|
+
export { registerInventoryType, getInventoryType, getAllInventoryTypes } from './registry'
|
|
49
|
+
export type { InventoryTypeDefinition } from './registry'
|
|
50
|
+
|
|
51
|
+
// Types
|
|
52
|
+
export type {
|
|
53
|
+
ItemStack,
|
|
54
|
+
SlotState,
|
|
55
|
+
SlotDefinition,
|
|
56
|
+
ProgressBar,
|
|
57
|
+
InventoryWindowState,
|
|
58
|
+
PlayerState,
|
|
59
|
+
TradeOffer,
|
|
60
|
+
InventoryAction,
|
|
61
|
+
MouseButton,
|
|
62
|
+
ClickMode,
|
|
63
|
+
RecipeGuide,
|
|
64
|
+
RecipeNavFrame,
|
|
65
|
+
} from './types'
|
package/src/mount.tsx
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { createRoot, type Root } from 'react-dom/client'
|
|
3
|
+
import { InventoryGUI, type InventoryGUIProps } from './InventoryGUI'
|
|
4
|
+
|
|
5
|
+
export interface MountedInventory {
|
|
6
|
+
update(props: Partial<InventoryGUIProps>): void
|
|
7
|
+
destroy(): void
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Mount an inventory GUI into a DOM element without writing React code.
|
|
12
|
+
*
|
|
13
|
+
* ```js
|
|
14
|
+
* import { mountInventory, createDemoConnector } from 'minecraft-inventory'
|
|
15
|
+
*
|
|
16
|
+
* const connector = createDemoConnector({ windowType: 'chest', slots: [] })
|
|
17
|
+
* const inv = mountInventory(document.getElementById('root'), {
|
|
18
|
+
* type: 'chest',
|
|
19
|
+
* connector,
|
|
20
|
+
* scale: 2,
|
|
21
|
+
* })
|
|
22
|
+
*
|
|
23
|
+
* // Later: update props
|
|
24
|
+
* inv.update({ scale: 3 })
|
|
25
|
+
*
|
|
26
|
+
* // Cleanup
|
|
27
|
+
* inv.destroy()
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export function mountInventory(
|
|
31
|
+
container: HTMLElement,
|
|
32
|
+
initialProps: InventoryGUIProps,
|
|
33
|
+
): MountedInventory {
|
|
34
|
+
let currentProps = { ...initialProps }
|
|
35
|
+
const root: Root = createRoot(container)
|
|
36
|
+
|
|
37
|
+
function render() {
|
|
38
|
+
root.render(<InventoryGUI {...currentProps} />)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
render()
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
update(props: Partial<InventoryGUIProps>) {
|
|
45
|
+
currentProps = { ...currentProps, ...props }
|
|
46
|
+
render()
|
|
47
|
+
},
|
|
48
|
+
destroy() {
|
|
49
|
+
root.unmount()
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { InventoryTypeDefinition } from '../types'
|
|
2
|
+
import { inventoryDefinitions } from './inventories'
|
|
3
|
+
|
|
4
|
+
const registry = new Map<string, InventoryTypeDefinition>(
|
|
5
|
+
Object.entries(inventoryDefinitions),
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
export function registerInventoryType(def: InventoryTypeDefinition): void {
|
|
9
|
+
registry.set(def.name, def)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function getInventoryType(name: string): InventoryTypeDefinition | undefined {
|
|
13
|
+
return registry.get(name)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function getAllInventoryTypes(): InventoryTypeDefinition[] {
|
|
17
|
+
return Array.from(registry.values())
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { inventoryDefinitions }
|
|
21
|
+
export type { InventoryTypeDefinition }
|