fix(ui): harden product-facing interaction flows

This commit is contained in:
john
2026-07-14 14:24:32 +08:00
parent 4e6abacce1
commit 77f1ea8350
6 changed files with 37 additions and 6 deletions
+7
View File
@@ -25,6 +25,13 @@ test('deriveUserFacingText removes routing hint and skill preface from agent pay
assert.equal(deriveUserFacingText(agentPayload), userText); assert.equal(deriveUserFacingText(agentPayload), userText);
}); });
test('deriveUserFacingText removes page-data prompt persisted as display text', () => {
const userText = '帮我做一个日记页面,可以每天写日记,其他人可以评价';
const persistedDisplayText = `${buildAutoChatSkillPrefix(userText, ['page-data-collect'])}${userText}`;
assert.match(persistedDisplayText, /page-data-collect/);
assert.equal(deriveUserFacingText(persistedDisplayText), userText);
});
test('deriveUserFacingText removes Memind task orchestration prefix', () => { test('deriveUserFacingText removes Memind task orchestration prefix', () => {
const userText = '帮我生成深度搜索报告'; const userText = '帮我生成深度搜索报告';
const agentPayload = [ const agentPayload = [
+5
View File
@@ -36,6 +36,11 @@ const messageTs = read('src/utils/message.ts');
assertIncludes(messageTs, 'deriveUserFacingText', 'message.ts'); assertIncludes(messageTs, 'deriveUserFacingText', 'message.ts');
assertIncludes(messageTs, 'deriveAssistantFacingText', 'message.ts'); assertIncludes(messageTs, 'deriveAssistantFacingText', 'message.ts');
assertIncludes(messageTs, 'chat-finish-sync.mjs', 'message.ts'); assertIncludes(messageTs, 'chat-finish-sync.mjs', 'message.ts');
assertIncludes(
messageTs,
'deriveUserFacingText(message.metadata.displayText)',
'message.ts metadata displayText guard',
);
const conversationDisplay = read('conversation-display.mjs'); const conversationDisplay = read('conversation-display.mjs');
assertIncludes(conversationDisplay, 'TASK_ROUTING_HINT_RE', 'conversation-display.mjs'); assertIncludes(conversationDisplay, 'TASK_ROUTING_HINT_RE', 'conversation-display.mjs');
+10 -4
View File
@@ -1586,7 +1586,7 @@ export function MindSpacePageDetail({
<button <button
type="button" type="button"
className={previewRefreshPending ? 'is-refresh-pending' : undefined} className={previewRefreshPending ? 'is-refresh-pending' : undefined}
onClick={handleManualPreviewRefresh} onClick={() => handleManualPreviewRefresh()}
title={previewRefreshPending ? '草稿已有新内容,点击刷新预览' : '将当前草稿同步到预览'} title={previewRefreshPending ? '草稿已有新内容,点击刷新预览' : '将当前草稿同步到预览'}
> >
{previewRefreshPending ? ' · 有新修改' : ''} {previewRefreshPending ? ' · 有新修改' : ''}
@@ -1684,15 +1684,21 @@ export function MindSpacePageDetail({
) : null} ) : null}
{confirmPublicationStatusOpen && page?.publication ? ( {confirmPublicationStatusOpen && page?.publication ? (
<MindSpaceModal className="mindspace-confirm-publication-status-modal"> <MindSpaceModal
open={confirmPublicationStatusOpen}
onClose={() => setConfirmPublicationStatusOpen(false)}
title="页面预览期确认"
eyebrow="PUBLICATION STATUS"
className="mindspace-confirm-publication-status-modal"
disableClose={statusConfirming}
>
<div className="mindspace-modal-content"> <div className="mindspace-modal-content">
<h3></h3>
<p> 30 </p> <p> 30 </p>
<div className="mindspace-modal-actions"> <div className="mindspace-modal-actions">
<button <button
className="mindspace-secondary" className="mindspace-secondary"
disabled={statusConfirming} disabled={statusConfirming}
onClick={() => confirmPublicationStatus('private')} onClick={() => confirmPublicationStatus('owner_only')}
> >
{statusConfirming ? '处理中...' : '改为私有'} {statusConfirming ? '处理中...' : '改为私有'}
</button> </button>
+4
View File
@@ -477,6 +477,10 @@ function formatBytes(bytes: number) {
type AssetFilter = 'all' | 'images' | 'files' | 'pages'; type AssetFilter = 'all' | 'images' | 'files' | 'pages';
function previewBlocked() {
return new Error('预览模式仅供查看,不能修改内容');
}
function canGenerateWithAgent(asset: MindSpaceAsset) { function canGenerateWithAgent(asset: MindSpaceAsset) {
const textLikeMimeTypes = new Set([ const textLikeMimeTypes = new Set([
'text/plain', 'text/plain',
+10 -1
View File
@@ -17,7 +17,16 @@ export function VoiceInputDialog({
onSend: (text: string) => void; onSend: (text: string) => void;
onError?: (message: string) => void; onError?: (message: string) => void;
}) { }) {
const { phase, text, analyser, liveRecognition, stopListening, finishFallbackRecording, resetSession } = const {
phase,
text,
analyser,
liveRecognition,
updateText,
stopListening,
finishFallbackRecording,
resetSession,
} =
useVoiceSession({ useVoiceSession({
active: open && !disabled, active: open && !disabled,
onError, onError,
+1 -1
View File
@@ -204,7 +204,7 @@ export function getDisplayText(message: Message): string {
// REGRESSION GUARD: never show agent-only routing/skill prefixes in the chat UI. // REGRESSION GUARD: never show agent-only routing/skill prefixes in the chat UI.
if (message.role === 'user') { if (message.role === 'user') {
if ('displayText' in message.metadata && message.metadata.displayText != null) { if ('displayText' in message.metadata && message.metadata.displayText != null) {
return stripImageUrlLines(message.metadata.displayText); return stripImageUrlLines(deriveUserFacingText(message.metadata.displayText));
} }
const raw = message.content const raw = message.content
.filter((c): c is Extract<MessageContent, { type: 'text' }> => c.type === 'text') .filter((c): c is Extract<MessageContent, { type: 'text' }> => c.type === 'text')