poi-plugin-inventory-cleaner 1.11.1 → 1.12.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.
- package/README-CN.md +1 -1
- package/README.md +1 -1
- package/index.js +126 -23
- package/lib/equip-types.js +1 -0
- package/lib/inventory.js +1 -0
- package/lib/style.js +115 -0
- package/package.json +1 -1
package/README-CN.md
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
- **联动当前任务**:任务需要的装备标「任务需要 N 件」,不参与可拆的件数就是预留的那部分。
|
|
18
18
|
- **按装备类型筛选**:沿用游戏本体「装备筛选」的 14 个大类 / 60 多个细分类型(与「装备信息」插件一致,如舰上战斗机、夜间轰炸机、高角炮、大口径主炮(II)),下拉框按大类分组。
|
|
19
19
|
- **排序**:按攻略判定 / 数量 / 可拆件数 / 名称 / 类型,随时切换。
|
|
20
|
-
-
|
|
20
|
+
- **名称显示**:中文名为主,日文原名作为副标题保留;每行和分类下拉都带游戏原版装备图标。
|
|
21
21
|
- **筛选与搜索**:全部 / 可拆 / 留一些 / 不拆 / 看计划 / 受保护,支持按名称或 id 搜索。
|
|
22
22
|
|
|
23
23
|
- **独立窗口**:使用 poi 原生的「在新窗口打开插件」功能(`poiPlugin.windowMode`,默认开启)。点击插件图标时 poi 会自己创建窗口并承载插件,带标题栏、主题、缩放和窗口位置记忆;不需要的话在「设置 → 插件 → 库存清理大师」里关掉即可。
|
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
- **Unfinished quest needs** — collects equipment from quest text (in-progress quests, `poi-plugin-quest-planner` observed quests, the `poi-plugin-quest-info-2` quest list, or the I-Tell-You cache — whichever is available), parses the required count, and reserves that many pieces. It does not depend on a single plugin; with none installed it still counts in-progress quests.
|
|
16
16
|
- **Type filter** — the game's own 14 equipment categories and 60+ detail types, matching Equipment Info, grouped in the dropdown.
|
|
17
17
|
- **Sorting** — verdict / count / clearable / name / type.
|
|
18
|
-
- **Names** — Chinese name first, original Japanese shown underneath.
|
|
18
|
+
- **Names** — Chinese name first, original Japanese shown underneath, with the in-game equipment icon on every row and in the type picker.
|
|
19
19
|
- **Filters & search** — all / scrap / keep some / keep / check plan / protected; search by name or id.
|
|
20
20
|
|
|
21
21
|
- **Native window mode** — ships `poiPlugin.windowMode`, so poi opens the plugin in its own managed window (titlebar, theme, zoom, remembered bounds). Toggle it off in poi settings if you prefer the tab.
|
package/index.js
CHANGED
|
@@ -133,6 +133,118 @@ function Stat({ label, value, tone }) {
|
|
|
133
133
|
)
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
function iconBase() {
|
|
137
|
+
if (typeof window === 'undefined') return ''
|
|
138
|
+
const root = window.ROOT || window.__ROOT || ''
|
|
139
|
+
if (!root) return ''
|
|
140
|
+
return 'file:///' + String(root).replace(/\\/g, '/').replace(/^\/+/, '')
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function iconUrl(iconType, kind) {
|
|
144
|
+
const base = iconBase()
|
|
145
|
+
if (!base) return ''
|
|
146
|
+
const id = Number(iconType) || 0
|
|
147
|
+
return kind === 'png'
|
|
148
|
+
? base + '/assets/img/slotitem/' + id + '.png'
|
|
149
|
+
: base + '/assets/svg/slotitem/' + id + '.svg'
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// poi 的 PNG 图标只到 47,48~60 只有 SVG;按 svg → png → 通用图标 的顺序回落
|
|
153
|
+
function fallbackIcon(event) {
|
|
154
|
+
const img = event.currentTarget
|
|
155
|
+
const stage = img.dataset.iconStage || 'svg'
|
|
156
|
+
const id = Number(img.dataset.iconId) || 0
|
|
157
|
+
if (stage === 'svg') {
|
|
158
|
+
img.dataset.iconStage = 'png'
|
|
159
|
+
img.src = iconUrl(id, 'png')
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
if (stage === 'png') {
|
|
163
|
+
img.dataset.iconStage = 'none'
|
|
164
|
+
img.src = iconUrl(-1, 'svg')
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function CategoryPicker({ value, groups, onChange }) {
|
|
169
|
+
const [open, setOpen] = React.useState(false)
|
|
170
|
+
const boxRef = React.useRef(null)
|
|
171
|
+
|
|
172
|
+
React.useEffect(() => {
|
|
173
|
+
if (!open) return undefined
|
|
174
|
+
const onDocClick = (event) => {
|
|
175
|
+
if (boxRef.current && !boxRef.current.contains(event.target)) setOpen(false)
|
|
176
|
+
}
|
|
177
|
+
document.addEventListener('mousedown', onDocClick)
|
|
178
|
+
return () => document.removeEventListener('mousedown', onDocClick)
|
|
179
|
+
}, [open])
|
|
180
|
+
|
|
181
|
+
return h(
|
|
182
|
+
'div',
|
|
183
|
+
{ className: 'stock-picker', ref: boxRef },
|
|
184
|
+
h(
|
|
185
|
+
'button',
|
|
186
|
+
{
|
|
187
|
+
type: 'button',
|
|
188
|
+
className: 'stock-picker-btn',
|
|
189
|
+
title: t('stock.categoryTitle'),
|
|
190
|
+
onClick: () => setOpen((current) => !current),
|
|
191
|
+
},
|
|
192
|
+
h('span', { className: 'stock-picker-label' }, value || t('stock.categoryAll')),
|
|
193
|
+
h('span', { className: 'stock-picker-caret' }, '▾'),
|
|
194
|
+
),
|
|
195
|
+
open
|
|
196
|
+
? h(
|
|
197
|
+
'div',
|
|
198
|
+
{ className: 'stock-picker-panel' },
|
|
199
|
+
h(
|
|
200
|
+
'button',
|
|
201
|
+
{
|
|
202
|
+
type: 'button',
|
|
203
|
+
className: 'stock-picker-item' + (value ? '' : ' is-active'),
|
|
204
|
+
onClick: () => {
|
|
205
|
+
onChange('')
|
|
206
|
+
setOpen(false)
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
h('span', { className: 'stock-picker-name' }, t('stock.categoryAll')),
|
|
210
|
+
),
|
|
211
|
+
groups.map(([groupName, list]) =>
|
|
212
|
+
h(
|
|
213
|
+
'div',
|
|
214
|
+
{ className: 'stock-picker-group', key: groupName },
|
|
215
|
+
h('div', { className: 'stock-picker-title' }, groupName),
|
|
216
|
+
list.map((entry) => {
|
|
217
|
+
const key = entry.key || OTHER_CATEGORY
|
|
218
|
+
return h(
|
|
219
|
+
'button',
|
|
220
|
+
{
|
|
221
|
+
type: 'button',
|
|
222
|
+
key,
|
|
223
|
+
className: 'stock-picker-item' + (value === key ? ' is-active' : ''),
|
|
224
|
+
onClick: () => {
|
|
225
|
+
onChange(key)
|
|
226
|
+
setOpen(false)
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
h('img', {
|
|
230
|
+
className: 'stock-picker-icon',
|
|
231
|
+
src: iconUrl(entry.icon),
|
|
232
|
+
alt: '',
|
|
233
|
+
draggable: 'false',
|
|
234
|
+
'data-icon-id': String(entry.icon || 0),
|
|
235
|
+
onError: fallbackIcon,
|
|
236
|
+
}),
|
|
237
|
+
h('span', { className: 'stock-picker-name' }, entry.key || t('stock.batchOther')),
|
|
238
|
+
h('span', { className: 'stock-picker-count' }, String(entry.kinds)),
|
|
239
|
+
)
|
|
240
|
+
}),
|
|
241
|
+
),
|
|
242
|
+
),
|
|
243
|
+
)
|
|
244
|
+
: null,
|
|
245
|
+
)
|
|
246
|
+
}
|
|
247
|
+
|
|
136
248
|
function StockRow({ row, onKeepChange }) {
|
|
137
249
|
const verdict = VERDICT[row.verdict] || VERDICT.unknown
|
|
138
250
|
return h(
|
|
@@ -141,6 +253,14 @@ function StockRow({ row, onKeepChange }) {
|
|
|
141
253
|
h(
|
|
142
254
|
'div',
|
|
143
255
|
{ className: 'stock-row-main' },
|
|
256
|
+
h('img', {
|
|
257
|
+
className: 'stock-icon',
|
|
258
|
+
src: iconUrl(row.icon),
|
|
259
|
+
alt: '',
|
|
260
|
+
draggable: 'false',
|
|
261
|
+
'data-icon-id': String(row.icon || 0),
|
|
262
|
+
onError: fallbackIcon,
|
|
263
|
+
}),
|
|
144
264
|
h('span', { className: 'stock-name' }, row.cn || row.jp || `#${row.id}`),
|
|
145
265
|
row.cn && row.jp && row.cn !== row.jp
|
|
146
266
|
? h('span', { className: 'stock-cn' }, row.jp)
|
|
@@ -307,6 +427,7 @@ function App() {
|
|
|
307
427
|
const entry = map.get(key) || {
|
|
308
428
|
key,
|
|
309
429
|
kinds: 0,
|
|
430
|
+
icon: row.icon || 0,
|
|
310
431
|
group: row.group || '',
|
|
311
432
|
groupId: row.groupId || 99,
|
|
312
433
|
categoryId: row.categoryId || 0,
|
|
@@ -391,29 +512,11 @@ function App() {
|
|
|
391
512
|
placeholder: t('stock.search'),
|
|
392
513
|
onChange: (event) => setKeyword(event.target.value),
|
|
393
514
|
}),
|
|
394
|
-
h(
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
title: t('stock.categoryTitle'),
|
|
400
|
-
onChange: (event) => setCategory(event.target.value),
|
|
401
|
-
},
|
|
402
|
-
h('option', { value: '' }, t('stock.categoryAll')),
|
|
403
|
-
categoryGroups.map(([groupName, list]) =>
|
|
404
|
-
h(
|
|
405
|
-
'optgroup',
|
|
406
|
-
{ key: groupName, label: groupName },
|
|
407
|
-
list.map((entry) =>
|
|
408
|
-
h(
|
|
409
|
-
'option',
|
|
410
|
-
{ key: entry.key || 'other', value: entry.key || OTHER_CATEGORY },
|
|
411
|
-
(entry.key || t('stock.batchOther')) + ' (' + entry.kinds + ')',
|
|
412
|
-
),
|
|
413
|
-
),
|
|
414
|
-
),
|
|
415
|
-
),
|
|
416
|
-
),
|
|
515
|
+
h(CategoryPicker, {
|
|
516
|
+
value: category,
|
|
517
|
+
groups: categoryGroups,
|
|
518
|
+
onChange: setCategory,
|
|
519
|
+
}),
|
|
417
520
|
h(
|
|
418
521
|
'select',
|
|
419
522
|
{
|
package/lib/equip-types.js
CHANGED
|
@@ -162,6 +162,7 @@ function buildTypeIndex(state) {
|
|
|
162
162
|
const group = detail ? GROUP_BY_ID.get(detail.group) : null
|
|
163
163
|
index.set(Number(id), {
|
|
164
164
|
filterType,
|
|
165
|
+
icon: Number(master.api_type && master.api_type[3]) || 0,
|
|
165
166
|
detailId: detail ? detail.id : 0,
|
|
166
167
|
groupId: detail ? detail.group : 99,
|
|
167
168
|
category: labels ? labels.cn : '',
|
package/lib/inventory.js
CHANGED
|
@@ -110,6 +110,7 @@ function buildInventory(state, options = {}) {
|
|
|
110
110
|
verdict,
|
|
111
111
|
rawVerdict: rule ? rule.raw : '',
|
|
112
112
|
category: typeInfo && typeInfo.category ? typeInfo.category : (rule ? rule.category : ''),
|
|
113
|
+
icon: typeInfo ? typeInfo.icon : 0,
|
|
113
114
|
categoryId: typeInfo ? typeInfo.detailId : 0,
|
|
114
115
|
group: typeInfo ? typeInfo.group : '',
|
|
115
116
|
groupId: typeInfo ? typeInfo.groupId : 99,
|
package/lib/style.js
CHANGED
|
@@ -100,6 +100,121 @@ const CSS = `
|
|
|
100
100
|
border-color: var(--stock-indigo);
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
.stock-icon {
|
|
104
|
+
width: 24px;
|
|
105
|
+
height: 24px;
|
|
106
|
+
flex: none;
|
|
107
|
+
border-radius: 4px;
|
|
108
|
+
opacity: 0.92;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.stock-picker {
|
|
112
|
+
position: relative;
|
|
113
|
+
display: inline-block;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.stock-picker-btn {
|
|
117
|
+
display: inline-flex;
|
|
118
|
+
align-items: center;
|
|
119
|
+
gap: 6px;
|
|
120
|
+
height: 32px;
|
|
121
|
+
max-width: 240px;
|
|
122
|
+
padding: 0 10px;
|
|
123
|
+
border: 1px solid var(--stock-border);
|
|
124
|
+
border-radius: 8px;
|
|
125
|
+
color: inherit;
|
|
126
|
+
background: var(--stock-surface);
|
|
127
|
+
font-family: inherit;
|
|
128
|
+
font-size: 12px;
|
|
129
|
+
cursor: pointer;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.stock-picker-btn:hover {
|
|
133
|
+
border-color: var(--stock-border-strong);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.stock-picker-label {
|
|
137
|
+
overflow: hidden;
|
|
138
|
+
text-overflow: ellipsis;
|
|
139
|
+
white-space: nowrap;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.stock-picker-caret {
|
|
143
|
+
flex: none;
|
|
144
|
+
opacity: 0.6;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.stock-picker-panel {
|
|
148
|
+
position: absolute;
|
|
149
|
+
top: calc(100% + 4px);
|
|
150
|
+
left: 0;
|
|
151
|
+
z-index: 60;
|
|
152
|
+
width: 330px;
|
|
153
|
+
max-height: 440px;
|
|
154
|
+
overflow-y: auto;
|
|
155
|
+
padding: 6px;
|
|
156
|
+
border: 1px solid var(--stock-border-strong);
|
|
157
|
+
border-radius: 10px;
|
|
158
|
+
background: #1b2129;
|
|
159
|
+
box-shadow: 0 14px 34px rgba(0, 0, 0, 0.5);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.stock-picker-group + .stock-picker-group {
|
|
163
|
+
margin-top: 2px;
|
|
164
|
+
border-top: 1px solid rgba(255, 255, 255, 0.06);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.stock-picker-title {
|
|
168
|
+
padding: 8px 8px 3px;
|
|
169
|
+
font-size: 10px;
|
|
170
|
+
letter-spacing: 0.06em;
|
|
171
|
+
color: rgba(232, 237, 244, 0.48);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.stock-picker-item {
|
|
175
|
+
display: flex;
|
|
176
|
+
align-items: center;
|
|
177
|
+
gap: 8px;
|
|
178
|
+
width: 100%;
|
|
179
|
+
padding: 6px 8px;
|
|
180
|
+
border: 0;
|
|
181
|
+
border-radius: 7px;
|
|
182
|
+
color: #e8edf4;
|
|
183
|
+
background: transparent;
|
|
184
|
+
font-family: inherit;
|
|
185
|
+
font-size: 12px;
|
|
186
|
+
text-align: left;
|
|
187
|
+
cursor: pointer;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.stock-picker-item:hover {
|
|
191
|
+
background: rgba(255, 255, 255, 0.08);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.stock-picker-item.is-active {
|
|
195
|
+
background: rgba(99, 102, 241, 0.4);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.stock-picker-icon {
|
|
199
|
+
width: 22px;
|
|
200
|
+
height: 22px;
|
|
201
|
+
flex: none;
|
|
202
|
+
border-radius: 3px;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.stock-picker-name {
|
|
206
|
+
flex: 1 1 auto;
|
|
207
|
+
overflow: hidden;
|
|
208
|
+
text-overflow: ellipsis;
|
|
209
|
+
white-space: nowrap;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.stock-picker-count {
|
|
213
|
+
flex: none;
|
|
214
|
+
font-variant-numeric: tabular-nums;
|
|
215
|
+
opacity: 0.5;
|
|
216
|
+
}
|
|
217
|
+
|
|
103
218
|
.stock-category {
|
|
104
219
|
height: 32px;
|
|
105
220
|
padding: 0 8px;
|