fix(chat): fold natal reports and drop homepage topic cards
Homepage no longer waits on /api/onboarding for six starter questions. Natal answers show the spoken layer first; the Level 2 skeleton sits in a collapsed 完整分析 block. Wide tables scroll sideways on a phone. Form inputs are 16px so iOS does not zoom on focus.
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
"use client";
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
import { answerDetailCaption } from "@/lib/chat-answer-split";
|
||||
|
||||
export function AnswerDetailDisclosure({
|
||||
headings,
|
||||
streaming = false,
|
||||
children,
|
||||
}: {
|
||||
readonly headings: readonly string[];
|
||||
readonly streaming?: boolean;
|
||||
readonly children?: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<details className="answer-detail">
|
||||
<summary>
|
||||
完整分析
|
||||
<small>{streaming ? "正在写…" : answerDetailCaption(headings)}</small>
|
||||
</summary>
|
||||
{streaming ? null : <div className="answer-detail-panel">{children}</div>}
|
||||
</details>
|
||||
);
|
||||
}
|
||||
@@ -2,9 +2,11 @@
|
||||
|
||||
import { memo, useEffect, useState, type ReactNode } from "react";
|
||||
|
||||
import { AnswerDetailDisclosure } from "@/components/chat-answer-detail";
|
||||
import { prefetchOnIdle } from "@/components/chat-chunk-prefetch";
|
||||
import { plainParagraphs } from "@/components/chat-message-paragraphs";
|
||||
import { TechniqueAuditDisclosure } from "@/components/technique-audit-disclosure";
|
||||
import { splitSpokenAndReport } from "@/lib/chat-answer-split";
|
||||
import { splitStableMarkdown } from "@/lib/chat-markdown-split";
|
||||
import type { TechniqueAuditRow } from "@/lib/consultation-agent-events";
|
||||
import {
|
||||
@@ -94,8 +96,19 @@ export function ChatMessageContent({
|
||||
const renderMarkdown = useMarkdownRenderer();
|
||||
const split = splitSpokenAnswerAndTechniqueAudit(text);
|
||||
const rows = resolveTechniqueAuditRows(split, auditRows);
|
||||
const spoken = split.spoken;
|
||||
const layers = splitSpokenAndReport(split.spoken);
|
||||
const spoken = layers.spoken;
|
||||
const report = layers.report;
|
||||
const showFoldedAudit = rows.length > 0 && split.rows.length === 0;
|
||||
const foldedAudit = showFoldedAudit ? <TechniqueAuditDisclosure rows={rows} /> : null;
|
||||
const reportBody = !streaming && report
|
||||
? (
|
||||
<>
|
||||
<div className="message-markdown">{renderProse(report, renderMarkdown)}</div>
|
||||
{foldedAudit}
|
||||
</>
|
||||
)
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div className="message-answer">
|
||||
@@ -103,14 +116,16 @@ export function ChatMessageContent({
|
||||
<div className="message-markdown">
|
||||
{streaming
|
||||
? <StreamingMarkdown text={spoken} renderMarkdown={renderMarkdown} />
|
||||
: renderMarkdown
|
||||
? renderMarkdown(spoken)
|
||||
: (plainParagraphs(spoken) ?? []).map((paragraph, index) => (
|
||||
<p key={index}>{paragraph}</p>
|
||||
))}
|
||||
: renderProse(spoken, renderMarkdown)}
|
||||
</div>
|
||||
) : null}
|
||||
{showFoldedAudit ? <TechniqueAuditDisclosure rows={rows} /> : null}
|
||||
{report
|
||||
? (
|
||||
<AnswerDetailDisclosure headings={layers.headings} streaming={streaming}>
|
||||
{reportBody}
|
||||
</AnswerDetailDisclosure>
|
||||
)
|
||||
: foldedAudit}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,21 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { ArrowUpRight } from "lucide-react";
|
||||
import type { ConsultationEntrypoint } from "@/lib/consultation-entrypoint";
|
||||
import type { DailyStarlanguageState, Theme } from "@/lib/home-types";
|
||||
import type { DailyStarlanguageState } from "@/lib/home-types";
|
||||
import type { RectificationEntrySummary } from "@/lib/rectification-entry";
|
||||
|
||||
export type StarterHomeTheme = {
|
||||
readonly id: string;
|
||||
readonly label: string;
|
||||
readonly prompt: string;
|
||||
};
|
||||
|
||||
export type StarterHomeSuggestion = {
|
||||
readonly theme: Theme;
|
||||
readonly text: string;
|
||||
};
|
||||
|
||||
export type StarterHomeProps = {
|
||||
readonly starterGreeting: { readonly salutation: string; readonly question: string };
|
||||
readonly dailyStarlanguage: DailyStarlanguageState;
|
||||
@@ -34,14 +22,6 @@ export type StarterHomeProps = {
|
||||
readonly rectificationError: string;
|
||||
readonly rectificationSurfaceOpen: boolean;
|
||||
readonly rectificationErrorMessage: string;
|
||||
readonly starterThemes: readonly StarterHomeTheme[];
|
||||
readonly starterSuggestions: readonly StarterHomeSuggestion[];
|
||||
readonly startSuggestedConsultation: (
|
||||
text: string,
|
||||
theme: Theme,
|
||||
entrypoint?: ConsultationEntrypoint | null,
|
||||
) => void;
|
||||
readonly onboardingError: string;
|
||||
};
|
||||
|
||||
export function StarterHome({
|
||||
@@ -62,13 +42,9 @@ export function StarterHome({
|
||||
rectificationError,
|
||||
rectificationSurfaceOpen,
|
||||
rectificationErrorMessage,
|
||||
starterThemes,
|
||||
starterSuggestions,
|
||||
startSuggestedConsultation,
|
||||
onboardingError,
|
||||
}: StarterHomeProps) {
|
||||
return (
|
||||
<div className="starter-list starter-workbench" aria-label="Jyotisha 推荐的初始问题">
|
||||
<div className="starter-list starter-workbench" aria-label="Jyotisha 首页">
|
||||
<section className="starter-hero" aria-labelledby="starter-heading">
|
||||
<div className="starter-hero-copy">
|
||||
<p className="starter-greeting">{starterGreeting.salutation}</p>
|
||||
@@ -128,37 +104,6 @@ export function StarterHome({
|
||||
{rectificationErrorMessage}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<section className="starter-themes" aria-labelledby="starter-themes-heading">
|
||||
<div className="starter-section-heading">
|
||||
<h2 id="starter-themes-heading">从一个主题开始</h2>
|
||||
<p>{natalMinuteAvailable
|
||||
? "选择一个你现在想解决的问题。"
|
||||
: "选择一个你现在想解决的问题;出生时间不足以支持的部分,我会明确说明,不会补造具体分钟。"}</p>
|
||||
</div>
|
||||
<div className="starter-theme-accordion">
|
||||
{starterSuggestions.map((item) => {
|
||||
const theme = starterThemes.find((candidate) => candidate.id === item.theme);
|
||||
return (
|
||||
<button
|
||||
className="starter-theme-card"
|
||||
key={`${item.theme}-${item.text}`}
|
||||
type="button"
|
||||
aria-label={`${theme?.label || "开始"}:${item.text}`}
|
||||
disabled={productEntrypointsDisabled}
|
||||
onClick={() => void startSuggestedConsultation(item.text, item.theme, "guided_topic")}
|
||||
>
|
||||
<span className="starter-content">
|
||||
<b>{theme?.label || "开始"}</b>
|
||||
<span>{item.text}</span>
|
||||
</span>
|
||||
<ArrowUpRight className="starter-arrow" aria-hidden="true" />
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
{onboardingError && <p className="starter-note">个性化问题暂时不可用,已显示安全的默认问题。</p>}
|
||||
</div>
|
||||
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user