收口作品详情分享反馈

将作品详情分享复制反馈迁移到 PlatformStatusMessage

按分享复制状态映射成功和错误提示外观

补充分享失败状态断言

更新 PlatformUiKit 文档和 Hermes 决策记录
This commit is contained in:
2026-06-10 14:12:24 +08:00
parent eff95886ad
commit 876fd37ce4
5 changed files with 52 additions and 18 deletions

View File

@@ -101,6 +101,7 @@
- 2026-06-10 追加RPG 世界详情页的发布状态、主题、作者、发布时间 / 可见性和展示标签等静态元信息 chip 迁移到 `PlatformPillBadge`;作品号复制和分享入口仍保留 `CopyCodeButton` / `CopyFeedbackButton` 管复制状态。
- 2026-06-10 追加:`CopyFeedbackButton` 支持 `actionAppearance="pill"``CopyCodeButton` 透传同一入口,并复用 `platformPillBadgeModel.ts``getPlatformPillBadgeClassName` 视觉 chrome可点击复制 / 分享胶囊 chip 不再在业务 JSX 中手写 `platform-pill`RPG 世界详情作品号复制 / 分享入口和抓大鹅批量新增 / 重生成物品名称预览已迁移。
- 2026-06-10 追加:平台作品详情页主题标签使用 `PlatformPillBadge tone="neutralSolid" size="sm"`,作品号复制按钮使用 `CopyCodeButton actionAppearance="pill" actionPillTone="neutralSolid" actionPillSize="sm"`;详情页只保留标签映射、作品号复制状态和顶部外边距,不再手写 `platform-work-detail__chip / code` 基础 chrome。验证命令`npm run test -- src/components/platform-entry/PlatformWorkDetailView.test.tsx src/components/common/PlatformPillBadge.test.tsx src/components/common/CopyCodeButton.test.tsx`
- 2026-06-10 追加:平台作品详情页分享复制反馈使用 `PlatformStatusMessage surface="platform"`,按 `shareState` 映射 `success / error`;详情页保留 `useCopyFeedback` 状态机和文案,不再让失败态复用成功 toast chrome。验证命令`npm run test -- src/components/platform-entry/PlatformWorkDetailView.test.tsx src/components/common/PlatformStatusMessage.test.tsx`
- 2026-06-10 追加:`PlatformPillBadge` 支持 `darkSoft` / `darkNeutral` / `darkSky` / `darkEmerald` / `darkAmber` / `darkRose` 暗色 tone用于 RPG 暗色弹窗和角色详情里的纯展示 chip角色身份 / 等级、技能列表出手方式、技能详情方式 / 风格 / 状态标签、地图节点方向标签、地图场景切换方向标签和营地编组状态数值已迁移。暗色动作按钮、runtime HUD、属性加成动态 pill 和按钮内部消耗 chip 暂不直接套静态 badge。
- 2026-06-10 追加:背景故事已解锁 / 需好感状态和好感等级 badge 也使用 `PlatformPillBadge``dark*` tone好感进度时间轴刻度、runtime HUD 和带点击卡片视觉的标签仍保留专用布局。
- 2026-06-10 追加RPG 角色资产工作室动作列表的生成中 / 已生成 / 待生成状态 chip 直接使用 `PlatformPillBadge``darkAmber` / `darkEmerald` / `darkNeutral` tone父弹窗不再维护本地 `StatusBadge` 浅封装,动作生成按钮仍保留工作室专用暗色按钮布局。
@@ -230,6 +231,7 @@
- 2026-06-10 验证补充:作品详情顶部和封面轮播图标按钮收口补跑 `npm run test -- src/components/platform-entry/PlatformWorkDetailView.test.tsx src/components/common/PlatformIconButton.test.tsx`
- 2026-06-10 验证补充:作品详情底部启动 / 改造动作收口补跑 `npm run test -- src/components/platform-entry/PlatformWorkDetailView.test.tsx src/components/common/PlatformActionButton.test.tsx`
- 2026-06-10 验证补充:平台作品详情主题标签和作品号复制 chip 收口后,补跑 `npm run test -- src/components/platform-entry/PlatformWorkDetailView.test.tsx src/components/common/PlatformPillBadge.test.tsx src/components/common/CopyCodeButton.test.tsx`
- 2026-06-10 验证补充:平台作品详情分享复制反馈按状态映射到 `PlatformStatusMessage surface="platform"` 后,补跑 `npm run test -- src/components/platform-entry/PlatformWorkDetailView.test.tsx src/components/common/PlatformStatusMessage.test.tsx`
- 2026-06-10 验证补充:大鱼吃小鱼结果页缺草稿空态收口补跑 `npm run test -- src/components/big-fish-result/BigFishResultView.test.tsx src/components/common/PlatformEmptyState.test.tsx`
- 2026-06-10 验证补充:大鱼吃小鱼结果页发布校验阻断项收口补跑 `npm run test -- src/components/big-fish-result/BigFishResultView.test.tsx src/components/common/PlatformStatusMessage.test.tsx`
- 2026-06-09 验证补充:通用输入 Composer 面板、文本域和读图错误状态收口补跑 `npm run test -- src/components/creative-agent/CreativeAgentInputComposer.test.tsx src/components/common/PlatformTextField.test.tsx src/components/common/PlatformStatusMessage.test.tsx src/components/common/PlatformSubpanel.test.tsx`

