显示图标规范输入字符计数

在一键优化与撤销操作行右侧实时显示 Unicode 字符数和 200 字符上限。

补充计数更新、emoji 截断和右对齐布局测试,并同步编辑器方案文档。
This commit is contained in:
2026-08-06 17:19:41 +08:00
parent 969afbaf94
commit fa02c366a9
3 changed files with 20 additions and 1 deletions
@@ -40,7 +40,7 @@
- 生成视频:`你希望生成什么视频?`
8. 多输入框面板必须保留每个字段标题和输入框边界,例如生成规范。图标素材生成不再使用多描述列表,改为复用角色形象生成面板同款单文本输入框;该完整文本按 Unicode 字符限制为 `200`,输入时按 code point 截断,不能用 UTF-16 `maxLength` 误截 emoji。
9. 生成规范下的角色规范、图标规范和自定义规范都使用同一生成类 shell:首行参考图区域、中央字段区、底部生成按钮区,不再出现缺首行参考区或单独 footer 样式。
10. 图标规范只使用 `specType="icon"`,历史 `specType="ui"` 快照在恢复边界迁移为 `icon`。表单字段使用 `playSetting / artStyle`,界面标题继续使用「玩法设定 / 美术风格」。两项初始为空且必填;每项独立支持一键优化、处理中锁定自身、成功后单次撤销,手工编辑后立即清除该字段已经失效的撤销快照,失败只保留当前文本与仍然有效的旧撤销快照。LLM 返回空文本、超长文本或 Markdown / 结构化内容时,后续有界重试必须携带上次无效输出和对应修正要求,不能原样重放同一请求。优化请求必须绑定发起时的生成对象 ID 和请求代次;活动对象身份只在 React effect 提交后更新,并在 cleanup 中失效,丢弃的并发 render 不得改变请求归属;对象切换或新请求取代旧请求后,旧成功或失败结果都不得更新当前面板。任一项处理中或任一项为空时禁用生成。控件继续使用平台默认样式,不新增图标规范专属 CSS。
10. 图标规范只使用 `specType="icon"`,历史 `specType="ui"` 快照在恢复边界迁移为 `icon`。表单字段使用 `playSetting / artStyle`,界面标题继续使用「玩法设定 / 美术风格」。两项初始为空且必填;每项独立支持一键优化、处理中锁定自身、成功后单次撤销,操作行最右侧按 Unicode 字符实时显示 `当前数/200`手工编辑后立即清除该字段已经失效的撤销快照,失败只保留当前文本与仍然有效的旧撤销快照。LLM 返回空文本、超长文本或 Markdown / 结构化内容时,后续有界重试必须携带上次无效输出和对应修正要求,不能原样重放同一请求。优化请求必须绑定发起时的生成对象 ID 和请求代次;活动对象身份只在 React effect 提交后更新,并在 cleanup 中失效,丢弃的并发 render 不得改变请求归属;对象切换或新请求取代旧请求后,旧成功或失败结果都不得更新当前面板。任一项处理中或任一项为空时禁用生成。控件继续使用平台默认样式,不新增图标规范专属 CSS。
11. 图标规范最终生成改走 `POST /api/editor/icon-specs/generations`。前端只提交业务字段和统一参考图 / 项目完成包络,不拼最终 prompt,不提交 `kind / assetKind / ExtraParam`;后端固定图片参数。HTTP handler 先调用可复用的图片请求预检,完成参考图稳定性、owner 授权、Provider 配置和运行时定价校验;全部通过后才调用文本 LLM 补齐 `ExtraParam` 和最终 prompt,再把完整图片请求交给既有 `editor_image_generation` inline / queue 分流。不得为图标规范新增独立外部任务类型;最终 worker 仍按执行时事实重新校验,避免排队期间状态变化产生 TOCTOU。
12. 图片快速编辑不展示额外参考图入口;原图或绘制了红框和序号的标注图始终作为 `/api/editor/images/edits``sourceImageSrc` 直接提交,不作为 `referenceImageSrcs`
13. 快速编辑打开后,画布视口应调整到原图完整展示,且面板位于原图下方并不遮挡原图;原图右侧显示竖向框选工具,支持矩形、椭圆和画笔自由框选。快速编辑进入时不默认启用框选工具,点击工具后出现选中态并保持高亮,再点同一工具取消启用;红色圈选框使用细描边。每完成一次框选,红色圈选框按完成顺序标注 `1 / 2 / 3...`,并在快速编辑提示词中追加一行 `对N号红色圈选框里的内容做以下修改:`
@@ -355,6 +355,15 @@ describe('ImageCanvasSpecGenerationPanelView', () => {
'玩法设定',
) as HTMLTextAreaElement;
const artStyle = screen.getByLabelText('美术风格') as HTMLTextAreaElement;
expect(screen.getByLabelText('玩法设定字符计数').textContent).toBe(
'5/200',
);
expect(screen.getByLabelText('美术风格字符计数').textContent).toBe(
'4/200',
);
expect(screen.getByLabelText('玩法设定字符计数').className).toContain(
'ml-auto',
);
expect(playSetting.hasAttribute('maxlength')).toBe(false);
expect(artStyle.hasAttribute('maxlength')).toBe(false);
@@ -362,6 +371,9 @@ describe('ImageCanvasSpecGenerationPanelView', () => {
expect(Array.from(playSetting.value)).toHaveLength(200);
fireEvent.change(playSetting, { target: { value: '🎮'.repeat(201) } });
expect(Array.from(playSetting.value)).toHaveLength(200);
expect(screen.getByLabelText('玩法设定字符计数').textContent).toBe(
'200/200',
);
fireEvent.click(screen.getByRole('button', { name: '提交生成规范' }));
expect(onSubmit).toHaveBeenCalledTimes(1);
@@ -554,6 +554,13 @@ export function ImageCanvasSpecGenerationPanelView({
</PlatformActionButton>
) : null}
<span
className="ml-auto text-[10px] leading-4 tabular-nums text-slate-400"
aria-label={`${item.title}字符计数`}
>
{getIconSpecPromptLength(value)}/
{EDITOR_ICON_SPEC_PROMPT_MAX_LENGTH}
</span>
</div>
</label>
);