界面迁移更新

This commit is contained in:
2026-09-05 00:12:11 +08:00
parent e2762d631d
commit 0aa66e1ec7
3187 changed files with 667915 additions and 211 deletions
@@ -0,0 +1,92 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { createStoreBinder } from '../../YouleNexus/assets/framework/ui/store-binder.ts';
import { signal } from '../../YouleNexus/assets/framework/core/reactive.ts';
test('createStoreBinder.bind: 订阅 store 后,值变化时 callback 触发', () => {
const store = signal({ name: 'init' });
const b = createStoreBinder();
let got: string | null = null;
b.bind(store, 'state', (s) => { got = s.name; });
assert.equal(got, 'init', '首次同步调用一次');
store.value = { name: 'changed' };
assert.equal(got, 'changed');
});
test('createStoreBinder.bind: 不同 slot 独立', () => {
const store1 = signal(1);
const store2 = signal('x');
const b = createStoreBinder();
const got: Array<number | string> = [];
b.bind(store1, 'a', (v) => got.push(v));
b.bind(store2, 'b', (v) => got.push(v));
assert.deepEqual(got, [1, 'x']);
got.length = 0;
store1.value = 2;
assert.deepEqual(got, [2]);
});
test('createStoreBinder.unbindAll: 退订后 store 变化不再触发 callback', () => {
const store = signal({ x: 0 });
const b = createStoreBinder();
let count = 0;
b.bind(store, 'state', () => { count++; });
assert.equal(count, 1);
store.value = { x: 1 };
assert.equal(count, 2);
b.unbindAll();
store.value = { x: 2 };
assert.equal(count, 2, '退订后不再触发');
});
test('createStoreBinder: 多次 bind 的 unbindAll 全部退订', () => {
const a = signal(1);
const b = signal(2);
const binder = createStoreBinder();
let na = 0, nb = 0;
binder.bind(a, 'a', () => na++);
binder.bind(b, 'b', () => nb++);
assert.equal(na, 1);
assert.equal(nb, 1);
binder.unbindAll();
a.value = 10;
b.value = 20;
assert.equal(na, 1);
assert.equal(nb, 1);
});
test('createStoreBinder: bind 同一 store 多次,各 callback 都触发', () => {
const store = signal(0);
const b = createStoreBinder();
let a = 0, b2 = 0;
b.bind(store, 'x', () => a++);
b.bind(store, 'y', () => b2++);
assert.equal(a, 1);
assert.equal(b2, 1);
store.value = 1;
assert.equal(a, 2);
assert.equal(b2, 2);
});
test('createStoreBinder.unbindAll: 重复调用安全(幂等)', () => {
const store = signal(0);
const b = createStoreBinder();
let n = 0;
b.bind(store, 's', () => n++);
b.unbindAll();
b.unbindAll(); // 不抛错
store.value = 1;
assert.equal(n, 1);
});
test('createStoreBinder.bind: callback 抛错不影响其它订阅者与下次 bind', () => {
const store = signal(0);
const b = createStoreBinder();
let reached = false;
b.bind(store, 'a', () => { throw new Error('boom'); });
b.bind(store, 'b', () => { reached = true; });
// 首次同步调用 + 第二次同步触发都不会让 binder 崩溃
assert.equal(reached, true);
store.value = 1;
assert.equal(reached, true);
});
@@ -0,0 +1,43 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { DEFAULT_THEME } from '../../YouleNexus/assets/framework/ui/theme/default-theme.ts';
import { resolveTheme } from '../../YouleNexus/assets/framework/ui/theme/resolve-theme.ts';
import { ThemeProvider } from '../../YouleNexus/assets/framework/ui/theme/theme-provider.ts';
test('resolveTheme 不传配置时返回默认主题', () => {
const t = resolveTheme();
assert.deepEqual(t, DEFAULT_THEME);
});
test('resolveTheme 只覆盖给出的字段,其余取默认', () => {
const t = resolveTheme({ colors: { primary: '#C8102E' } });
assert.equal(t.colors.primary, '#C8102E');
assert.equal(t.colors.accent, DEFAULT_THEME.colors.accent);
assert.equal(t.fonts.sizeBody, DEFAULT_THEME.fonts.sizeBody);
assert.equal(t.layout.myInfoAnchorX, DEFAULT_THEME.layout.myInfoAnchorX);
});
test('resolveTheme 分组独立合并,互不影响', () => {
const t = resolveTheme({ layout: { myInfoAnchorX: 130 } });
assert.equal(t.layout.myInfoAnchorX, 130);
assert.equal(t.layout.nicknameMaxChars, DEFAULT_THEME.layout.nicknameMaxChars);
assert.deepEqual(t.colors, DEFAULT_THEME.colors);
});
test('resolveTheme 不修改 DEFAULT_THEME 本身', () => {
const before = DEFAULT_THEME.colors.primary;
resolveTheme({ colors: { primary: '#000000' } });
assert.equal(DEFAULT_THEME.colors.primary, before);
});
test('ThemeProvider 未 init 就读取会抛错(第二准则:不兜底)', () => {
ThemeProvider.reset();
assert.throws(() => ThemeProvider.get(), /未初始化/);
});
test('ThemeProvider init 后可读回同一份主题', () => {
ThemeProvider.reset();
const t = resolveTheme({ colors: { primary: '#123456' } });
ThemeProvider.init(t);
assert.equal(ThemeProvider.get().colors.primary, '#123456');
});