Sync source to CNB / Sync Git ref (push) Canceled after 0s
从 Axolotl 1.9.7 移植皮肤编辑器的盔甲/盔甲纹饰预览功能, 让玩家在皮肤选择页可试穿盔甲并调整纹饰。 新增: - 前端组件 ArmorTrimTab / SkinListSkeleton 及 skin-armor-preview helper - packages/ui 组件 ArmorPreviewControls / ArmorTrimPanel - packages/ui armor 预览 composable(armor-preview-assets/types、use-armor-preview) - packages/ui webgl 盔甲渲染(armor-geometry / armor-preview-object / armor-rendering) - packages/ui/src/assets/minecraft-armor 盔甲素材 改动: - SkinPreviewRenderer 增加 armorPreview/armorControls props、 armorConfig model 与 useArmorPreview 集成 - Skins.vue 增加盔甲 tab、状态逻辑与模板接入 - 补齐 skin/composables/utils 的导出 适配说明:ArmorPreviewControls 原版依赖 reka-ui 的 popover, Starlight 无该依赖且只用 external 模式,故改写为 external-only, 去除 reka-ui 与 overlays.css 依赖。
61 lines
1.7 KiB
TypeScript
61 lines
1.7 KiB
TypeScript
import assert from 'node:assert/strict'
|
|
import test from 'node:test'
|
|
|
|
import { loadSkinArmorPreview, saveSkinArmorPreview } from './skin-armor-preview.ts'
|
|
|
|
const originalStorage = Object.getOwnPropertyDescriptor(globalThis, 'localStorage')
|
|
|
|
function withStorage(run: (values: Map<string, string>) => void): void {
|
|
const values = new Map<string, string>()
|
|
Object.defineProperty(globalThis, 'localStorage', {
|
|
configurable: true,
|
|
value: {
|
|
getItem: (key: string) => values.get(key) ?? null,
|
|
setItem: (key: string, value: string) => values.set(key, value),
|
|
},
|
|
})
|
|
try {
|
|
run(values)
|
|
} finally {
|
|
if (originalStorage) Object.defineProperty(globalThis, 'localStorage', originalStorage)
|
|
else delete (globalThis as { localStorage?: Storage }).localStorage
|
|
}
|
|
}
|
|
|
|
test('armor preview selections survive storage round trips', () => {
|
|
withStorage(() => {
|
|
const config = loadSkinArmorPreview()
|
|
config.helmet.material = 'diamond'
|
|
config.helmet.trimPattern = 'sentry'
|
|
config.helmet.trimMaterial = 'gold'
|
|
saveSkinArmorPreview(config)
|
|
|
|
assert.deepEqual(loadSkinArmorPreview(), config)
|
|
})
|
|
})
|
|
|
|
test('invalid stored armor values fall back per slot and field', () => {
|
|
withStorage((values) => {
|
|
values.set(
|
|
'axolotl:skin-armor-preview',
|
|
JSON.stringify({
|
|
helmet: { material: 'diamond', trimPattern: 'invalid', trimMaterial: 'gold' },
|
|
boots: { material: 'turtle', trimPattern: 'sentry', trimMaterial: 'invalid' },
|
|
}),
|
|
)
|
|
|
|
const config = loadSkinArmorPreview()
|
|
assert.deepEqual(config.helmet, {
|
|
material: 'diamond',
|
|
trimPattern: null,
|
|
trimMaterial: 'gold',
|
|
})
|
|
assert.deepEqual(config.boots, {
|
|
material: null,
|
|
trimPattern: 'sentry',
|
|
trimMaterial: 'iron',
|
|
})
|
|
assert.equal(config.chestplate.material, null)
|
|
})
|
|
})
|