minecraft-inventory 0.1.19 → 0.1.20
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/package.json +1 -1
- package/src/InventoryGUI.tsx +3 -0
- package/src/components/InventoryOverlay/InventoryOverlay.tsx +9 -3
- package/src/components/InventoryWindow/EnchantmentOptions.tsx +24 -5
- package/src/components/InventoryWindow/InventoryWindow.tsx +2 -2
- package/src/components/JEI/JEI.tsx +6 -6
- package/src/components/JEI/StarIcon.tsx +31 -0
- package/src/components/Notes/Notes.tsx +20 -6
- package/src/components/Slot/Slot.tsx +7 -1
- package/src/context/InventoryContext.tsx +17 -13
- package/src/hooks/useCloseOnWClick.ts +55 -0
- package/src/registry/inventories.ts +4 -1
package/package.json
CHANGED
package/src/InventoryGUI.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { useCallback, useEffect, useRef } from 'react'
|
|
2
|
+
import { useCloseOnWClick } from './hooks/useCloseOnWClick'
|
|
2
3
|
import type { InventoryConnector } from './connector/types'
|
|
3
4
|
import type { SlotState } from './types'
|
|
4
5
|
import { InventoryProvider } from './context/InventoryContext'
|
|
@@ -51,6 +52,8 @@ export function InventoryGUI({
|
|
|
51
52
|
}: InventoryGUIProps) {
|
|
52
53
|
const containerRef = useRef<HTMLDivElement>(null)
|
|
53
54
|
|
|
55
|
+
useCloseOnWClick(onClose)
|
|
56
|
+
|
|
54
57
|
const handleClose = useCallback(
|
|
55
58
|
(e: KeyboardEvent) => {
|
|
56
59
|
if (e.code === 'Escape') onClose?.()
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { useCallback, useState } from 'react'
|
|
2
|
+
import { useCloseOnWClick } from '../../hooks/useCloseOnWClick'
|
|
2
3
|
import { useInventoryContext } from '../../context/InventoryContext'
|
|
3
4
|
import { useScale } from '../../context/ScaleContext'
|
|
4
5
|
import { getInventoryType } from '../../registry'
|
|
@@ -93,6 +94,8 @@ export function InventoryOverlay({
|
|
|
93
94
|
const { heldItem, sendAction, setHeldItem, focusedSlot, setFocusedSlot } = useInventoryContext()
|
|
94
95
|
const { scale } = useScale()
|
|
95
96
|
|
|
97
|
+
useCloseOnWClick(onClose)
|
|
98
|
+
|
|
96
99
|
const def = getInventoryType(type)
|
|
97
100
|
const invUnscaledW = def?.backgroundWidth ?? 176
|
|
98
101
|
const sideGapPx = 5 * scale // gap from overlay edge and from inventory edge
|
|
@@ -196,7 +199,6 @@ export function InventoryOverlay({
|
|
|
196
199
|
{(leftPanel || enableNotes || (showJEI && jeiPosition === 'left')) && (
|
|
197
200
|
<div
|
|
198
201
|
className="mc-inv-overlay-side mc-inv-overlay-side-left"
|
|
199
|
-
onClick={(e) => e.stopPropagation()}
|
|
200
202
|
style={{ ...sidePanelBase, left: sideGapPx, pointerEvents: 'auto' }}
|
|
201
203
|
>
|
|
202
204
|
{enableNotes && (
|
|
@@ -205,13 +207,17 @@ export function InventoryOverlay({
|
|
|
205
207
|
</div>
|
|
206
208
|
)}
|
|
207
209
|
{leftPanel && (
|
|
208
|
-
<div
|
|
210
|
+
<div
|
|
211
|
+
onClick={(e) => e.stopPropagation()}
|
|
212
|
+
style={{ flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column' }}
|
|
213
|
+
>
|
|
209
214
|
{leftPanel}
|
|
210
215
|
</div>
|
|
211
216
|
)}
|
|
212
217
|
{showJEI && jeiPosition === 'left' && (
|
|
213
218
|
<div
|
|
214
219
|
className="mc-inv-overlay-jei mc-inv-overlay-jei-left"
|
|
220
|
+
onClick={(e) => e.stopPropagation()}
|
|
215
221
|
style={{ flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column', width: '100%' }}
|
|
216
222
|
>
|
|
217
223
|
{jeiPanel}
|
|
@@ -304,7 +310,7 @@ export function InventoryOverlay({
|
|
|
304
310
|
lineHeight: 1,
|
|
305
311
|
}}
|
|
306
312
|
>
|
|
307
|
-
INV 0.1.
|
|
313
|
+
INV 0.1.20
|
|
308
314
|
</a>
|
|
309
315
|
)}
|
|
310
316
|
|
|
@@ -42,6 +42,23 @@ const ENCHANTMENT_NAMES: Record<number, string> = {
|
|
|
42
42
|
74: 'Vanishing Curse',
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
const ROMAN_NUMERALS: [number, string][] = [
|
|
46
|
+
[10, 'X'], [9, 'IX'], [5, 'V'], [4, 'IV'], [1, 'I'],
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
function toRoman(num: number): string {
|
|
50
|
+
if (num <= 0) return ''
|
|
51
|
+
let result = ''
|
|
52
|
+
let remaining = num
|
|
53
|
+
for (const [value, symbol] of ROMAN_NUMERALS) {
|
|
54
|
+
while (remaining >= value) {
|
|
55
|
+
result += symbol
|
|
56
|
+
remaining -= value
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return result
|
|
60
|
+
}
|
|
61
|
+
|
|
45
62
|
interface EnchantmentOptionsProps {
|
|
46
63
|
properties: Record<string, number>
|
|
47
64
|
x: number
|
|
@@ -49,9 +66,9 @@ interface EnchantmentOptionsProps {
|
|
|
49
66
|
}
|
|
50
67
|
|
|
51
68
|
const OPTION_KEYS = [
|
|
52
|
-
{ levelKey: 'topEnchantLevel', idKey: 'topEnchantId', slot: 0 },
|
|
53
|
-
{ levelKey: 'middleEnchantLevel', idKey: 'middleEnchantId', slot: 1 },
|
|
54
|
-
{ levelKey: 'bottomEnchantLevel', idKey: 'bottomEnchantId', slot: 2 },
|
|
69
|
+
{ levelKey: 'topEnchantLevel', idKey: 'topEnchantId', clueKey: 'topLevelClue', slot: 0 },
|
|
70
|
+
{ levelKey: 'middleEnchantLevel', idKey: 'middleEnchantId', clueKey: 'middleLevelClue', slot: 1 },
|
|
71
|
+
{ levelKey: 'bottomEnchantLevel', idKey: 'bottomEnchantId', clueKey: 'bottomLevelClue', slot: 2 },
|
|
55
72
|
]
|
|
56
73
|
|
|
57
74
|
export function EnchantmentOptions({ properties, x, y }: EnchantmentOptionsProps) {
|
|
@@ -69,11 +86,13 @@ export function EnchantmentOptions({ properties, x, y }: EnchantmentOptionsProps
|
|
|
69
86
|
gap: 0,
|
|
70
87
|
}}
|
|
71
88
|
>
|
|
72
|
-
{OPTION_KEYS.map(({ levelKey, idKey, slot }, i) => {
|
|
89
|
+
{OPTION_KEYS.map(({ levelKey, idKey, clueKey, slot }, i) => {
|
|
73
90
|
const level = properties[levelKey] ?? 0
|
|
74
91
|
const enchId = properties[idKey] ?? -1
|
|
92
|
+
const levelClue = properties[clueKey] ?? 0
|
|
75
93
|
const name = ENCHANTMENT_NAMES[enchId] ?? (enchId >= 0 ? `Enchant #${enchId}` : '???')
|
|
76
94
|
const disabled = level <= 0
|
|
95
|
+
const levelSuffix = levelClue > 0 ? ` ${toRoman(levelClue)}` : ''
|
|
77
96
|
|
|
78
97
|
return (
|
|
79
98
|
<div
|
|
@@ -99,7 +118,7 @@ export function EnchantmentOptions({ properties, x, y }: EnchantmentOptionsProps
|
|
|
99
118
|
{level}
|
|
100
119
|
</span>
|
|
101
120
|
<span style={{ flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
|
|
102
|
-
{disabled ? '' : name}
|
|
121
|
+
{disabled ? '' : `${name}${levelSuffix}`}
|
|
103
122
|
</span>
|
|
104
123
|
</div>
|
|
105
124
|
)
|
|
@@ -68,9 +68,9 @@ export function InventoryWindow({
|
|
|
68
68
|
// Player inventory: don't show title (it's just the player's own inventory)
|
|
69
69
|
const effectiveTitle = type === 'player' ? undefined : (title ?? windowState?.title ?? def.title)
|
|
70
70
|
const isVillager = type === 'villager'
|
|
71
|
-
const isEnchanting =
|
|
71
|
+
const isEnchanting = def?.name === 'enchanting_table'
|
|
72
72
|
const isHotbar = type === 'hotbar'
|
|
73
|
-
const isAnvil =
|
|
73
|
+
const isAnvil = def?.name === 'anvil'
|
|
74
74
|
|
|
75
75
|
const resolveItem = (slotIndex: number) => {
|
|
76
76
|
const fromProp = effectiveSlots.find((s) => s.index === slotIndex)
|
|
@@ -3,6 +3,7 @@ import type { ItemStack, RecipeGuide, RecipeNavFrame, BlockTextureRender } from
|
|
|
3
3
|
import { useScale } from '../../context/ScaleContext'
|
|
4
4
|
import { useInventoryContext } from '../../context/InventoryContext'
|
|
5
5
|
import { Slot } from '../Slot'
|
|
6
|
+
import { StarIcon } from './StarIcon'
|
|
6
7
|
import styles from './JEI.module.css'
|
|
7
8
|
|
|
8
9
|
export interface JEIItem {
|
|
@@ -303,7 +304,7 @@ export function JEI({
|
|
|
303
304
|
style={{ fontSize: 6 * scale, padding: `${scale}px ${2 * scale}px` }}
|
|
304
305
|
title={showFavorites ? 'Show all items' : 'Show favorites (F to toggle)'}
|
|
305
306
|
>
|
|
306
|
-
|
|
307
|
+
<StarIcon size={Math.max(1, Math.round(6 * scale))} />
|
|
307
308
|
</button>
|
|
308
309
|
</div>
|
|
309
310
|
</div>
|
|
@@ -371,14 +372,13 @@ export function JEI({
|
|
|
371
372
|
position: 'absolute',
|
|
372
373
|
top: 0,
|
|
373
374
|
right: 0,
|
|
374
|
-
|
|
375
|
-
color: '#ffcc00',
|
|
376
|
-
lineHeight: 1,
|
|
375
|
+
lineHeight: 0,
|
|
377
376
|
pointerEvents: 'none',
|
|
378
|
-
|
|
377
|
+
color: '#ffd700',
|
|
378
|
+
filter: `drop-shadow(0 0 ${Math.max(1, scale)}px rgba(0,0,0,0.85))`,
|
|
379
379
|
}}
|
|
380
380
|
>
|
|
381
|
-
|
|
381
|
+
<StarIcon size={Math.max(1, Math.round(6 * scale))} />
|
|
382
382
|
</span>
|
|
383
383
|
)}
|
|
384
384
|
</div>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
/** Pixel-art star (18×18 viewBox), same appearance on all platforms */
|
|
4
|
+
const STAR_PATH =
|
|
5
|
+
'M15.4375 15.1238L15.625 15.3113V16.8113L15.4375 16.9988H14.6875L14.5 16.8113V16.4363L14.3125 16.2488H12.8125L12.625 16.0613V15.3113L12.4375 15.1238H10.9375L10.75 14.9363V14.1863L10.5625 13.9988H7.9375L7.75 14.1863V14.9363L7.5625 15.1238H6.0625L5.875 15.3113V16.0613L5.6875 16.2488H4.1875L4 16.4363V16.8113L3.8125 16.9988H3.0625L2.875 16.8113V15.3113L3.0625 15.1238H3.4375L3.625 14.9363V13.8113L3.8125 13.6238H4.1875L4.375 13.4363V11.9363L4.5625 11.7488H4.9375L5.125 11.5613V10.0613L4.9375 9.87384H4.1875L4 9.68634V8.93634L3.8125 8.74884H2.6875L2.5 8.56134V7.81134L2.3125 7.62384H1.1875L1 7.43634V6.68634L1.1875 6.49884H6.63738L6.82488 6.31134V5.61543V4.60586L7.01238 4.41836H7.5625L7.75 4.23086V2.6875L7.9375 2.5H8.43022L8.61772 2.3125V1.1875L8.80522 1H9.66735L9.85485 1.1875V2.3125L10.0424 2.5H10.5625L10.75 2.6875V4.23086L10.9375 4.41836H11.4375L11.625 4.60586V6.31134L11.8125 6.49884H17.3125L17.5 6.68634V7.43634L17.3125 7.62384H16.1875L16 7.81134V8.56134L15.8125 8.74884H14.6875L14.5 8.93634V9.68634L14.3125 9.87384H13.5625L13.375 10.0613V11.5613L13.5625 11.7488H13.9375L14.125 11.9363V13.4363L14.3125 13.6238H14.6875L14.875 13.8113V14.9363L15.0625 15.1238H15.4375Z'
|
|
6
|
+
|
|
7
|
+
export interface StarIconProps {
|
|
8
|
+
size: number
|
|
9
|
+
className?: string
|
|
10
|
+
style?: React.CSSProperties
|
|
11
|
+
title?: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function StarIcon({ size, className, style, title }: StarIconProps) {
|
|
15
|
+
return (
|
|
16
|
+
<svg
|
|
17
|
+
width={size}
|
|
18
|
+
height={size}
|
|
19
|
+
viewBox="0 0 18 18"
|
|
20
|
+
fill="none"
|
|
21
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
22
|
+
className={className}
|
|
23
|
+
style={{ display: 'block', flexShrink: 0, ...style }}
|
|
24
|
+
aria-hidden={title ? undefined : true}
|
|
25
|
+
role={title ? 'img' : undefined}
|
|
26
|
+
>
|
|
27
|
+
{title ? <title>{title}</title> : null}
|
|
28
|
+
<path d={STAR_PATH} fill="currentColor" />
|
|
29
|
+
</svg>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
@@ -134,14 +134,14 @@ export function Notes({
|
|
|
134
134
|
...style,
|
|
135
135
|
}}
|
|
136
136
|
>
|
|
137
|
-
<div style={{
|
|
137
|
+
{/* <div style={{
|
|
138
138
|
padding: `${3 * scale}px ${4 * scale}px`,
|
|
139
139
|
color: '#404040',
|
|
140
140
|
fontWeight: 'bold',
|
|
141
141
|
flexShrink: 0,
|
|
142
142
|
}}>
|
|
143
143
|
Current Tasks
|
|
144
|
-
</div>
|
|
144
|
+
</div> */}
|
|
145
145
|
|
|
146
146
|
{/* Task list */}
|
|
147
147
|
<div style={{ overflowY: 'auto', padding: `${2 * scale}px`, minHeight: 0 }}>
|
|
@@ -162,11 +162,15 @@ export function Notes({
|
|
|
162
162
|
marginBottom: 2 * scale,
|
|
163
163
|
padding: `${2 * scale}px`,
|
|
164
164
|
}}>
|
|
165
|
-
<span style={{ flex: 1, color: '#
|
|
165
|
+
<span style={{ flex: 1, color: '#d6d6d6', wordBreak: 'break-word', lineHeight: 1.3 }}>
|
|
166
166
|
{note.text}
|
|
167
167
|
</span>
|
|
168
168
|
<button
|
|
169
|
-
|
|
169
|
+
type="button"
|
|
170
|
+
onClick={(e) => {
|
|
171
|
+
e.stopPropagation()
|
|
172
|
+
removeNote(note.id)
|
|
173
|
+
}}
|
|
170
174
|
style={{
|
|
171
175
|
background: '#8b0000',
|
|
172
176
|
color: '#fff',
|
|
@@ -201,6 +205,7 @@ export function Notes({
|
|
|
201
205
|
value={draft}
|
|
202
206
|
rows={2}
|
|
203
207
|
autoFocus
|
|
208
|
+
onClick={(e) => e.stopPropagation()}
|
|
204
209
|
onChange={(e) => {
|
|
205
210
|
setDraft(e.target.value.replace(/[\r\n]/g, ''))
|
|
206
211
|
}}
|
|
@@ -229,7 +234,12 @@ export function Notes({
|
|
|
229
234
|
<div style={{ display: 'flex', flexDirection: 'column', gap: scale, flexShrink: 0 }}>
|
|
230
235
|
{showInput && (
|
|
231
236
|
<button
|
|
232
|
-
|
|
237
|
+
type="button"
|
|
238
|
+
onClick={(e) => {
|
|
239
|
+
e.stopPropagation()
|
|
240
|
+
setShowInput(false)
|
|
241
|
+
setDraft('')
|
|
242
|
+
}}
|
|
233
243
|
title="Hide"
|
|
234
244
|
style={{
|
|
235
245
|
background: '#5a3a3a',
|
|
@@ -246,7 +256,11 @@ export function Notes({
|
|
|
246
256
|
</button>
|
|
247
257
|
)}
|
|
248
258
|
<button
|
|
249
|
-
|
|
259
|
+
type="button"
|
|
260
|
+
onClick={(e) => {
|
|
261
|
+
e.stopPropagation()
|
|
262
|
+
handleAddButtonClick()
|
|
263
|
+
}}
|
|
250
264
|
disabled={showInput && !draft.trim()}
|
|
251
265
|
style={{
|
|
252
266
|
background: '#404040',
|
|
@@ -272,6 +272,12 @@ export function Slot({
|
|
|
272
272
|
|
|
273
273
|
if (pKeyActive) setPKeyActive(false)
|
|
274
274
|
|
|
275
|
+
// JEI / recipe / custom slots: same handler as desktop onMouseUp (not focus/swap).
|
|
276
|
+
if (onClickOverride) {
|
|
277
|
+
onClickOverride('left', 'normal')
|
|
278
|
+
return
|
|
279
|
+
}
|
|
280
|
+
|
|
275
281
|
if (heldItem) {
|
|
276
282
|
// When holding an item, place it (standard behavior, no focus needed)
|
|
277
283
|
sendAction({ type: 'click', slotIndex: index, button: 'left', mode: 'normal' })
|
|
@@ -291,7 +297,7 @@ export function Slot({
|
|
|
291
297
|
setFocusedSlot(null)
|
|
292
298
|
}
|
|
293
299
|
},
|
|
294
|
-
[isMobile, disabled, heldItem, sendAction, index, pKeyActive, setPKeyActive, focusedSlot, setFocusedSlot],
|
|
300
|
+
[isMobile, disabled, heldItem, sendAction, index, pKeyActive, setPKeyActive, focusedSlot, setFocusedSlot, onClickOverride],
|
|
295
301
|
)
|
|
296
302
|
|
|
297
303
|
const handleMobilePickAll = useCallback(() => {
|
|
@@ -102,6 +102,8 @@ export function InventoryProvider({ connector, children, noDragSpread = false, n
|
|
|
102
102
|
// Set to true when endDrag fires; cleared on the next mouseDown in Slot.
|
|
103
103
|
// Prevents spurious mouseUp events from sending unwanted clicks after a drag.
|
|
104
104
|
const dragEndedRef = useRef(false)
|
|
105
|
+
/** Latest full context value; updated every render for global debug (`globalThis.__mcInv.state`). */
|
|
106
|
+
const valueRef = useRef<InventoryContextValue | null>(null)
|
|
105
107
|
|
|
106
108
|
useEffect(() => {
|
|
107
109
|
if (!connector) return
|
|
@@ -284,19 +286,6 @@ export function InventoryProvider({ connector, children, noDragSpread = false, n
|
|
|
284
286
|
[windowState],
|
|
285
287
|
)
|
|
286
288
|
|
|
287
|
-
// Expose state to window for easier debugging from browser DevTools.
|
|
288
|
-
// Access via: window.__mcInv
|
|
289
|
-
useEffect(() => {
|
|
290
|
-
;(window as unknown as Record<string, unknown>).__mcInv = {
|
|
291
|
-
get windowState() { return windowStateRef.current },
|
|
292
|
-
get heldItem() { return heldItemRef.current },
|
|
293
|
-
get dragSlots() { return dragSlots },
|
|
294
|
-
get isDragging() { return isDragging },
|
|
295
|
-
get focusedSlot() { return focusedSlot },
|
|
296
|
-
get pKeyActive() { return pKeyActive },
|
|
297
|
-
}
|
|
298
|
-
})
|
|
299
|
-
|
|
300
289
|
const value: InventoryContextValue = {
|
|
301
290
|
windowState,
|
|
302
291
|
playerState,
|
|
@@ -331,5 +320,20 @@ export function InventoryProvider({ connector, children, noDragSpread = false, n
|
|
|
331
320
|
dragEndedRef,
|
|
332
321
|
}
|
|
333
322
|
|
|
323
|
+
valueRef.current = value
|
|
324
|
+
|
|
325
|
+
// Full inventory UI state on global for DevTools: globalThis.__mcInv.state (e.g. windowState, playerState, heldItem, drag state).
|
|
326
|
+
useEffect(() => {
|
|
327
|
+
const g = globalThis as any
|
|
328
|
+
g.__mcInv = {
|
|
329
|
+
get state() {
|
|
330
|
+
return valueRef.current
|
|
331
|
+
},
|
|
332
|
+
}
|
|
333
|
+
return () => {
|
|
334
|
+
delete g.__mcInv
|
|
335
|
+
}
|
|
336
|
+
}, [])
|
|
337
|
+
|
|
334
338
|
return <InventoryContext.Provider value={value}>{children}</InventoryContext.Provider>
|
|
335
339
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react'
|
|
2
|
+
|
|
3
|
+
function isTypingTarget(el: EventTarget | null): boolean {
|
|
4
|
+
if (!el || !(el instanceof HTMLElement)) return false
|
|
5
|
+
const tag = el.tagName
|
|
6
|
+
if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return true
|
|
7
|
+
if (el.isContentEditable) return true
|
|
8
|
+
return false
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* While W is held, a primary-button pointerdown anywhere (capture phase) calls onClose.
|
|
13
|
+
* Ignores events when the target is an input/textarea/select or contenteditable.
|
|
14
|
+
*/
|
|
15
|
+
export function useCloseOnWClick(onClose?: () => void) {
|
|
16
|
+
const onCloseRef = useRef(onClose)
|
|
17
|
+
onCloseRef.current = onClose
|
|
18
|
+
const wHeldRef = useRef(false)
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (!onClose) return
|
|
22
|
+
|
|
23
|
+
const onKeyDown = (e: KeyboardEvent) => {
|
|
24
|
+
if (e.code === 'KeyW' && !e.repeat) wHeldRef.current = true
|
|
25
|
+
}
|
|
26
|
+
const onKeyUp = (e: KeyboardEvent) => {
|
|
27
|
+
if (e.code === 'KeyW') wHeldRef.current = false
|
|
28
|
+
}
|
|
29
|
+
const resetW = () => {
|
|
30
|
+
wHeldRef.current = false
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const onPointerDownCapture = (e: PointerEvent) => {
|
|
34
|
+
if (e.button !== 0 || !wHeldRef.current) return
|
|
35
|
+
if (isTypingTarget(e.target)) return
|
|
36
|
+
const cb = onCloseRef.current
|
|
37
|
+
if (!cb) return
|
|
38
|
+
cb()
|
|
39
|
+
e.preventDefault()
|
|
40
|
+
e.stopPropagation()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
window.addEventListener('keydown', onKeyDown)
|
|
44
|
+
window.addEventListener('keyup', onKeyUp)
|
|
45
|
+
window.addEventListener('blur', resetW)
|
|
46
|
+
document.addEventListener('pointerdown', onPointerDownCapture, true)
|
|
47
|
+
|
|
48
|
+
return () => {
|
|
49
|
+
window.removeEventListener('keydown', onKeyDown)
|
|
50
|
+
window.removeEventListener('keyup', onKeyUp)
|
|
51
|
+
window.removeEventListener('blur', resetW)
|
|
52
|
+
document.removeEventListener('pointerdown', onPointerDownCapture, true)
|
|
53
|
+
}
|
|
54
|
+
}, [onClose])
|
|
55
|
+
}
|
|
@@ -452,6 +452,9 @@ export const inventoryDefinitions = makeInventoryDefinitions({
|
|
|
452
452
|
topEnchantId: { dataSlot: 4, description: 'Top enchantment ID' },
|
|
453
453
|
middleEnchantId: { dataSlot: 5, description: 'Middle enchantment ID' },
|
|
454
454
|
bottomEnchantId: { dataSlot: 6, description: 'Bottom enchantment ID' },
|
|
455
|
+
topLevelClue: { dataSlot: 7, description: 'Top enchantment level clue (e.g. III)' },
|
|
456
|
+
middleLevelClue: { dataSlot: 8, description: 'Middle enchantment level clue (e.g. II)' },
|
|
457
|
+
bottomLevelClue: { dataSlot: 9, description: 'Bottom enchantment level clue (e.g. I)' },
|
|
455
458
|
},
|
|
456
459
|
slots: [
|
|
457
460
|
{ x: 15, y: 47, group: 'enchant' },
|
|
@@ -645,7 +648,7 @@ export const inventoryDefinitions = makeInventoryDefinitions({
|
|
|
645
648
|
backgroundHeight: 166,
|
|
646
649
|
slots: [
|
|
647
650
|
{ x: 15, y: 15, group: 'input', label: 'Map' },
|
|
648
|
-
{ x: 15, y:
|
|
651
|
+
{ x: 15, y: 52, group: 'input', label: 'Paper/Map' },
|
|
649
652
|
{ x: 145, y: 39, group: 'result', resultSlot: true },
|
|
650
653
|
...playerInv(84),
|
|
651
654
|
],
|