Q144: each message says where the reply it asked for stands
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
075dea725a
commit
2a89d1e883
18 files changed
+211
-5
No files matched your search
@@ -50,6 +50,13 @@ export const zh: Record<string, string> = {
|
||||
"Send reply": "发送回复",
|
||||
"Earlier messages could not load: {reason}": "更早的消息未能加载:{reason}",
|
||||
"This is the start of the conversation.": "这是会话的开头。",
|
||||
"{agent} will reply; queued.": "{agent} 将回复,已排队。",
|
||||
"{agent} is replying…": "{agent} 正在回复…",
|
||||
"{agent} is waiting: {reason} Resumes {time}.": "{agent} 正在等待:{reason} 将于 {time} 恢复。",
|
||||
"{agent} is waiting: {reason}": "{agent} 正在等待:{reason}",
|
||||
"{agent} did not reply: {reason}": "{agent} 未回复:{reason}",
|
||||
"{agent}'s reply failed: {reason}": "{agent} 的回复失败:{reason}",
|
||||
"{agent}'s reply was stopped.": "{agent} 的回复已停止。",
|
||||
"Resume": "恢复",
|
||||
"Missing this run": "本次运行缺少的能力",
|
||||
"Disconnect": "断开连接",
|
||||
|
||||
@@ -238,6 +238,7 @@ export function ChatPage({ snapshot, actions }: PageProps) {
|
||||
participants={participants}
|
||||
messagesInSpace={messagesInSpace}
|
||||
threads={snapshot.chatThreads}
|
||||
replyStatuses={snapshot.chatReplyStatuses}
|
||||
pendingApprovals={pendingApprovalsInSpace}
|
||||
attachments={snapshot.chatAttachments}
|
||||
mentions={mentionsAll}
|
||||
|
||||
@@ -14,7 +14,7 @@ import { ChatComposer, type Draft, type SendState } from "./ChatComposer";
|
||||
import { ChatTimeline } from "./ChatTimeline";
|
||||
import { ThreadPanel } from "./ThreadPanel";
|
||||
import { buildThreadLinks } from "./thread-links";
|
||||
import type { SpaceHistoryPage } from "../../../../shared/chat-domain";
|
||||
import type { ChatReplyStatus, SpaceHistoryPage } from "../../../../shared/chat-domain";
|
||||
|
||||
/** Older messages fetched per "Show earlier messages". */
|
||||
const HISTORY_PAGE = 150;
|
||||
@@ -26,6 +26,7 @@ export function ChatConversation({
|
||||
participants,
|
||||
messagesInSpace,
|
||||
threads,
|
||||
replyStatuses,
|
||||
pendingApprovals,
|
||||
attachments,
|
||||
mentions,
|
||||
@@ -53,6 +54,7 @@ export function ChatConversation({
|
||||
participants: string[];
|
||||
messagesInSpace: ChatMessageRecord[];
|
||||
threads: ChatThreadRecord[];
|
||||
replyStatuses: ChatReplyStatus[];
|
||||
pendingApprovals: ApprovalRecord[];
|
||||
attachments: ChatAttachmentRecord[];
|
||||
mentions: ChatMentionRecord[];
|
||||
@@ -130,6 +132,10 @@ export function ChatConversation({
|
||||
const pagedIds = useMemo(() => new Set((older?.messages ?? []).map((message) => message.id)), [older]);
|
||||
const allAttachments = useMemo(() => [...(older?.attachments ?? []), ...attachments.filter((item) => !pagedIds.has(item.messageId))], [older, attachments, pagedIds]);
|
||||
const allMentions = useMemo(() => [...(older?.mentions ?? []), ...mentions.filter((item) => !pagedIds.has(item.messageId))], [older, mentions, pagedIds]);
|
||||
const allReplyStatuses = useMemo(
|
||||
() => [...(older?.replyStatuses ?? []), ...replyStatuses.filter((item) => !pagedIds.has(item.messageId))],
|
||||
[older, replyStatuses, pagedIds],
|
||||
);
|
||||
const allSlashInvocations = useMemo(
|
||||
() => [...(older?.slashInvocations ?? []), ...slashInvocations.filter((item) => !pagedIds.has(item.messageId))],
|
||||
[older, slashInvocations, pagedIds],
|
||||
@@ -214,6 +220,7 @@ export function ChatConversation({
|
||||
historyError={historyError}
|
||||
onLoadOlder={loadOlder}
|
||||
threadLinks={threadLinks}
|
||||
replyStatuses={allReplyStatuses}
|
||||
onOpenThread={setOpenThreadId}
|
||||
onApprove={onApprove}
|
||||
onDecline={onDecline}
|
||||
|
||||
@@ -10,6 +10,7 @@ import type {
|
||||
import { formatTime } from "../types";
|
||||
import { messageAttachments, senderName, type MessageContext } from "./chat-helpers";
|
||||
import type { ThreadLink } from "./thread-links";
|
||||
import type { ChatReplyStatus } from "../../../../shared/chat-domain";
|
||||
import { t } from "../../i18n/i18n";
|
||||
|
||||
export function ChatMessageRow({
|
||||
@@ -21,6 +22,7 @@ export function ChatMessageRow({
|
||||
context,
|
||||
canModify = false,
|
||||
thread,
|
||||
replyStatuses = [],
|
||||
onOpenThread,
|
||||
onEdit,
|
||||
onDelete,
|
||||
@@ -35,6 +37,8 @@ export function ChatMessageRow({
|
||||
canModify?: boolean;
|
||||
/** Where the message sits in its thread; absent inside the thread view itself. */
|
||||
thread?: ThreadLink;
|
||||
/** Where each agent's reply to this message stands; a finished reply shows nothing. */
|
||||
replyStatuses?: ChatReplyStatus[];
|
||||
onOpenThread?: () => void;
|
||||
onEdit?: (bodyMd: string) => void;
|
||||
onDelete?: () => void;
|
||||
@@ -124,6 +128,11 @@ export function ChatMessageRow({
|
||||
{thread.replyCount === 1 ? t("1 reply") : t("{count} replies", { count: String(thread.replyCount) })}
|
||||
</button>
|
||||
)}
|
||||
{replyStatuses.filter((status) => status.state !== "replied").map((status) => (
|
||||
<div key={`${status.agentId}-${status.state}`} role="status" style={{ fontSize: 12, color: status.state === "failed" || status.state === "expired" ? "var(--red)" : "var(--text-3)", marginTop: 4 }}>
|
||||
{replyStatusText(status, context)}
|
||||
</div>
|
||||
))}
|
||||
{readers.length > 0 && (
|
||||
<div style={{ fontSize: 12, color: "var(--text-3)", marginTop: 4 }}>
|
||||
{t("Read by {names}", { names: readers.join(", ") })}
|
||||
@@ -134,6 +143,20 @@ export function ChatMessageRow({
|
||||
);
|
||||
}
|
||||
|
||||
function replyStatusText(status: ChatReplyStatus, context: MessageContext): string {
|
||||
const agent = context.agents.find((item) => item.id === status.agentId)?.name ?? t("Former member");
|
||||
if (status.state === "queued") return t("{agent} will reply; queued.", { agent });
|
||||
if (status.state === "running") return t("{agent} is replying…", { agent });
|
||||
if (status.state === "waiting") {
|
||||
return status.resumesAt
|
||||
? t("{agent} is waiting: {reason} Resumes {time}.", { agent, reason: status.detail, time: formatTime(status.resumesAt) })
|
||||
: t("{agent} is waiting: {reason}", { agent, reason: status.detail });
|
||||
}
|
||||
if (status.state === "expired") return t("{agent} did not reply: {reason}", { agent, reason: status.detail });
|
||||
if (status.state === "failed") return t("{agent}'s reply failed: {reason}", { agent, reason: status.detail });
|
||||
return t("{agent}'s reply was stopped.", { agent });
|
||||
}
|
||||
|
||||
function renderBody(body: string, mentions: ChatMentionRecord[], context: MessageContext): ReactNode {
|
||||
if (mentions.length === 0) return formatInline(body, "b");
|
||||
const parts: ReactNode[] = [];
|
||||
|
||||
@@ -10,6 +10,7 @@ import type {
|
||||
} from "../../../../shared/domain";
|
||||
import { ChatMessageRow } from "./ChatMessageRow";
|
||||
import type { ThreadLink } from "./thread-links";
|
||||
import type { ChatReplyStatus } from "../../../../shared/chat-domain";
|
||||
import { formatDay, type MessageContext } from "./chat-helpers";
|
||||
import { t } from "../../i18n/i18n";
|
||||
|
||||
@@ -42,6 +43,7 @@ export function ChatTimeline({
|
||||
historyError,
|
||||
onLoadOlder,
|
||||
threadLinks,
|
||||
replyStatuses,
|
||||
onOpenThread,
|
||||
onApprove,
|
||||
onDecline,
|
||||
@@ -62,6 +64,7 @@ export function ChatTimeline({
|
||||
historyError: string | null;
|
||||
onLoadOlder: () => void;
|
||||
threadLinks: Map<string, ThreadLink>;
|
||||
replyStatuses: ChatReplyStatus[];
|
||||
onOpenThread: (threadId: string) => void;
|
||||
onApprove: (approvalId: string) => void;
|
||||
onDecline: (approvalId: string) => void;
|
||||
@@ -72,6 +75,7 @@ export function ChatTimeline({
|
||||
const [visibleCount, setVisibleCount] = useState(WINDOW_SIZE);
|
||||
|
||||
const attachmentsByMessage = groupBy(attachments, (item) => item.messageId);
|
||||
const repliesByMessage = groupBy(replyStatuses, (item) => item.messageId);
|
||||
const mentionsByMessage = groupBy(mentions, (item) => item.messageId);
|
||||
const invocationsByMessage = groupBy(slashInvocations, (item) => item.messageId);
|
||||
|
||||
@@ -113,6 +117,7 @@ export function ChatTimeline({
|
||||
context={context}
|
||||
canModify={message.senderType === "employee" && message.senderId === currentEmployeeId && !message.deletedAt}
|
||||
thread={threadLinks.get(message.id)}
|
||||
replyStatuses={repliesByMessage.get(message.id)}
|
||||
onOpenThread={threadId ? () => onOpenThread(threadId) : undefined}
|
||||
onEdit={(bodyMd) => onEditMessage(message.id, bodyMd)}
|
||||
onDelete={() => onDeleteMessage(message.id)}
|
||||
|
||||
Reference in new issue
Block a user