View File

@@ -62,10 +62,11 @@
- `CopyFeedbackButton`:接收 `state``idleLabel``copiedLabel``failedLabel`、三态图标、`showIcon``showLabel``labelClassName``accessibleLabel``actionSurface``actionTone``actionSize``actionFullWidth``actionAppearance="pill"``actionPillTone``actionPillSize`文本按钮、chip 按钮和运行态纯图标分享按钮都应走同一 Module。需要平台主按钮外观时通过 `actionSurface="platform"``actionSurface="profile"` 复用 `PlatformActionButton` 样式,不在业务 JSX 中传整串 `platform-button` class需要可点击胶囊复制 / 分享 chip 时用 `actionAppearance="pill"` 复用 `PlatformPillBadge` chrome不在业务 JSX 中传 `platform-pill`
- `CopyCodeButton`:接收 `state``code``codeLabel``copiedSuffix``failedSuffix``codeClassName``suffixClassName``actionAppearance="pill"``actionPillTone``actionPillSize` 和复制按钮透传属性;作品号、用户号等短代码 chip 优先用它,不在业务 JSX 中重复写 `{code} + 已复制 / 复制失败` fragments也不直接传 `platform-pill` class。
- `CopyCodeButton` 补充:作品详情页作品号复制按钮使用 `actionAppearance="pill" actionPillTone="neutralSolid" actionPillSize="sm"`;详情页只保留顶部外边距和复制回调,不再把代码 chip 基础 chrome 写在 `platform-work-detail__code`
- `CopyFeedbackMessage`:接收 `state``copiedLabel``failedLabel`toast 或行内状态只展示成功 / 失败时使用,不在业务页手写三态分支。
- `CopyFeedbackMessage`:接收 `state``copiedLabel``failedLabel`toast 或行内状态只展示成功 / 失败时使用,不在业务页手写三态分支。若场景需要按成功 / 失败切换状态条色值,可在业务壳层继续使用 `useCopyFeedback` 状态机,并组合 `PlatformStatusMessage` 渲染对应 tone。
- `PlatformStatusMessage`:接收 `tone="error" | "success" | "info" | "warning" | "neutral"``surface="light" | "tinted" | "platform" | "profile" | "editorDark"``size="xs" | "sm" | "md"``remapSurface``children``className`;根节点固定带 `platform-status-message` 稳定类名,业务测试可断言公共状态条接入。局部可覆盖圆角、外边距和网格布局,但状态色值、基础内边距和字号由 Module 统一控制。结果页、发布检查、素材生成面板和 creation-agent composer 错误条等需要复用旧 `platform-banner--danger / success / info / warning / neutral` token 外观时使用 `surface="platform"`;需要在局部 platform token 作用域内重映射 CSS 变量的提示条传 `remapSurface`,不在业务 JSX 手写 `platform-remap-surface platform-banner`。个人中心弹窗、认证入口、验证码提示、统一创作工作台和通用创作输入区需要 profile token 外观时使用 `surface="profile"`RPG 暗色编辑 / 运行面板内的普通状态提示使用 `surface="editorDark"`;背包故事档案 QA、NPC 叙事提示、角色聊天错误提示、营地编组战斗中提示、自定义选择弹窗错误 / 生成中提示等暗色状态条已迁移。旧 `platform-profile-error` / `platform-profile-success`、暗色手写 `border-*-300/15 bg-*-500/10 text-*-50/90``platform-banner--danger / success / info / warning / neutral` 不再作为业务 JSX 接口。
- `PlatformStatusMessage` 补充:大鱼吃小鱼结果页发布校验阻断项使用 `tone="warning" surface="platform" size="xs"`;结果页只保留阻断项裁剪和文案,不再手写 amber 文本列表。
- `PlatformStatusMessage` 补充:拼图首访 onboarding 的输入错误和登录保存错误使用 `surface="editorDark"`onboarding 只保留错误文案和条件渲染,不再手写暗色红色错误条。
- `PlatformStatusMessage` 补充:平台作品详情页分享复制反馈使用 `surface="platform"` 并按 `shareState` 映射 `success / error`;详情页只保留复制状态机和文案,不再为失败态复用成功 toast chrome。
- `PlatformRuntimeStatusToast`:接收 `tone="error" | "success" | "info" | "warning" | "neutral"``surface="light" | "dark" | "solid"``size="xs" | "sm" | "md"``shape="pill" | "rounded"``children``className`;根节点固定带 `platform-runtime-status-toast` 稳定类名,默认按 `tone` 写入 `role="alert/status"``aria-live`。它只承接运行态 HUD 中短错误、成功和反馈 chip 的圆角、字号、阴影、色值和可访问语义,具体浮层位置、玩法资产按钮、计分牌、蓄力提示、强品牌 primary 按钮仍由玩法 runtime 控制。跳一跳、拼图、敲木鱼、方洞和宝贝爱画运行态的短错误 / 成功 / 投放反馈已先迁移;后续同类短 toast 不再手写 `rounded-full bg-white/* text-*`、暗色 `border-rose/emerald bg-*/text-*` 或单玩法 `*-runtime-error-chip`
- `PlatformDarkOptionCard`:接收 `selected``tone="emerald" | "sky" | "rose" | "amber"``radius="sm" | "md" | "lg"``padding="sm" | "md" | "lg"``children``className` 和原生 button props根节点固定带 `platform-dark-option-card` 稳定类名,统一承接 RPG 暗色弹窗 / 面板中的 selected / idle / hover / disabled 可选项卡按钮外观。NPC 交易模式、交易物品行、赠礼候选、招募替换候选、角色素材工作室动作预览格、营地编组替换位按钮和角色聊天建议按钮已先迁移;业务页只保留选中判断、点击回调和内容布局,不再重复手写 `rounded-* border px-3 py-*``border-*-400/* bg-*-500/10``border-white/* bg-black/20 hover:border-white/15`
- `PlatformEmptyState`:接收 `surface="soft" | "dashed" | "subpanel" | "editorDark"``size="compact" | "panel" | "inline"``tone="base" | "soft"``children``className`;根节点固定带 `platform-empty-state` 稳定类名,业务测试可断言公共空态接入。`soft + compact` 用于公开广场、排行和作品架内的轻量空态,`soft + panel` 用于创作中心作品架整块空态,`dashed + panel` 用于素材选择、历史资源等弹窗的大面积空态或读取态,`subpanel + inline` 用于视觉小说 runtime、个人中心充值 / 任务等白底子面板内的无操作空态,`editorDark + compact/inline` 用于 RPG 大编辑器、实体详情弹窗、营地编组、角色聊天和运行态设置弹窗等暗色面板里的纯展示空态 / 禁用提示。组件只承接外观,不内置业务文案。
@@ -150,7 +151,7 @@
12. RPG 场景背景和作品封面生成结果未保存时,退出确认也使用 `UnifiedConfirmDialog`;像素风场景生成弹窗通过 `variant="pixel"` 适配视觉。
13. 公开作品详情或运行态深链失效时,由平台入口壳展示 `UnifiedConfirmDialog` 的“作品不可用”提示;用户确认后再回到首页,错误处理分支不再调用浏览器原生 `window.alert`
14. 带复杂内容的专用 Module 可以保留自己的布局,但复制反馈仍应复用 `useCopyFeedback`;如果有可点击复制按钮,优先复用 `CopyFeedbackButton`;如果只展示复制结果提示,优先复用 `CopyFeedbackMessage`
15. 白底平台弹窗、详情页、结果页、目录页、个人页、认证入口、统一创作工作台和通用创作输入区的基础错误 / 成功 / 信息 / 警告 / 中性状态提示逐步迁移到 `PlatformStatusMessage`RPG 结果页、拼图结果页、抓大鹅结果页、跳一跳结果页、敲木鱼结果页、拼消消结果页、宝贝识物结果页、方洞结果页、汪汪声浪结果页、视觉小说结果页、拼消消创作工作台、宝贝识物创作工作台、视觉小说创作工作台、汪汪声浪创作工作台、creative-agent 工作台、creation-agent operation banner、自定义世界实体目录拼消消 runtime 白底错误条已使用 `surface="platform"` 承接发布检查、错误提示、进度提示、素材生成提示、资源未就绪提示主线目标提示;个人中心、认证入口、统一创作工作台和创作输入区需要 profile token 外观时使用 `surface="profile"`RPG 暗色编辑 / 运行面板和拼图首访 onboarding 里的普通错误 / 成功 / 信息 / 警告 / 中性提示使用 `surface="editorDark"`,背包故事档案 QA 提示、NPC 交易 / 赠礼 / 招募叙事提示和角色聊天错误提示已先迁移。运行态里的短错误 / 成功 / 命中反馈 chip 使用 `PlatformRuntimeStatusToast`,位置和玩法强品牌 HUD 仍留在 runtime 壳层;深色半透明游戏内提示和强品牌样式可以暂保留专用布局,避免状态条组件过早承接游戏视觉。
15. 白底平台弹窗、详情页、结果页、目录页、个人页、认证入口、统一创作工作台和通用创作输入区的基础错误 / 成功 / 信息 / 警告 / 中性状态提示逐步迁移到 `PlatformStatusMessage`RPG 结果页、拼图结果页、抓大鹅结果页、跳一跳结果页、敲木鱼结果页、拼消消结果页、宝贝识物结果页、方洞结果页、汪汪声浪结果页、视觉小说结果页、拼消消创作工作台、宝贝识物创作工作台、视觉小说创作工作台、汪汪声浪创作工作台、creative-agent 工作台、creation-agent operation banner、自定义世界实体目录拼消消 runtime 白底错误条和平台作品详情分享复制反馈已使用 `surface="platform"` 承接发布检查、错误提示、进度提示、素材生成提示、资源未就绪提示主线目标提示和复制反馈;个人中心、认证入口、统一创作工作台和创作输入区需要 profile token 外观时使用 `surface="profile"`RPG 暗色编辑 / 运行面板和拼图首访 onboarding 里的普通错误 / 成功 / 信息 / 警告 / 中性提示使用 `surface="editorDark"`,背包故事档案 QA 提示、NPC 交易 / 赠礼 / 招募叙事提示和角色聊天错误提示已先迁移。运行态里的短错误 / 成功 / 命中反馈 chip 使用 `PlatformRuntimeStatusToast`,位置和玩法强品牌 HUD 仍留在 runtime 壳层;深色半透明游戏内提示和强品牌样式可以暂保留专用布局,避免状态条组件过早承接游戏视觉。
16. 正方形图片裁剪的初始居中、边界 clamp 和裁剪矩形类型统一从 `squareImageCropModel` 导入,避免头像裁剪、拼图参考图裁剪等业务页面依赖弹窗组件文件里的 helper。
17. 个人中心的账户充值、泥点账单、每日任务、兑换码、扫码、存档、玩过作品、邀请 / 社区、昵称修改、头像裁剪,以及平台筛选、创作图片预览、认证入口、邀请码弹窗、公开编号搜索结果弹窗、方洞结果页图片素材弹窗、视觉小说结果页资产 / 音频 / 编辑器弹窗、视觉小说 runtime 普通面板、creative-agent 模板确认弹窗、像素风 UnifiedModal 和自定义选择弹窗等圆形关闭按钮迁移到 `PlatformModalCloseButton`;后续新增弹窗关闭按钮先判断是否属于 `profile``profileCompact``floating``floatingPlain``platformIcon``pixel``editorDark` 七类,确有品牌化或运行态 HUD 语义时才保留专用按钮。
17.1. 平台弹窗 header 和普通工具栏里的 `platform-icon-button` 迁移到 `PlatformIconButton`历史图片选择弹窗、RPG 发布检查弹窗、creative-agent 侧边栏关闭 / 外观 / 设置入口、通用输入 Composer 上传 / 发送 / 移除参考图、creation-agent composer 上传文档 / 上传参考图、creation-agent 参考图移除、敲木鱼结果页新增主题标签入口、敲木鱼创作工作台功德词条删除入口、拼图结果页标签生成 / 标签新增 / 关卡详情关闭 / 发布弹窗关闭 / 删除关卡入口、视觉小说结果页素材选择 / 音频生成 / 保存草稿 / 运行配置入口、RPG 首页搜索结果清空入口、方洞结果页形状 / 洞口选项删除入口,以及抓大鹅结果页标签生成 / 标签新增 / 物品素材删除 / 参考图上传入口已先迁移。结果页内的普通平台弹窗关闭入口使用 `PlatformModalCloseButton variant="platformIcon"`;图标上传控件使用 `PlatformIconButton asChild="label"` 保留 label + file input 语义,不改成普通按钮;`PlatformIconButton` 的 label 模式会自动写入隐藏文本,保证内嵌 file input 仍能继承可访问名称。通用创作图片面板中覆盖在图片上的更换主图、移除主图、历史入口短标签按钮和提示词参考图上传入口,抓大鹅封面编辑中覆盖在封面图上的移除入口,以及敲木鱼创作工作台功德词条删除入口使用 `PlatformIconButton variant="surfaceFloating"`,不再手写白底圆形 / 短标签浮动按钮 chrome。运行态 HUD、带复制状态或需要专用交互禁用语义的图标按钮先保留专用布局等对应场景验证时再迁移。
@@ -267,6 +268,7 @@
- `npm run test -- src/components/platform-entry/PlatformWorkDetailView.test.tsx src/components/common/PlatformIconButton.test.tsx`
- `npm run test -- src/components/platform-entry/PlatformWorkDetailView.test.tsx src/components/common/PlatformActionButton.test.tsx`
- `npm run test -- src/components/platform-entry/PlatformWorkDetailView.test.tsx src/components/common/PlatformPillBadge.test.tsx src/components/common/CopyCodeButton.test.tsx`
- `npm run test -- src/components/platform-entry/PlatformWorkDetailView.test.tsx src/components/common/PlatformStatusMessage.test.tsx`
- `npm run test -- src/components/common/PlatformIconBadge.test.tsx`
- `npm run test -- src/components/common/PlatformIconBadge.test.tsx src/components/big-fish-result/BigFishResultView.test.tsx -t "shows publish failures in a dismissible modal"`
- `npm run test -- src/components/common/PlatformIconBadge.test.tsx src/components/common/CreativeImageInputPanel.test.tsx`

