修复排队消息 chip 的 @ 引用露出内部 resourceId(review 第 2 条)
- queuedChatTurnLabel 增加 assets 入参并透传给 directCodexContentToPromptText,@ 引用按 manifest 显示名展开,与聊天输入区文案口径一致 - ComposerTurnQueue 增加 assets 属性,ProjectSupervisorView 把 chatProjectAssets 传下去 - chat-composer 用例新增「队列 chip 的 @ 引用按 manifest 显示名展开」,钉住显示名文案与不传清单时的稳定 id 兜底
This commit is contained in:
@@ -19,6 +19,7 @@ import {
|
||||
import type { RefObject } from 'react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import type { GameCreationAppAssetManifestEntry } from '../../../../../packages/shared/src/contracts/gameCreationApp';
|
||||
import { resolveTauriInvoke } from '../../app/tauri';
|
||||
import type {
|
||||
GameCreatorAppConfigView,
|
||||
@@ -192,9 +193,12 @@ export function ComposerPendingAttachments({
|
||||
/** 队列 chip:回合运行中入队的消息,按 FIFO 顺序展示,可单条取消。 */
|
||||
export function ComposerTurnQueue({
|
||||
turns,
|
||||
assets,
|
||||
onCancel,
|
||||
}: {
|
||||
turns: readonly QueuedChatTurn[];
|
||||
/** 与聊天输入区同源的素材清单:chip 文案里的 `@` 引用按它展开成显示名。 */
|
||||
assets?: readonly GameCreationAppAssetManifestEntry[];
|
||||
onCancel: (id: string) => void;
|
||||
}) {
|
||||
if (turns.length === 0) {
|
||||
@@ -211,11 +215,11 @@ export function ComposerTurnQueue({
|
||||
{index + 1}
|
||||
</span>
|
||||
<span className="project-supervisor-composer-queue-text">
|
||||
{queuedChatTurnLabel(turn)}
|
||||
{queuedChatTurnLabel(turn, assets)}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
aria-label={`取消排队消息 ${queuedChatTurnLabel(turn)}`}
|
||||
aria-label={`取消排队消息 ${queuedChatTurnLabel(turn, assets)}`}
|
||||
title="取消这条排队消息"
|
||||
onClick={() => onCancel(turn.id)}
|
||||
>
|
||||
|
||||
@@ -889,6 +889,7 @@ export function ProjectSupervisorView({
|
||||
>
|
||||
<ComposerTurnQueue
|
||||
turns={directCodex ? queuedTurns : []}
|
||||
assets={chatProjectAssets}
|
||||
onCancel={(id) => onCancelQueuedTurn?.(id)}
|
||||
/>
|
||||
<ComposerPendingAttachments
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* 回合运行中用户再次发送时,消息进入 FIFO 队列而不是被丢弃;当前回合结束后按入队顺序
|
||||
* 依次发出。队列项能在输入盒上方单独取消。这里只放与 React 无关的纯逻辑,便于单测。
|
||||
*/
|
||||
import type { GameCreationAppAssetManifestEntry } from '../../../../../packages/shared/src/contracts/gameCreationApp';
|
||||
import type { DirectCodexUserItem } from './generated';
|
||||
import { directCodexContentToPromptText } from './resourceReferences';
|
||||
|
||||
@@ -73,9 +74,17 @@ export function chatQueueFullNotice(): string {
|
||||
return `队列已满(最多 ${MAX_QUEUED_CHAT_TURNS} 条),请等当前回合结束后再发送`;
|
||||
}
|
||||
|
||||
/** 队列 chip 上显示的文字:单行、有长度上限。 */
|
||||
export function queuedChatTurnLabel(turn: QueuedChatTurn): string {
|
||||
const text = directCodexContentToPromptText(turn.userItem.content)
|
||||
/**
|
||||
* 队列 chip 上显示的文字:单行、有长度上限。
|
||||
*
|
||||
* `assets` 必须与聊天输入区同源(`manifest.assets`):`@` 引用要按显示名展开,否则
|
||||
* chip 上会露出 `@asset:…` 这样的内部 id,与用户输入时看到的 `@显示名` 对不上。
|
||||
*/
|
||||
export function queuedChatTurnLabel(
|
||||
turn: QueuedChatTurn,
|
||||
assets: readonly GameCreationAppAssetManifestEntry[] = [],
|
||||
): string {
|
||||
const text = directCodexContentToPromptText(turn.userItem.content, assets)
|
||||
.trim()
|
||||
.replace(/\s+/gu, ' ');
|
||||
if (text) {
|
||||
|
||||
@@ -196,6 +196,37 @@ function queuedTurn(
|
||||
}
|
||||
|
||||
export function registerChatComposerControlTests() {
|
||||
it('队列 chip 的 @ 引用按 manifest 显示名展开,不露出内部 resourceId', () => {
|
||||
const turn = createQueuedChatTurn({
|
||||
id: 'turn-ref',
|
||||
clientTurnId: 'client-ref',
|
||||
userItem: directCodexUserItemFromContent(
|
||||
[
|
||||
{ type: 'input_text', text: '用这张图改一下' },
|
||||
{ type: 'agc_resource_reference', resourceId: 'asset:hero' },
|
||||
],
|
||||
'client-ref:user',
|
||||
),
|
||||
createdAt: 1,
|
||||
});
|
||||
const manifestAssets = [
|
||||
{
|
||||
id: 'asset:hero',
|
||||
kind: 'character',
|
||||
mediaType: 'image/png',
|
||||
localPath: 'assets/hero.png',
|
||||
source: { kind: 'uploaded' as const },
|
||||
},
|
||||
];
|
||||
|
||||
// chip 文案与聊天输入区同口径:用户看到的是 `@显示名`,不是 `@内部 id`。
|
||||
expect(queuedChatTurnLabel(turn, manifestAssets)).toBe(
|
||||
'用这张图改一下@hero',
|
||||
);
|
||||
// 素材清单不传(老调用方)时退回稳定 id,不抛错。
|
||||
expect(queuedChatTurnLabel(turn)).toBe('用这张图改一下@asset:hero');
|
||||
});
|
||||
|
||||
it('keeps queued chat turns in FIFO order and drops only the cancelled one', () => {
|
||||
const first = queuedTurn('turn-1', 'client-1', '第一条', 1);
|
||||
const second = queuedTurn('turn-2', 'client-2', '第二条', 2);
|
||||
|
||||
Reference in New Issue
Block a user