refactor(chat): remove the post-answer suggestion chips and the table that fed them

Measured use of the three chips above the composer was negligible. They
were also not what they appeared to be: the server looked up a fixed
triplet by session theme and passed it as metadata that overrode
anything the model produced, so the same ten hardcoded sets served every
user regardless of question or chart. That is a plausible reason nobody
pressed them.

Both copies of the per-theme table are gone, reply metadata narrows to
the session title, and the two parse entry points collapse into one now
that they return the same shape. The write schema still tolerates a
suggestions field so a client on the previous bundle does not lose its
message mid-deploy, and stored answers containing the legacy hidden
block are still stripped rather than shown raw.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-08-18 15:44:19 +08:00
co-authored by Cursor
parent a6d4473ef0
commit 52f3c04b3b
21 changed files with 123 additions and 204 deletions
+1 -3
View File
@@ -469,14 +469,12 @@ export async function POST(request: Request) {
try {
const reply = parseAgentReply(
rawTransformedText,
consultationTheme,
createConsultationReplyMetadata({ theme: consultationTheme, question: visibleQuestion }),
createConsultationReplyMetadata({ question: visibleQuestion }),
);
if (!reply.text) throw new Error("empty_agent_reply");
const responseMessage = {
role: "assistant" as const,
text: reply.text,
suggestions: reply.suggestions,
techniqueTruth,
workflowReceipt,
...(agentExecutionReceipt ? { agentExecutionReceipt } : {}),
+2 -20
View File
@@ -224,7 +224,6 @@ button:disabled { cursor: default; opacity: .45; }
to { background-position: -120% 0; }
}
.agent-activity-status + .message-markdown { margin-top: var(--space-2); }
.composer-suggestions::-webkit-scrollbar { display: none; }
.composer textarea::placeholder { color: var(--color-ink-tertiary); }
.composer button svg { width: 19px; height: 19px; }
.dialog-close svg { width: 19px; height: 19px; }
@@ -270,7 +269,7 @@ button:disabled { cursor: default; opacity: .45; }
.status-已过期, .status-已兑换 { color: var(--color-ink-secondary); }
.empty-cell { color: var(--color-ink-secondary); text-align: center !important; }
.new-chat:not(:disabled):active, .session-main:not(:disabled):active, .session-menu-trigger:not(:disabled):active, .session-action-item:not([data-disabled]):active, .profile-trigger:not(:disabled):active, .credit-button:not(:disabled):active, .account-menu-item:not([data-disabled]):active, .starter-list > button:not(:disabled):active, .composer-suggestions button:not(:disabled):active, .composer button:not(:disabled):active, .button-primary:not(:disabled):active, .button-secondary:not(:disabled):active, .dialog-close:not(:disabled):active, .generated-list button:not(:disabled):active, .inline-actions button:not(:disabled):active { transform: scale(.96); }
.new-chat:not(:disabled):active, .session-main:not(:disabled):active, .session-menu-trigger:not(:disabled):active, .session-action-item:not([data-disabled]):active, .profile-trigger:not(:disabled):active, .credit-button:not(:disabled):active, .account-menu-item:not([data-disabled]):active, .starter-list > button:not(:disabled):active, .composer button:not(:disabled):active, .button-primary:not(:disabled):active, .button-secondary:not(:disabled):active, .dialog-close:not(:disabled):active, .generated-list button:not(:disabled):active, .inline-actions button:not(:disabled):active { transform: scale(.96); }
@keyframes app-loading-orbit { to { transform: rotate(360deg); } }
@keyframes pulse { from { opacity: .28; transform: translateY(1px); } to { opacity: 1; transform: translateY(-1px); } }
@@ -724,8 +723,6 @@ button:disabled { cursor: default; opacity: .45; }
.error-message { margin: 12px 0 0; padding: 12px 14px; border-left: 3px solid var(--color-danger); background: var(--color-danger-muted); color: var(--color-danger); font-size: 13px; line-height: 1.6; border-color: var(--color-danger); border-radius: 0 var(--radius-md) var(--radius-md) 0; }
.composer-wrap { position: sticky; bottom: 0; z-index: 2; min-width: 0; border-top: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); padding: var(--space-3) var(--space-6) var(--space-4); background: var(--color-frosted); backdrop-filter: saturate(130%) blur(20px); }
.composer-suggestions { width: min(680px, 100%); margin: 0 auto 9px; overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x proximity; scrollbar-width: none; touch-action: pan-x; -webkit-overflow-scrolling: touch; display: grid; grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); gap: var(--space-2); margin-bottom: var(--space-3); overflow: visible; }
.composer-suggestions button { flex: 0 0 clamp(176px, 56vw, 230px); overflow: hidden; border: 1px solid var(--color-border); color: var(--color-ink-secondary); cursor: pointer; scroll-snap-align: start; text-overflow: ellipsis; white-space: nowrap; transition: border-color 120ms ease-out, color 120ms ease-out, transform 120ms ease-out; width: 100%; min-width: 0; min-height: 44px; padding: 0 var(--space-2); border-color: var(--color-border); border-radius: var(--radius-md); background: var(--color-canvas-soft); font-size: var(--type-caption); }
.composer { width: min(760px, 100%); display: flex; align-items: flex-end; gap: 10px; margin: 0 auto; border: 1px solid var(--color-border); transition: border-color 140ms ease-out; min-height: 60px; padding: var(--space-2) var(--space-2) var(--space-2) var(--space-4); border-color: var(--color-border-strong); border-radius: var(--radius-lg); background: var(--color-canvas); box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-border) 42%, transparent); }
.composer:focus-within { border-color: var(--color-action); box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-action) 15%, transparent); }
.composer textarea { min-width: 0; min-height: 44px; max-height: 128px; flex: 1; resize: none; padding: 11px 0 8px; border: 0; outline: 0; background: transparent; color: var(--color-ink); line-height: 1.5; font-size: 16px; }
@@ -907,7 +904,6 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class
.model-selector-option:hover { background: var(--color-canvas-soft); color: var(--color-ink); }
.starter-list > button:not(:disabled):hover { background: var(--color-canvas-strong); }
.starter-list > button:first-of-type:not(:disabled):hover { background: color-mix(in srgb, var(--color-action-soft) 72%, var(--color-canvas)); }
.composer-suggestions button:not(:disabled):hover { border-color: var(--color-action); background: var(--color-canvas); color: var(--color-action-hover); }
.button-secondary:not(:disabled):hover { background: var(--color-canvas-muted); }
.danger-primary:not(:disabled):hover { background: color-mix(in srgb, var(--color-danger) 88%, var(--color-ink)); }
.birth-time-source-option:hover, .birth-time-answer-list button:not(:disabled):hover { border-color: var(--color-action); }
@@ -1333,8 +1329,7 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class
}
.composer-wrap-starter .composer,
.composer-wrap-starter .composer-footer,
.composer-wrap-starter .composer-suggestions {
.composer-wrap-starter .composer-footer {
width: min(1040px, 100%);
}
@@ -1513,7 +1508,6 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class
border-top-color: color-mix(in srgb, var(--color-border) 54%, transparent);
}
.conversation:not(.is-empty):not(.is-rectification) + .composer-wrap .composer-suggestions,
.conversation:not(.is-empty):not(.is-rectification) + .composer-wrap .composer,
.conversation:not(.is-empty):not(.is-rectification) + .composer-wrap .composer-footer {
width: min(var(--session-column-width), 100%);
@@ -1525,18 +1519,6 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class
box-shadow: 0 1px 2px rgba(29, 29, 31, .05), 0 12px 32px -24px rgba(29, 29, 31, .34);
}
.conversation:not(.is-empty):not(.is-rectification) + .composer-wrap .composer-suggestions button {
background: color-mix(in srgb, var(--color-canvas-soft) 72%, var(--color-canvas));
}
@media (hover: hover) {
.conversation:not(.is-empty):not(.is-rectification) + .composer-wrap .composer-suggestions button:not(:disabled):hover {
border-color: color-mix(in srgb, var(--color-action) 36%, var(--color-border));
background: var(--color-action-soft);
color: var(--color-action-hover);
}
}
@media (max-width: 767px) {
:root {
--session-column-gutter: var(--space-4);
+4 -41
View File
@@ -266,8 +266,6 @@ const china = chinaLocations.country;
const themes = defaultGuidedJyotishTopics;
const rectifyBeforeConsultationSuggestion = "先完成生时校正";
const accountDialogTitles = {
profile: "个人资料",
logout: "退出登录?",
@@ -564,14 +562,6 @@ function completedOnboardingTranscript(profile: Profile, greeting: string): Mess
];
}
function readSuggestions(value: unknown) {
if (!Array.isArray(value)) return [];
return [...new Set(value
.filter((item): item is string => typeof item === "string")
.map((item) => item.replace(/\s+/g, " ").trim().slice(0, 80))
.filter(Boolean))].slice(0, 3);
}
function readProfile(value: unknown): Profile {
if (!value || typeof value !== "object") return emptyProfile;
const profile = value as Partial<Profile> & {
@@ -686,7 +676,6 @@ function readSessions(value: unknown, catalog: PublicLanguageModelCatalog | null
? [{
role: (message as Message).role,
text: (message as Message).text.slice(0, 12000),
suggestions: readSuggestions((message as Message).suggestions),
}]
: []
))
@@ -1165,10 +1154,6 @@ export default function Home() {
localStorage.setItem(`${prefix}archived`, JSON.stringify(archivedSessionIds));
}, [accountId, archivedSessionIds, hydrated, pinnedSessionIds]);
const activeSuggestions = activeSession?.messages.reduce<readonly string[]>(
(latest, message) => message.role === "assistant" && message.suggestions?.length ? message.suggestions : latest,
[],
) ?? [];
useEffect(() => {
if (!accountId) {
setChartLibrary([]);
@@ -1372,7 +1357,7 @@ export default function Home() {
const previewMessages: Message[] = previewMode === "conversation" || previewMode === "streaming" || previewMode === "partial"
? [
{ role: "user", text: "未来半年是否适合换工作?" },
{ role: "assistant", text: "可以先看职业方向、关键时间。\n同时评估现实风险。\n此处只展示本地预览,\n不调用真实星盘。", suggestions: ["先看事业方向", "再看关键时间", "评估现实风险"] },
{ role: "assistant", text: "可以先看职业方向、关键时间。\n同时评估现实风险。\n此处只展示本地预览,\n不调用真实星盘。" },
]
: [];
const previewSession: ChatSession = {
@@ -2359,18 +2344,6 @@ export default function Home() {
window.requestAnimationFrame(() => composerInput.current?.focus());
}
function chooseConversationSuggestion(suggestion: string) {
if (suggestion !== rectifyBeforeConsultationSuggestion
|| activeSession?.sessionType !== "consultation") {
chooseSuggestedQuestion(suggestion);
return;
}
const originalQuestion = [...activeSession.messages]
.reverse()
.find((message) => message.role === "user")?.text.trim();
void openRectificationFromHomepage(originalQuestion ?? null);
}
function draftDailyStarlanguageQuestion() {
chooseSuggestedQuestion(
personalChartAvailable
@@ -2937,16 +2910,14 @@ export default function Home() {
}
const previewReply = parseAgentReply([
"这是本地交互预览。正式对话会结合你的星盘证据继续分析。",
'<!--AYANAM_SUGGESTIONS:["继续梳理方向","查看时间窗口","评估现实行动"]-->',
"<!--AYANAM_TITLE:事业方向与时间选择-->",
].join("\n"), theme);
].join("\n"));
const previewSession: ChatSession = {
...userSession,
title: userSession.title,
messages: [...userSession.messages, {
role: "assistant",
text: previewReply.text,
suggestions: previewReply.suggestions,
}],
updatedAt: timestamp(),
};
@@ -3047,7 +3018,7 @@ export default function Home() {
const decoder = new TextDecoder();
let answer = "";
const updateStreamingAnswer = (activity?: AgentActivityView) => {
const partialReply = parseAgentReply(answer, theme).text;
const partialReply = parseAgentReply(answer).text;
latestPartialReply = partialReply;
setStreamingReply({ sessionId, text: partialReply, activity });
if (partialReply && pendingConsultation.current?.requestId === requestId) {
@@ -3100,7 +3071,7 @@ export default function Home() {
}
if (controller.signal.aborted) return Boolean(latestPartialReply);
if (!answer.trim()) throw new Error("Agent 没有返回内容,请重试。");
const reply = parseAgentReply(answer, theme);
const reply = parseAgentReply(answer);
if (!reply.text) throw new Error("Agent 没有返回可显示的回答,请重试。");
const completedSession: ChatSession = {
@@ -3109,7 +3080,6 @@ export default function Home() {
messages: [...userSession.messages, {
role: "assistant",
text: reply.text,
suggestions: reply.suggestions,
techniqueTruth,
workflowReceipt,
agentExecutionReceipt,
@@ -3519,13 +3489,6 @@ export default function Home() {
</button>
</div>
)}
{activeSuggestions.length > 0 && (
<div className="composer-suggestions" aria-label="推荐继续提问">
{activeSuggestions.map((question) => (
<button key={question} type="button" disabled={!account || !modelCatalog || isLoading || cancellationPending || rectificationSurfaceOpen} onClick={() => chooseConversationSuggestion(question)}>{question}</button>
))}
</div>
)}
<ChatComposer
inputRef={composerInput}
inputLabel={!profileComplete && onboardingStep === "name" ? "输入你的称呼" : "输入你的问题"}