View File

@@ -264,7 +264,36 @@ test('PlatformWorkDetailView copies public work code and share text', async () =
expect(clipboardService.copyTextToClipboard).toHaveBeenLastCalledWith(
expect.stringContaining('作品号PZ-001'),
);
expect(await screen.findByText('分享内容已复制')).toBeTruthy();
const shareCopiedMessage = await screen.findByText('分享内容已复制');
expect(shareCopiedMessage.className).toContain('platform-status-message');
expect(shareCopiedMessage.className).toContain(
'bg-[var(--platform-success-bg)]',
);
});
test('PlatformWorkDetailView shows failed share feedback as an error status', async () => {
vi.mocked(clipboardService.copyTextToClipboard).mockResolvedValue(false);
render(
<PlatformWorkDetailView
entry={createPuzzleEntry()}
isBusy={false}
error={null}
onBack={vi.fn()}
onLike={vi.fn()}
onStart={vi.fn()}
onRemix={vi.fn()}
/>,
);
fireEvent.click(screen.getByRole('button', { name: '分享' }));
const shareFailedMessage = await screen.findByText('分享失败');
expect(shareFailedMessage.className).toContain('platform-status-message');
expect(shareFailedMessage.className).toContain(
'bg-[var(--platform-button-danger-fill)]',
);
});
test('PlatformWorkDetailView switches remix action label for owned work edit', () => {

View File

@@ -17,10 +17,10 @@ import { useEffect, useMemo, useState } from 'react';
import type { PublicUserSummary } from '../../../packages/shared/src/contracts/auth';
import { buildPublicWorkDetailUrl } from '../../routing/appPageRoutes';
import { CopyCodeButton } from '../common/CopyCodeButton';
import { CopyFeedbackMessage } from '../common/CopyFeedbackMessage';
import { PlatformActionButton } from '../common/PlatformActionButton';
import { PlatformIconButton } from '../common/PlatformIconButton';
import { PlatformPillBadge } from '../common/PlatformPillBadge';
import { PlatformStatusMessage } from '../common/PlatformStatusMessage';
import { useCopyFeedback } from '../common/useCopyFeedback';
import { ResolvedAssetImage } from '../ResolvedAssetImage';
import {
@@ -454,12 +454,16 @@ export function PlatformWorkDetailView({
copiedIcon={<Copy className="h-4 w-4" />}
/>
) : null}
<CopyFeedbackMessage
state={shareState}
className="platform-work-detail__toast"
copiedLabel="分享内容已复制"
failedLabel="分享失败"
/>
{shareState !== 'idle' ? (
<PlatformStatusMessage
tone={shareState === 'copied' ? 'success' : 'error'}
surface="platform"
size="sm"
className="platform-work-detail__toast"
>
{shareState === 'copied' ? '分享内容已复制' : '分享失败'}
</PlatformStatusMessage>
) : null}
</section>
</div>

View File

@@ -8698,26 +8698,23 @@ button {
margin-top: 1.3rem;
}
.platform-work-detail__error,
.platform-work-detail__toast {
.platform-work-detail__error {
margin-top: 1rem;
border-radius: 0.85rem;
padding: 0.8rem 0.9rem;
font-size: 0.875rem;
}
.platform-work-detail__toast {
margin-top: 1rem;
}
.platform-work-detail__error {
border: 1px solid var(--platform-button-danger-border);
background: var(--platform-button-danger-fill);
color: var(--platform-button-danger-text);
}
.platform-work-detail__toast {
border: 1px solid var(--platform-success-border);
background: var(--platform-success-bg);
color: var(--platform-success-text);
}
.platform-work-detail__bottom {
position: sticky;
bottom: 0;