perf(frontend): split chat streaming, load Inter, isolate admin CSS
Independent Staging Quality Gate / validate (push) Successful in 10m14s
Independent Staging Quality Gate / publish (push) Successful in 11m5s

Settled messages no longer rebuild on every token, Inter is actually
requested, and admin routes drop the 33 KB chat stylesheet. Root
force-dynamic is gone so public shells can prerender without changing
the no-store Cache-Control contract.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-08-29 03:39:43 +08:00
parent bf697d71a2
commit 3198fb6b2b
41 changed files with 1528 additions and 307 deletions
@@ -0,0 +1,77 @@
"use client";
import { X } from "lucide-react";
import { memo, useEffect, useState, type MutableRefObject, type ReactNode } from "react";
export type AccountOverlayLibraryControls = Readonly<{
open: boolean;
toggle: () => void;
}>;
export type AccountOverlayModel = Readonly<{
title: string;
dialogClass: string;
signingOut: boolean;
close: () => void;
takeOpenLibraryRequest: () => boolean;
overlayRef: MutableRefObject<HTMLElement | null>;
closeButtonRef: MutableRefObject<HTMLButtonElement | null>;
renderProfile: (library: AccountOverlayLibraryControls) => ReactNode;
renderLogout: () => ReactNode;
}>;
export const AccountDialogOverlay = memo(function AccountDialogOverlay({
open,
dialog,
openEpoch,
modelRef,
}: Readonly<{
open: boolean;
dialog: "profile" | "logout" | null;
openEpoch: number;
modelRef: MutableRefObject<AccountOverlayModel | null>;
}>) {
const [chartLibraryOpen, setChartLibraryOpen] = useState(false);
useEffect(() => {
if (!open) return;
if (modelRef.current?.takeOpenLibraryRequest()) setChartLibraryOpen(true);
}, [dialog, modelRef, open]);
if (!open || dialog === null) return null;
const model = modelRef.current;
if (!model) return null;
void openEpoch;
return (
<div className="account-modal-overlay" onMouseDown={model.close}>
<section
className={`account-modal ${model.dialogClass}`}
ref={model.overlayRef}
role="dialog"
aria-modal="true"
aria-labelledby="account-dialog-title"
onMouseDown={(event) => event.stopPropagation()}
>
<header className="account-modal-header">
<h2 id="account-dialog-title">{model.title}</h2>
<button
className="dialog-close"
ref={model.closeButtonRef}
aria-label="关闭"
type="button"
onClick={model.close}
disabled={model.signingOut}
>
<X aria-hidden="true" />
</button>
</header>
{dialog === "profile" && model.renderProfile({
open: chartLibraryOpen,
toggle: () => setChartLibraryOpen((current) => !current),
})}
{dialog === "logout" && model.renderLogout()}
</section>
</div>
);
});
+1 -1
View File
@@ -51,7 +51,7 @@ export function AdminApp({ children }: { children: ReactNode }) {
borderRadiusLG: 8,
boxShadowSecondary: "0 1px 2px rgba(29, 29, 31, .06)",
boxShadowTertiary: "none",
fontFamily: "StyreneB, Inter, system-ui, sans-serif",
fontFamily: "StyreneB, var(--font-inter, Inter), system-ui, sans-serif",
},
components: {
Layout: {
+293
View File
@@ -0,0 +1,293 @@
"use client";
import { ChatMessageRow } from "@/components/chat-message-row";
import {
ChatMessageActions,
toggleChatMessageFeedback,
type ChatMessageFeedback,
} from "@/components/chat-message-actions";
import { ConversationFollowUps } from "@/components/conversation-follow-ups";
import { isGeneralDailyFortuneQuestion } from "@/lib/consultation-entrypoint";
import { deriveConsultationFollowUps } from "@/lib/consultation-follow-ups";
import type { ConsultationDomain } from "@/lib/consultation-domain-registry";
import type { AgentActivityView, ChatMessage, ChatMessageView } from "@/lib/chat-message-view";
import { chatMessageViews, settledChatMessageViews, streamingChatMessageView } from "@/lib/chat-message-view";
import type { PublicThinkingSection } from "@/lib/consultation-thinking-plan";
import type { ConsultationTimelineRow } from "@/lib/consultation-run-timeline";
import {
noteSettledListRender,
noteSettledRowRender,
noteStreamingRowRender,
noteUnsplitListRender,
} from "@/lib/home-streaming-render-probe";
import { memo, type MutableRefObject } from "react";
export type ChatTranscriptActions = Readonly<{
onFeedback: (feedbackKey: string, requested: ChatMessageFeedback) => void;
onCopy: (feedbackKey: string, text: string) => void;
onRegenerate: (renderKey: string) => void;
onFollowUp: (question: string) => void;
}>;
export type ChatTranscriptProps = Readonly<{
messages: readonly ChatMessage[];
loading: boolean;
streamingText: string;
streamingActivity?: AgentActivityView;
streamingThinking?: string;
streamingSections?: readonly PublicThinkingSection[];
streamingTimeline?: readonly ConsultationTimelineRow[];
sessionId: string;
sessionType: "consultation" | "birth_time_rectification";
theme?: ConsultationDomain;
messageFeedback: Readonly<Record<string, ChatMessageFeedback>>;
copiedMessageKey: string | null;
cancellationPending: boolean;
productEntrypointsDisabled: boolean;
actionsRef: MutableRefObject<ChatTranscriptActions>;
}>;
const SettledMessageEntry = memo(function SettledMessageEntry({
message,
views,
index,
sessionId,
sessionType,
theme,
messageFeedback,
copiedMessageKey,
loading,
cancellationPending,
productEntrypointsDisabled,
actionsRef,
}: Readonly<{
message: ChatMessageView;
views: readonly ChatMessageView[];
index: number;
sessionId: string;
sessionType: "consultation" | "birth_time_rectification";
theme?: ConsultationDomain;
messageFeedback: Readonly<Record<string, ChatMessageFeedback>>;
copiedMessageKey: string | null;
loading: boolean;
cancellationPending: boolean;
productEntrypointsDisabled: boolean;
actionsRef: MutableRefObject<ChatTranscriptActions>;
}>) {
noteSettledRowRender();
const showActions = message.role === "assistant"
&& message.state === "settled"
&& Boolean(message.text);
const feedbackKey = `${sessionId}:${message.renderKey}`;
const latestRegeneratableKey = !loading && !cancellationPending
? [...views].reverse().find((item) => (
item.role === "assistant" && item.state === "settled" && Boolean(item.text)
))?.renderKey
: undefined;
const previousQuestion = views[index - 1]?.role === "user" ? views[index - 1]?.text : "";
const followUps = showActions
&& message.renderKey === latestRegeneratableKey
&& sessionType === "consultation"
&& previousQuestion
? deriveConsultationFollowUps({
question: previousQuestion,
answer: message.text,
theme,
entrypoint: isGeneralDailyFortuneQuestion(previousQuestion) ? "daily_starlanguage" : null,
})
: [];
return (
<div className="message-entry">
<ChatMessageRow message={message} />
{showActions && (
<ChatMessageActions
feedback={messageFeedback[feedbackKey]}
copied={copiedMessageKey === feedbackKey}
canRegenerate={message.renderKey === latestRegeneratableKey}
onFeedback={(requested) => actionsRef.current.onFeedback(feedbackKey, requested)}
onCopy={() => actionsRef.current.onCopy(feedbackKey, message.text)}
onRegenerate={() => actionsRef.current.onRegenerate(message.renderKey)}
/>
)}
<ConversationFollowUps
questions={followUps}
disabled={productEntrypointsDisabled}
onSelect={(question) => actionsRef.current.onFollowUp(question)}
/>
</div>
);
});
export const SettledMessageList = memo(function SettledMessageList({
messages,
sessionId,
sessionType,
theme,
messageFeedback,
copiedMessageKey,
loading,
cancellationPending,
productEntrypointsDisabled,
actionsRef,
}: Omit<ChatTranscriptProps, "streamingText" | "streamingActivity" | "streamingThinking" | "streamingSections" | "streamingTimeline">) {
noteSettledListRender();
const views = settledChatMessageViews(messages);
return (
<>
{views.map((message, index) => (
<SettledMessageEntry
key={message.renderKey}
message={message}
views={views}
index={index}
sessionId={sessionId}
sessionType={sessionType}
theme={theme}
messageFeedback={messageFeedback}
copiedMessageKey={copiedMessageKey}
loading={loading}
cancellationPending={cancellationPending}
productEntrypointsDisabled={productEntrypointsDisabled}
actionsRef={actionsRef}
/>
))}
</>
);
});
export const StreamingMessageEntry = memo(function StreamingMessageEntry({
message,
}: Readonly<{ message: ChatMessageView }>) {
noteStreamingRowRender();
return (
<div className="message-entry">
<ChatMessageRow message={message} />
</div>
);
});
export const ChatTranscript = memo(function ChatTranscript({
messages,
loading,
streamingText,
streamingActivity,
streamingThinking,
streamingSections,
streamingTimeline,
sessionId,
sessionType,
theme,
messageFeedback,
copiedMessageKey,
cancellationPending,
productEntrypointsDisabled,
actionsRef,
}: ChatTranscriptProps) {
const streamingMessage = streamingChatMessageView(
messages,
loading,
streamingText,
streamingActivity,
streamingThinking,
streamingSections,
streamingTimeline,
);
return (
<>
<SettledMessageList
messages={messages}
loading={loading}
sessionId={sessionId}
sessionType={sessionType}
theme={theme}
messageFeedback={messageFeedback}
copiedMessageKey={copiedMessageKey}
cancellationPending={cancellationPending}
productEntrypointsDisabled={productEntrypointsDisabled}
actionsRef={actionsRef}
/>
{streamingMessage ? <StreamingMessageEntry message={streamingMessage} /> : null}
</>
);
});
export function UnsplitChatTranscript({
messages,
loading,
streamingText,
streamingActivity,
streamingThinking,
streamingSections,
streamingTimeline,
sessionId,
sessionType,
theme,
messageFeedback,
copiedMessageKey,
cancellationPending,
productEntrypointsDisabled,
actionsRef,
}: ChatTranscriptProps) {
noteUnsplitListRender();
const views = chatMessageViews(
messages,
loading,
streamingText,
streamingActivity,
streamingThinking,
streamingSections,
streamingTimeline,
);
return (
<>
{views.map((message, index) => {
noteSettledRowRender();
if (message.state !== "settled") noteStreamingRowRender();
const showActions = message.role === "assistant"
&& message.state === "settled"
&& Boolean(message.text);
const feedbackKey = `${sessionId}:${message.renderKey}`;
const latestRegeneratableKey = !loading && !cancellationPending
? [...views].reverse().find((item) => (
item.role === "assistant" && item.state === "settled" && Boolean(item.text)
))?.renderKey
: undefined;
const previousQuestion = views[index - 1]?.role === "user" ? views[index - 1]?.text : "";
const followUps = showActions
&& message.renderKey === latestRegeneratableKey
&& sessionType === "consultation"
&& previousQuestion
? deriveConsultationFollowUps({
question: previousQuestion,
answer: message.text,
theme,
entrypoint: isGeneralDailyFortuneQuestion(previousQuestion) ? "daily_starlanguage" : null,
})
: [];
return (
<div className="message-entry" key={message.renderKey}>
<ChatMessageRow message={message} />
{showActions && (
<ChatMessageActions
feedback={messageFeedback[feedbackKey]}
copied={copiedMessageKey === feedbackKey}
canRegenerate={message.renderKey === latestRegeneratableKey}
onFeedback={(requested) => actionsRef.current.onFeedback(feedbackKey, requested)}
onCopy={() => actionsRef.current.onCopy(feedbackKey, message.text)}
onRegenerate={() => actionsRef.current.onRegenerate(message.renderKey)}
/>
)}
<ConversationFollowUps
questions={followUps}
disabled={productEntrypointsDisabled}
onSelect={(question) => actionsRef.current.onFollowUp(question)}
/>
</div>
);
})}
</>
);
}
export { toggleChatMessageFeedback };