modelstage 0.5.24 → 0.6.1
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/CHANGELOG.md +83 -0
- package/README.md +2 -56
- package/bin/modelstage.mjs +48 -12
- package/config/models/live2d.assetbundls.json +1 -1
- package/config/models/live2d.azurlane.json +1 -1
- package/config/models/live2d.azurlane_bundle.json +1 -1
- package/config/models/live2d.azurlane_bundle.jsonl +2 -0
- package/config/models/live2d.azurlane_spine.json +1 -1
- package/config/models/live2d.bd2.json +1 -1
- package/config/models/live2d.bd2.jsonl +2 -0
- package/config/models/live2d.bluearchive.json +1 -1
- package/config/models/live2d.nikke.json +1 -1
- package/config/models/live2d.nikke.jsonl +2 -0
- package/config/models/live2d.nikke_2.json +1 -1
- package/config/models/live2d.npc.json +1 -1
- package/config/models/spine.assetbundls.json +1 -1
- package/config/models/spine.azurlane.json +1 -1
- package/config/models/spine.azurlane_bundle.json +1 -1
- package/config/models/spine.azurlane_bundle.jsonl +2 -0
- package/config/models/spine.azurlane_spine.json +1 -1
- package/config/models/spine.bd2.json +1 -1
- package/config/models/spine.bd2.jsonl +433 -0
- package/config/models/spine.bluearchive.json +1 -1
- package/config/models/spine.nikke.json +1 -1
- package/config/models/spine.nikke_2.json +1 -1
- package/config/models/spine.npc.json +1 -1
- package/package.json +8 -2
- package/src/server/assets/probe.mjs +1 -1
- package/src/server/core/config.mjs +2 -2
- package/src/server/core/log.mjs +1 -1
- package/src/server/data/catalog.mjs +11 -14
- package/src/server/data/live2d.mjs +139 -40
- package/src/server/data/manifest-cache.mjs +53 -10
- package/src/server/data/manifest-jsonl.mjs +264 -0
- package/src/server/data/manifest-stamp.mjs +52 -2
- package/src/server/data/scan-progress.mjs +305 -0
- package/src/server/data/scan-queue.mjs +99 -0
- package/src/server/data/spine.mjs +324 -106
- package/src/server/http/write.mjs +8 -1
- package/src/server/plugins/registry.mjs +1 -1
- package/src/server/routes/data.mjs +76 -17
- package/src/server/serve.mjs +71 -4
- package/src/server/unpack/plugin-store.mjs +1 -1
- package/src/server/unpack/runtime/exec.mjs +37 -8
- package/src/server/unpack/worker-client.mjs +23 -8
- package/src/shared/scan-limits.mjs +23 -0
- package/src/shared/type-labels.mjs +31 -0
- package/src/web/app/anim-list.js +1 -1
- package/src/web/app/backgrounds.js +4 -8
- package/src/web/app/dir-picker.js +3 -5
- package/src/web/app/memory-budget.js +0 -1
- package/src/web/app/memory-guard.js +0 -1
- package/src/web/app/model-actions.js +18 -15
- package/src/web/app/root-manager.js +2 -4
- package/src/web/app/transport-actions.js +1 -1
- package/src/web/app/ui-state.js +1 -1
- package/src/web/components.css +149 -0
- package/src/web/debug/race-probe.js +36 -13
- package/src/web/engine/textures.js +1 -2
- package/src/web/index.html +2 -0
- package/src/web/infra/api.js +10 -19
- package/src/web/infra/document.js +3 -3
- package/src/web/infra/plugin-host-unpack.mjs +81 -9
- package/src/web/infra/ready.js +177 -39
- package/src/web/live2d/bridge.js +1 -1
- package/src/web/live2d/runtime-adapter.js +2 -4
- package/src/web/panel/panel-settings.js +88 -3
- package/src/web/roster/events.js +159 -0
- package/src/web/roster/filter.js +4 -4
- package/src/web/roster/list.js +3 -3
- package/src/web/roster/load.js +440 -0
- package/src/web/roster/persist.js +64 -15
- package/src/web/roster/render.js +284 -0
- package/src/web/roster/reveal.js +4 -9
- package/src/web/roster/rows.js +35 -13
- package/src/web/roster/scan-stream.js +346 -0
- package/src/web/roster/snapshot.js +16 -8
- package/src/web/roster/store.js +292 -0
- package/src/web/roster/type-filter.js +97 -0
- package/src/web/roster/view.js +148 -544
- package/src/web/spine/canvas-runtime.js +4 -5
- package/src/web/spine/runtimes.js +1 -1
- package/tool/lib/check-perms.mjs +0 -114
- package/tool/lib/contract-checks.mjs +0 -582
- package/tool/lib/cubism-core.mjs +0 -122
- package/tool/lib/export-bundle.mjs +0 -328
- package/tool/lib/spine-runtime.mjs +0 -131
- package/tool/lib/test-lock.mjs +0 -178
- package/tool/lib/test-report.mjs +0 -133
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// roster/type-filter.js —— 名册「内容类型」筛选(纯函数,不碰 DOM、不读存储)
|
|
2
|
+
//
|
|
3
|
+
// 与 filter.js 的分工:
|
|
4
|
+
// · filter.js 管**关键词**(角色整组判定:命中角色名或任一变体即显示整组)
|
|
5
|
+
// · 本文件 管**类型**(按条目自带的 kind 归并、计数、过滤)
|
|
6
|
+
// 两者正交:视图层先按类型筛,再按关键词筛(或反过来),结果一致(交集)。
|
|
7
|
+
//
|
|
8
|
+
// 类型从哪来:条目的 `kind`(内容类型)。**未解包的容器条目 kind 可能为 null**,
|
|
9
|
+
// 一律归入 UNKNOWN_TYPE_KEY(显示名「待解包」)—— 否则这些条目在"选中任意类型"时会被全部滤掉,
|
|
10
|
+
// 用户会以为资源包不见了(设计稿 §5.1 的关键结论)。
|
|
11
|
+
|
|
12
|
+
import { UNKNOWN_TYPE_KEY } from '../../shared/type-labels.mjs';
|
|
13
|
+
import { visibleChars } from './filter.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 单个变体的类型键:条目 kind 为空 ⇒ unknown。
|
|
17
|
+
* 为什么看 kind 而不是 container:container 是"装载方式"(要先解包),kind 才是"内容类型"。
|
|
18
|
+
* @param {{model?: {kind?: string|null}}} variant
|
|
19
|
+
* @returns {string}
|
|
20
|
+
*/
|
|
21
|
+
export function typeKeyOf(variant) {
|
|
22
|
+
const model = variant && variant.model ? variant.model : variant;
|
|
23
|
+
const kind = model && model.kind != null ? String(model.kind).trim() : '';
|
|
24
|
+
return kind || UNKNOWN_TYPE_KEY;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 类型计数:**当前关键词下、该类型可见的变体数**(不含类型过滤本身的影响)。
|
|
29
|
+
*
|
|
30
|
+
* 为什么要排除类型过滤:数字要回答"如果我再选它,会有多少"。
|
|
31
|
+
* 若把类型过滤也算进去,选中 Spine 后其它胶囊会立刻变 0,用户就没法判断要不要切换。
|
|
32
|
+
* 为什么仍计入关键词:面板上的数字必须和"现在列表里能看到多少"对得上(设计稿 §5.3)。
|
|
33
|
+
*
|
|
34
|
+
* scope 参数是为将来扩维度(如按资源根)预留的:当前只消费 keyword,新增维度时只加 scope 字段,
|
|
35
|
+
* 不动本函数的排序/计数逻辑(设计稿 §5.4)。
|
|
36
|
+
*
|
|
37
|
+
* @param {{groups: object[], groupChars: Map, keyword?: string,
|
|
38
|
+
* scope?: {keyword?: string}}} input
|
|
39
|
+
* @returns {Array<{key: string, count: number}>} 按数量降序,并列按 key 字典序(稳定)
|
|
40
|
+
*/
|
|
41
|
+
export function deriveTypeCounts({ groups, groupChars, keyword = '', scope = null }) {
|
|
42
|
+
const effectiveKeyword = String((scope && scope.keyword) != null ? scope.keyword : keyword || '').toLowerCase();
|
|
43
|
+
const counts = new Map();
|
|
44
|
+
for (const group of groups || []) {
|
|
45
|
+
// 复用关键词过滤的同一口径(角色整组判定),保证"数字"与"列表可见性"不会两套算法
|
|
46
|
+
const chars = visibleChars(group, { groupChars, keyword: effectiveKeyword });
|
|
47
|
+
for (const char of chars) {
|
|
48
|
+
for (const variant of char.variants || []) {
|
|
49
|
+
const typeKey = typeKeyOf(variant);
|
|
50
|
+
counts.set(typeKey, (counts.get(typeKey) || 0) + 1);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return [...counts.entries()]
|
|
55
|
+
.map(([key, count]) => ({ key, count }))
|
|
56
|
+
.sort((left, right) => (right.count - left.count) || (left.key < right.key ? -1 : left.key > right.key ? 1 : 0));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 按选中类型过滤角色(并把每个角色的变体裁到命中集合)。
|
|
61
|
+
*
|
|
62
|
+
* 语义:selectedTypes 为空 ⇒ 不过滤(等于"全部");否则 = 并集(命中任一选中类型即保留)。
|
|
63
|
+
* 注意返回的是**新数组/新角色对象**,不改动 state.groupChars 里的原对象(渲染层只读)。
|
|
64
|
+
*
|
|
65
|
+
* @param {object[]} chars 角色数组(每个带 variants)
|
|
66
|
+
* @param {Set<string>|string[]} selectedTypes
|
|
67
|
+
* @returns {object[]}
|
|
68
|
+
*/
|
|
69
|
+
export function filterCharsByType(chars, selectedTypes) {
|
|
70
|
+
const selected = selectedTypes instanceof Set ? selectedTypes : new Set(selectedTypes || []);
|
|
71
|
+
if (!selected.size) return chars;
|
|
72
|
+
const kept = [];
|
|
73
|
+
for (const char of chars) {
|
|
74
|
+
const variants = (char.variants || []).filter((variant) => selected.has(typeKeyOf(variant)));
|
|
75
|
+
if (variants.length) kept.push({ ...char, variants });
|
|
76
|
+
}
|
|
77
|
+
return kept;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* 把存储里读到的选中集合收敛成"当前可用"的集合。
|
|
82
|
+
*
|
|
83
|
+
* 失效项(当前类型集合里没有的 key)在**当前页面生命周期内被忽略**,但**不删存储**——
|
|
84
|
+
* 这样某个类型暂时消失(例如最后一个资源包被解包)后再出现时,用户的选中会自动恢复。
|
|
85
|
+
* @param {string[]} storedKeys 存储里读到的 key 列表
|
|
86
|
+
* @param {Iterable<string>} availableKeys 当前名册实际出现的类型
|
|
87
|
+
* @returns {Set<string>} 可用的选中集合
|
|
88
|
+
*/
|
|
89
|
+
export function usableTypeSelection(storedKeys, availableKeys) {
|
|
90
|
+
const available = new Set([...availableKeys].map((key) => String(key)));
|
|
91
|
+
const out = new Set();
|
|
92
|
+
for (const raw of Array.isArray(storedKeys) ? storedKeys : []) {
|
|
93
|
+
const key = String(raw == null ? '' : raw);
|
|
94
|
+
if (key && available.has(key)) out.add(key);
|
|
95
|
+
}
|
|
96
|
+
return out;
|
|
97
|
+
}
|