fix(chat): choose the chart person before the first message
This commit is contained in:
@@ -34,6 +34,7 @@ import {
|
||||
type ChatMessageFeedback,
|
||||
} from "@/components/chat-message-actions";
|
||||
import { ChatTranscript, type ChatTranscriptActions } from "@/components/chat-transcript";
|
||||
import { ChatProfilePicker } from "@/components/chat-profile-picker";
|
||||
import { ModelSelector } from "@/components/model-selector";
|
||||
import { OnboardingRedeemPaywall } from "@/components/onboarding-redeem-paywall";
|
||||
import { ChatComposer } from "@/components/chat-composer";
|
||||
@@ -174,6 +175,8 @@ import {
|
||||
type StreamingReply,
|
||||
type Theme,
|
||||
} from "@/lib/home-types";
|
||||
import { chatSubjectSendBlocked } from "@/lib/chat-profile-picker-model";
|
||||
import { useChatProfileCatalog } from "@/hooks/use-chat-profile-catalog";
|
||||
import {
|
||||
birthQuestion,
|
||||
chartSnapshotForSession,
|
||||
@@ -454,6 +457,8 @@ export default function Home() {
|
||||
togglePinnedSession,
|
||||
shareSession,
|
||||
startNewChat,
|
||||
bindEmptyChatSubject,
|
||||
openChatBoundToProfile,
|
||||
selectSession, selectSessionModel, loadMoreSessions,
|
||||
} = useSessionManagement({
|
||||
account, accountId, activeChartId, activeSession, activeSessionId, activeSessionIdRef,
|
||||
@@ -563,6 +568,36 @@ export default function Home() {
|
||||
queueMicrotask(() => setActiveChartId(storedChartId));
|
||||
}, [accountId, hydrated]);
|
||||
|
||||
const {
|
||||
status: chatProfileStatus,
|
||||
markReady: markChatProfilesReady,
|
||||
markFailed: markChatProfilesFailed,
|
||||
beginReload: beginChatProfileReload,
|
||||
} = useChatProfileCatalog(accountId);
|
||||
const subjectDeleted = chatSubjectSendBlocked({
|
||||
session: activeSession,
|
||||
status: chatProfileStatus,
|
||||
library: chartLibrary,
|
||||
});
|
||||
|
||||
function reloadChatProfiles() {
|
||||
if (!accountId) return;
|
||||
const requested = accountId;
|
||||
const profileForLibrary: Profile = activeChartId === "self" ? profile : account ? readProfile(account.profile) : profile;
|
||||
beginChatProfileReload();
|
||||
void fetchCloudChartLibrary()
|
||||
.then((cloudLibrary) => {
|
||||
if (chartLibraryLoadedAccount.current !== requested) return;
|
||||
setChartLibrary(chartLibraryFromCloudOthers(cloudLibrary, profileForLibrary, upsertSelfChart));
|
||||
markChatProfilesReady(requested);
|
||||
})
|
||||
.catch(() => {
|
||||
if (chartLibraryLoadedAccount.current !== requested) return;
|
||||
markChatProfilesFailed(requested);
|
||||
setComposerNotice("星盘库暂时无法读取,请重试。");
|
||||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const branch = chartLibrarySessionBranch(accountId, chartLibraryLoadedAccount.current);
|
||||
if (branch === "clear" || !accountId) {
|
||||
@@ -582,17 +617,26 @@ export default function Home() {
|
||||
clearSynastryHistory();
|
||||
void fetchCloudChartLibrary()
|
||||
.then((cloudLibrary) => {
|
||||
if (chartLibraryLoadedAccount.current !== accountId) return;
|
||||
setChartLibrary(chartLibraryFromCloudOthers(cloudLibrary, profileForLibrary, upsertSelfChart));
|
||||
markChatProfilesReady(accountId);
|
||||
})
|
||||
.catch(() => {
|
||||
if (chartLibraryLoadedAccount.current !== accountId) return;
|
||||
markChatProfilesFailed(accountId);
|
||||
setComposerNotice("星盘库暂时无法读取,请重试。", {
|
||||
label: "重试",
|
||||
onClick: () => {
|
||||
beginChatProfileReload();
|
||||
void fetchCloudChartLibrary()
|
||||
.then((cloudLibrary) => {
|
||||
if (chartLibraryLoadedAccount.current !== accountId) return;
|
||||
setChartLibrary(chartLibraryFromCloudOthers(cloudLibrary, profileForLibrary, upsertSelfChart));
|
||||
markChatProfilesReady(accountId);
|
||||
})
|
||||
.catch(() => {
|
||||
if (chartLibraryLoadedAccount.current !== accountId) return;
|
||||
markChatProfilesFailed(accountId);
|
||||
setComposerNotice("星盘库暂时无法读取,请重试。");
|
||||
});
|
||||
},
|
||||
@@ -618,7 +662,7 @@ export default function Home() {
|
||||
});
|
||||
}
|
||||
setChartLibrary((current) => upsertSelfChart(current, profileForLibrary));
|
||||
}, [account, accountId, activeChartId, applyCloudSynastryHistory, clearSynastryHistory, profile]);
|
||||
}, [account, accountId, activeChartId, applyCloudSynastryHistory, beginChatProfileReload, clearSynastryHistory, markChatProfilesFailed, markChatProfilesReady, profile]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!hydrated || !accountId || chartLibrary.length === 0) return;
|
||||
@@ -747,6 +791,7 @@ export default function Home() {
|
||||
pendingConsultation,
|
||||
pendingSessionId,
|
||||
profile,
|
||||
subjectDeleted,
|
||||
sessions,
|
||||
setAccount,
|
||||
setActiveSessionId,
|
||||
@@ -1426,6 +1471,7 @@ export default function Home() {
|
||||
if (onboardingStep === "name" && presetMessageFinished) void saveOnboardingName();
|
||||
return;
|
||||
}
|
||||
if (subjectDeleted) return;
|
||||
if (isLoading || cancellationPending) return void enqueueQueuedDraft(composerDraftSnapshot());
|
||||
void send(composerDraftSnapshot(), draftTheme.current ?? undefined, draftEntrypoint.current);
|
||||
}
|
||||
@@ -1566,7 +1612,19 @@ export default function Home() {
|
||||
<SidebarTrigger placement="inset" />
|
||||
<div className="chat-header-title">
|
||||
<strong>{activeSession?.title || "新对话"}</strong>
|
||||
{activeSession ? <span className="chat-header-chart">{sessionChartLabel(activeSession, chartLibrary)}</span> : null}
|
||||
{activeSession?.sessionType === "consultation" ? (
|
||||
<ChatProfilePicker
|
||||
session={activeSession}
|
||||
library={chartLibrary}
|
||||
status={chatProfileStatus}
|
||||
requestInFlight={Boolean(pendingSessionId) || cancellationPending}
|
||||
onBindEmpty={bindEmptyChatSubject}
|
||||
onOpenNew={openChatBoundToProfile}
|
||||
onReload={reloadChatProfiles}
|
||||
/>
|
||||
) : activeSession ? (
|
||||
<span className="chat-header-chart">{sessionChartLabel(activeSession, chartLibrary)}</span>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="chat-header-actions">
|
||||
<div className="chat-header-rectification" data-rectification-header-slot="" ref={setRectificationHeaderSlot} />
|
||||
@@ -1711,11 +1769,11 @@ export default function Home() {
|
||||
<ChatComposer
|
||||
inputRef={composerInput}
|
||||
inputLabel={!profileComplete && onboardingStep === "name" ? "输入你的称呼" : "输入你的问题"}
|
||||
placeholder={ordinaryComposerPlaceholder({ locked: rectificationComposerLocked, hasAccount: Boolean(account), profileComplete, onboardingName: onboardingStep === "name", presetMessageFinished, creditsEmpty: Boolean(account && account.credits === 0 && !account.activeSubscription) })}
|
||||
placeholder={ordinaryComposerPlaceholder({ locked: rectificationComposerLocked, hasAccount: Boolean(account), profileComplete, onboardingName: onboardingStep === "name", presetMessageFinished, creditsEmpty: Boolean(account && account.credits === 0 && !account.activeSubscription), subjectDeleted })}
|
||||
maxLength={!profileComplete && onboardingStep === "name" ? 80 : 500}
|
||||
inputDisabled={sessionMessagesLoading || rectificationSurfaceOpen || activeRectificationSession || (!profileComplete && (onboardingStep !== "name" || !presetMessageFinished || profileSaving))}
|
||||
inputDisabled={subjectDeleted || sessionMessagesLoading || rectificationSurfaceOpen || activeRectificationSession || (!profileComplete && (onboardingStep !== "name" || !presetMessageFinished || profileSaving))}
|
||||
submitLabel={!profileComplete && onboardingStep === "name" ? "确认称呼" : "发送"}
|
||||
submitBlocked={Boolean(pendingSessionId) || Boolean(queuedDraft) || sessionMessagesLoading || rectificationSurfaceOpen || activeRectificationSession || !account || !modelCatalog || (!profileComplete && (onboardingStep !== "name" || !presetMessageFinished || profileSaving))}
|
||||
submitBlocked={subjectDeleted || Boolean(pendingSessionId) || Boolean(queuedDraft) || sessionMessagesLoading || rectificationSurfaceOpen || activeRectificationSession || !account || !modelCatalog || (!profileComplete && (onboardingStep !== "name" || !presetMessageFinished || profileSaving))}
|
||||
stopVisible={isLoading}
|
||||
stopLabel={consultationPhase === "undo" ? "撤回发送,本次不扣点" : activeStreamingText ? "停止回答,保留已生成内容并退回本次点数" : "停止回答并申请退回本次点数"}
|
||||
stopTitle={consultationPhase === "undo" ? "撤回发送,本次不扣点" : activeStreamingText ? "停止回答,保留现有内容并申请退回本次点数" : "停止回答"}
|
||||
|
||||
@@ -642,6 +642,7 @@ button:disabled { cursor: default; opacity: .45; }
|
||||
.chat-header > div { min-width: 0; flex: 1; }
|
||||
.chat-header { padding: 0 var(--space-3); }
|
||||
.chat-header-chart { display: none; }
|
||||
button.chat-profile-picker-trigger { display: inline-flex; }
|
||||
.onboarding-card { padding: 16px; }
|
||||
|
||||
.message-assistant .message-content { max-width: 100%; }
|
||||
@@ -678,7 +679,7 @@ button:disabled { cursor: default; opacity: .45; }
|
||||
width: 8px !important;
|
||||
height: 8px !important;
|
||||
}
|
||||
.model-selector-popup[data-starting-style], .model-selector-popup[data-ending-style], .session-actions[data-starting-style], .session-actions[data-ending-style], .account-menu-popup[data-starting-style], .account-menu-popup[data-ending-style], [role="tooltip"][data-starting-style], [role="tooltip"][data-ending-style] { transform: none; }
|
||||
.model-selector-popup[data-starting-style], .model-selector-popup[data-ending-style], .chat-profile-picker-popup[data-starting-style], .chat-profile-picker-popup[data-ending-style], .session-actions[data-starting-style], .session-actions[data-ending-style], .account-menu-popup[data-starting-style], .account-menu-popup[data-ending-style], [role="tooltip"][data-starting-style], [role="tooltip"][data-ending-style] { transform: none; }
|
||||
.auth-step { transform: none; transition: opacity 80ms linear !important; }
|
||||
@starting-style { .auth-step { opacity: 0; transform: none; } }
|
||||
}
|
||||
@@ -906,12 +907,33 @@ button:disabled { cursor: default; opacity: .45; }
|
||||
.chat-panel.is-rectification { grid-template-rows: 46px minmax(0, 1fr); }
|
||||
.chat-header { z-index: 2; min-width: 0; display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: var(--space-3); border-bottom: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); padding: 0 var(--space-4); background: var(--color-frosted); backdrop-filter: saturate(130%) blur(20px); text-align: left; }
|
||||
.chat-header > div { min-width: 0; }
|
||||
/* One line, 46px tall. The former second line read `分析对象:…`; the chart it
|
||||
analyses is now a quiet chip beside the title, and drops out on narrow
|
||||
viewports rather than wrapping the header to two rows. */
|
||||
.chat-header-title { min-width: 0; display: flex; align-items: baseline; gap: var(--space-3); }
|
||||
/* One line, 46px tall. The chart chip stays beside the title and ellipsizes
|
||||
instead of wrapping the header. Narrow viewports hide the static note; the
|
||||
ordinary-chat person button stays, because it is the only person entry. */
|
||||
.chat-header-title { min-width: 0; display: flex; align-items: center; gap: var(--space-3); }
|
||||
.chat-header strong { min-width: 0; overflow: hidden; line-height: 1.35; text-overflow: ellipsis; white-space: nowrap; font-size: var(--type-body-md); font-weight: 500; letter-spacing: -.2px; }
|
||||
.chat-header-chart { flex: 0 1 auto; min-width: 0; overflow: hidden; color: var(--color-ink-tertiary); font-size: var(--type-caption); text-overflow: ellipsis; white-space: nowrap; }
|
||||
.chat-profile-picker-trigger { max-width: 46%; min-width: 44px; min-height: 44px; display: inline-flex; align-items: center; margin: 0; padding: 0 var(--space-2); border: 0; border-radius: var(--radius-md); background: transparent; color: var(--color-ink-tertiary); cursor: pointer; font: inherit; font-size: var(--type-caption); line-height: 1.35; text-align: left; transition: background-color 120ms ease-out, color 120ms ease-out; }
|
||||
.chat-profile-picker-trigger:focus-visible { outline: 3px solid color-mix(in srgb, var(--color-focus) 56%, transparent); outline-offset: 1px; }
|
||||
.chat-profile-picker-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.chat-profile-picker-positioner { z-index: 30; outline: 0; }
|
||||
.chat-profile-picker-popup { width: max-content; min-width: 220px; max-width: min(320px, calc(100vw - var(--space-6))); border: 1px solid var(--color-border); padding: var(--space-3); border-radius: var(--radius-lg); background: var(--color-canvas); box-shadow: var(--shadow-elevated); transform-origin: var(--transform-origin); transition: opacity 120ms ease-out, transform 120ms var(--ease-out); }
|
||||
.chat-profile-picker-popup[data-starting-style], .chat-profile-picker-popup[data-ending-style] { opacity: 0; transform: translateY(calc(var(--space-1) * -1)); }
|
||||
.chat-profile-picker-title { margin: 0; color: var(--color-ink); font-family: var(--font-display); font-size: var(--type-title-sm); font-weight: 500; }
|
||||
.chat-profile-picker-status { margin: var(--space-2) 0 0; color: var(--color-ink-secondary); font-size: var(--type-caption); line-height: 1.45; }
|
||||
.chat-profile-picker-status p { margin: 0; }
|
||||
.chat-profile-picker-retry { min-height: 44px; margin-top: var(--space-2); padding: 0 var(--space-3); border: 0; border-radius: var(--radius-md); background: transparent; color: var(--color-action); cursor: pointer; font: inherit; font-size: var(--type-caption); }
|
||||
.chat-profile-picker-options { display: grid; gap: var(--space-1); margin: var(--space-2) 0 0; }
|
||||
.chat-profile-picker-option { width: 100%; min-height: 44px; display: grid; grid-template-columns: minmax(0, 1fr) 20px; align-items: center; gap: var(--space-3); padding: var(--space-2) var(--space-3); border: 1px solid transparent; border-radius: var(--radius-md); background: transparent; color: var(--color-ink-secondary); cursor: pointer; text-align: left; font: inherit; transition: border-color 120ms ease-out, background-color 120ms ease-out, color 120ms ease-out; }
|
||||
.chat-profile-picker-option[aria-disabled="true"] { cursor: default; color: var(--color-ink-tertiary); }
|
||||
.chat-profile-picker-option[data-selected] { border-color: var(--color-border); background: var(--color-selected); color: var(--color-ink); }
|
||||
.chat-profile-picker-option:focus-visible { outline: 3px solid color-mix(in srgb, var(--color-focus) 56%, transparent); outline-offset: 1px; }
|
||||
.chat-profile-picker-option:disabled { cursor: default; }
|
||||
.chat-profile-picker-copy { min-width: 0; display: grid; gap: 2px; }
|
||||
.chat-profile-picker-copy b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: inherit; font-size: var(--type-body-sm); font-weight: 500; line-height: 1.35; }
|
||||
.chat-profile-picker-copy small { color: var(--color-ink-tertiary); font-size: var(--type-caption); line-height: 1.35; }
|
||||
.chat-profile-picker-check { width: 18px; height: 18px; opacity: 0; color: var(--color-action); }
|
||||
.chat-profile-picker-option[data-selected] .chat-profile-picker-check { opacity: 1; }
|
||||
.chat-header-actions { min-width: max-content; display: flex; flex: 0 0 auto; align-items: center; gap: var(--space-2); white-space: nowrap; }
|
||||
.chat-header-actions > * { flex: 0 0 auto; }
|
||||
/* The page header is 46px tall, so its actions are 32px — the button primitives
|
||||
@@ -1981,6 +2003,8 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class
|
||||
.composer button:not(:disabled):hover, .button-primary:not(:disabled):hover { background: var(--color-action-hover); }
|
||||
.model-selector-trigger:not(:disabled):hover { background: var(--color-canvas-muted); color: var(--color-ink); }
|
||||
.model-selector-option:hover { background: var(--color-canvas-soft); color: var(--color-ink); }
|
||||
.chat-profile-picker-trigger:hover { background: var(--color-canvas-muted); color: var(--color-ink); }
|
||||
.chat-profile-picker-option:not(:disabled):not([aria-disabled="true"]):hover { background: var(--color-canvas-soft); color: var(--color-ink); }
|
||||
.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); }
|
||||
|
||||
Reference in New Issue
Block a user