diff --git a/.hermes/shared-memory/decision-log.md b/.hermes/shared-memory/decision-log.md index b2bfcdd0..de43d580 100644 --- a/.hermes/shared-memory/decision-log.md +++ b/.hermes/shared-memory/decision-log.md @@ -61,6 +61,7 @@ - 2026-06-11 追加:`PlatformDarkModalFooter` 不只收动作按钮区,也继续覆盖纯内容 footer;`CompanionCampModal.tsx` 底部“营地气氛”区域已改成 `layout="content"` + `padding="roomy"` 的共享 footer frame,保留原有文案和卡片布局,不再单独手写 `border-t border-white/10 px-5 py-4`。 - 2026-06-11 追加:`PlatformProfileModalShell` 继续补齐标准 footer 插槽,直接透传 `UnifiedModal.footer` 与 `footerClassName`;`RpgEntryHomeView.tsx` 的昵称修改弹窗已改成标准 profile footer,不再把双按钮动作区手写在 body 末尾。后续个人中心里同类“表单内容 + 底部双按钮”弹窗优先走壳层 footer 接法。 - 2026-06-11 追加:`PlatformProfileModalShell` 的标准 footer 接法继续扩展到单 CTA 表单收尾;`PlatformProfileRewardCodeRedeemModal.tsx` 的兑换按钮已迁到壳层 footer,body 只保留输入和反馈消息。`PlatformAsyncStatePanel` 同日继续扩展到 `PlatformAssetPickerGrid`、`VisualNovelSavePanel.tsx` 与 `AccountModal.tsx` 的账号安全三个子区块;其中公共素材网格继续把 `error` banner 放在状态壳外层,保持错误提示可与加载态或内容并存的原语义。 +- 2026-06-11 追加:按钮层继续补齐轻量漏网项。`PlatformTagEditor.tsx` 的标签 chip 删除入口已改成紧凑 `PlatformIconButton`,保留透明背景和原 chip 高度;`RpgEntryCharacterSelectView.tsx` 的两处“返回”按钮统一沉到局部 `CharacterSelectBackButton`,底层委托 `PlatformActionButton surface="editorDark"`。同日 `GenerationProgressHero.tsx` 新增 `GenerationHeaderBackButton`,`CustomWorldGenerationView.tsx` 与 `BarkBattleGeneratingView.tsx` 已开始复用这套暖色生成页返回入口骨架;后续同类轻量返回按钮与 chip 删除按钮优先继续沿共享按钮 + 薄包装的方向推进。 - 2026-06-09 追加:通用输入 Composer 的上传参考图、发送和移除参考图已迁移到 `PlatformIconButton`;图标上传仍使用 `asChild="label"` 保留 label + file input 语义,公共组件会自动写入隐藏文本,确保内嵌 file input 继承可访问名称。 - 2026-06-10 追加:creation-agent composer 的上传文档 / 上传参考图入口使用 `PlatformIconButton` 默认 `platformIcon`;工作台只保留动态 label、title、busy 状态和 picker 回调,发送按钮继续保留主题色动作布局。验证命令:`npm run test -- src/components/creation-agent/CreationAgentWorkspace.test.tsx src/components/common/PlatformIconButton.test.tsx`。 - 2026-06-10 追加:作品详情顶部返回 / 分享和封面轮播上一张 / 下一张入口使用 `PlatformIconButton variant="platformIcon"`;详情页保留原 `platform-work-detail__*` 局部 class 控制位置和尺寸,点赞、复制三态等专用动作暂不迁移。验证命令:`npm run test -- src/components/platform-entry/PlatformWorkDetailView.test.tsx src/components/common/PlatformIconButton.test.tsx`。 diff --git a/docs/technical/【前端架构】PlatformUiKit弹窗组件收口计划-2026-06-08.md b/docs/technical/【前端架构】PlatformUiKit弹窗组件收口计划-2026-06-08.md index 166d9917..7b5cdb2c 100644 --- a/docs/technical/【前端架构】PlatformUiKit弹窗组件收口计划-2026-06-08.md +++ b/docs/technical/【前端架构】PlatformUiKit弹窗组件收口计划-2026-06-08.md @@ -266,6 +266,8 @@ 19.3.40. `PlatformNavigableListItem` 继续从桌面首页扩展到 profile 设置行:`src/components/platform-entry/PlatformProfilePrimitives.tsx` 里的 `ProfileSettingsRow` 现已统一委托共享 `button + leading + trailing` 骨架,保留本地 `platform-profile-settings-row` class 承接行间分隔、icon 胶囊和字号微调。后续 profile / 账户中心里同类“左图标标题 + 右箭头”的轻量导航行,优先直接复用 `PlatformNavigableListItem`,不要再回退成原生 ` + className="h-3.5 w-3.5 border-0 bg-transparent p-0 opacity-70 shadow-none transition hover:translate-y-0 hover:bg-transparent disabled:opacity-45" + icon={} + /> ))} {normalizedTags.length <= 0 ? ( diff --git a/src/components/rpg-entry/RpgEntryCharacterSelectView.test.tsx b/src/components/rpg-entry/RpgEntryCharacterSelectView.test.tsx index 72ee4cea..21e1722e 100644 --- a/src/components/rpg-entry/RpgEntryCharacterSelectView.test.tsx +++ b/src/components/rpg-entry/RpgEntryCharacterSelectView.test.tsx @@ -103,6 +103,7 @@ afterEach(() => { test('custom world character selection stays stable when character ids are empty', async () => { const user = userEvent.setup(); + const handleBack = vi.fn(); const handleConfirm = vi.fn(); const consoleErrorSpy = vi .spyOn(console, 'error') @@ -201,11 +202,21 @@ test('custom world character selection stays stable when character ids are empty ], }, } as unknown as CustomWorldProfile} - onBack={() => {}} + onBack={handleBack} onConfirm={handleConfirm} />, ); + const backButton = screen.getByRole('button', {name: '返回'}); + expect(backButton.className).toContain('platform-action-button--editor-dark'); + expect(backButton.className).toContain('rounded-full'); + expect(backButton.className).toContain('px-3'); + expect(backButton.className).toContain('py-1.5'); + expect(backButton.className).toContain('text-[11px]'); + + await user.click(backButton); + expect(handleBack).toHaveBeenCalledTimes(1); + expect(screen.getByText(/潮骨:/u)).toBeTruthy(); expect(screen.queryByText(/力量:/u)).toBeNull(); @@ -235,7 +246,9 @@ test('custom world character selection stays stable when character ids are empty expect(duplicateKeyCalls).toHaveLength(0); }); -test('custom world character selection falls back instead of rendering a blank screen when profile characters are malformed', () => { +test('custom world character selection falls back instead of rendering a blank screen when profile characters are malformed', async () => { + const user = userEvent.setup(); + const handleBack = vi.fn(); vi.spyOn(console, 'warn').mockImplementation(() => undefined); vi.mocked(buildCustomWorldPlayableCharacters).mockImplementation(() => { throw new TypeError('profile.playableNpcs is not iterable'); @@ -268,7 +281,7 @@ test('custom world character selection falls back instead of rendering a blank s ], }, } as unknown as CustomWorldProfile} - onBack={() => {}} + onBack={handleBack} onConfirm={() => {}} />, ); @@ -276,4 +289,7 @@ test('custom world character selection falls back instead of rendering a blank s expect(screen.getByText('选择你的角色')).toBeTruthy(); expect(screen.getAllByText('兜底侠').length).toBeGreaterThan(0); expect(screen.getByRole('button', { name: /进入营地/u })).toBeTruthy(); + + await user.click(screen.getByRole('button', {name: '返回'})); + expect(handleBack).toHaveBeenCalledTimes(1); }); diff --git a/src/components/rpg-entry/RpgEntryCharacterSelectView.tsx b/src/components/rpg-entry/RpgEntryCharacterSelectView.tsx index 8f3be02f..8d12f052 100644 --- a/src/components/rpg-entry/RpgEntryCharacterSelectView.tsx +++ b/src/components/rpg-entry/RpgEntryCharacterSelectView.tsx @@ -20,6 +20,7 @@ import { import { getNineSliceStyle, UI_CHROME } from '../../uiAssets'; import { CharacterAnimator } from '../CharacterAnimator'; import { CharacterDetailModal } from '../CharacterDetailModal'; +import { PlatformActionButton } from '../common/PlatformActionButton'; import { ResolvedAssetImage } from '../ResolvedAssetImage'; import { CharacterDraftModal } from '../SelectionCustomizationModals'; @@ -215,6 +216,21 @@ function getCharacterCardStyle(index: number, progress: number) { }; } +function CharacterSelectBackButton({onBack}: {onBack: () => void}) { + return ( + + 返回 + + ); +} + export function RpgEntryCharacterSelectView({ worldType, customWorldProfile, @@ -344,13 +360,7 @@ export function RpgEntryCharacterSelectView({ if (!selectedCharacter || !selectedCharacterMeta) { return (
- +
角色数据暂不可用
); @@ -360,13 +370,7 @@ export function RpgEntryCharacterSelectView({ <>
- +
选择你的角色