Update spacetime-client bindings and frontend

Large update across server and web clients: regenerated/added many spacetime-client module bindings and input types (including new delete/work_delete input types and numerous procedure/reducer files), updates to server-rs API modules (bark_battle, jump_hop, wooden_fish, auth, module-runtime and shared contracts), and fixes in module-runtime behavior and domain logic. Frontend changes include new/updated components and tests (creative audio helpers, bark-battle/jump-hop/wooden-fish clients and views, unified generation pages, RPG entry views, and runtime shells), plus CSS and service updates. Documentation and operational notes updated (.hermes pitfalls and multiple PRD/docs) to cover daily-task refresh, banner asset fallback, recommend-key bug, and other platform behaviors. Tests and verification steps added/updated alongside these changes.
This commit is contained in:
2026-06-04 22:44:19 +08:00
parent 2678954627
commit 27b30f974b
326 changed files with 4374 additions and 2539 deletions

View File

@@ -17,10 +17,12 @@ const baseUser: AuthUser = {
displayName: '138****8000',
avatarUrl: null,
publicUserCode: 'user-tester',
phoneNumber: '13800138000',
phoneNumberMasked: '138****8000',
loginMethod: 'phone',
bindingStatus: 'active',
wechatBound: true,
wechatAccount: 'wx-openid-bind-001',
};
function renderAccountModal(overrides?: {
@@ -112,6 +114,10 @@ test('settings header uses a generic title instead of the phone number', () => {
expect(screen.queryByText('当前主题')).toBeNull();
expect(screen.queryByRole('button', { name: '退出登录' })).toBeNull();
expect(screen.queryByRole('button', { name: '退出全部设备' })).toBeNull();
expect(screen.getByRole('button', { name: //u })).toBeTruthy();
expect(screen.getByRole('button', { name: //u })).toBeTruthy();
expect(screen.queryByRole('button', { name: //u })).toBeNull();
expect(screen.queryByRole('button', { name: //u })).toBeNull();
});
test('direct account entry does not render the settings shell as another dialog', () => {
@@ -129,12 +135,52 @@ test('direct account entry does not render the settings shell as another dialog'
).toBeNull();
});
test('account panel uses compact binding cards and keeps logout actions at the bottom', () => {
renderAccountModal({ entryMode: 'account' });
const accountDialog = screen.getByRole('dialog', { name: '账号信息' });
expect(within(accountDialog).getByText('账号信息')).toBeTruthy();
expect(within(accountDialog).queryByText('身份信息')).toBeNull();
expect(
within(accountDialog).queryByText(
'统一查看身份、安全状态、登录设备与最近操作。',
),
).toBeNull();
expect(within(accountDialog).queryByText('登录方式')).toBeNull();
expect(within(accountDialog).getByText('绑定手机号')).toBeTruthy();
expect(within(accountDialog).getByText('13800138000')).toBeTruthy();
expect(within(accountDialog).queryByText('138****8000')).toBeNull();
expect(within(accountDialog).getByText('绑定微信')).toBeTruthy();
expect(within(accountDialog).getByText('wx-openid-bind-001')).toBeTruthy();
const compactCards = accountDialog.querySelectorAll(
'[data-account-binding-card]',
);
expect(compactCards).toHaveLength(2);
expect(
within(compactCards[0] as HTMLElement).getByRole('button', {
name: '更换手机号',
}),
).toBeTruthy();
expect(
within(compactCards[1] as HTMLElement).getByRole('button', {
name: '更换微信号',
}),
).toBeTruthy();
const accountContent =
accountDialog.querySelector('[data-account-content]') ?? accountDialog;
expect(
accountContent.lastElementChild?.getAttribute('data-account-actions'),
).toBe('true');
});
test('account actions open in independent panels instead of inline expansion', async () => {
const user = userEvent.setup();
renderAccountModal();
await user.click(screen.getByRole('button', { name: /账号信息/ }));
await user.click(screen.getByRole('button', { name: /账号与安全/ }));
const accountDialog = screen.getByRole('dialog', { name: '账号信息' });
expect(accountDialog).toBeTruthy();
@@ -162,7 +208,7 @@ test('nested settings panels keep back navigation without an extra close action'
renderAccountModal();
await user.click(screen.getByRole('button', { name: /账号信息/ }));
await user.click(screen.getByRole('button', { name: /账号与安全/ }));
const accountDialog = screen.getByRole('dialog', { name: '账号信息' });
const accountHeader = accountDialog.firstElementChild as HTMLElement | null;
@@ -201,7 +247,7 @@ test('settings overlays move focus away from inert triggers and restore it on ba
renderAccountModal();
const accountTrigger = screen.getByRole('button', { name: /账号信息/ });
const accountTrigger = screen.getByRole('button', { name: /账号与安全/ });
expect(document.activeElement).not.toBe(accountTrigger);
await user.click(accountTrigger);
@@ -283,7 +329,7 @@ test('account panel includes merged security devices and audit sections', async
],
});
await user.click(screen.getByRole('button', { name: /账号信息/ }));
await user.click(screen.getByRole('button', { name: /账号与安全/ }));
const accountDialog = screen.getByRole('dialog', { name: '账号信息' });
expect(within(accountDialog).getByText('安全状态')).toBeTruthy();
@@ -324,7 +370,7 @@ test('current merged session group hides kick action and shows count', async ()
],
});
await user.click(screen.getByRole('button', { name: /账号信息/ }));
await user.click(screen.getByRole('button', { name: /账号与安全/ }));
const accountDialog = screen.getByRole('dialog', { name: '账号信息' });
expect(within(accountDialog).getByText('2 个会话')).toBeTruthy();
@@ -348,7 +394,7 @@ test('remote merged session group can be revoked with loading state', async () =
revokingSessionIds: ['usess_remote'],
});
await user.click(screen.getByRole('button', { name: /账号信息/ }));
await user.click(screen.getByRole('button', { name: /账号与安全/ }));
const accountDialog = screen.getByRole('dialog', { name: '账号信息' });
const revokeButton = within(accountDialog).getByRole('button', {
@@ -373,7 +419,7 @@ test('remote session revoke passes the grouped session payload', async () => {
onRevokeSession,
});
await user.click(screen.getByRole('button', { name: /账号信息/ }));
await user.click(screen.getByRole('button', { name: /账号与安全/ }));
await user.click(
within(screen.getByRole('dialog', { name: '账号信息' })).getByRole(
'button',