feat: add guided Jyotish topic evidence cards
This commit is contained in:
@@ -41,6 +41,7 @@ import {
|
||||
isGuidedBirthTimePreview,
|
||||
previewRectificationJourney,
|
||||
} from "@/lib/birth-time-guided-preview";
|
||||
import { defaultGuidedJyotishTopics } from "@/lib/guided-jyotish-topics";
|
||||
import { keepFocusWithin } from "@/lib/focus-trap";
|
||||
import { chatMessageViews, type ChatMessage } from "@/lib/chat-message-view";
|
||||
import {
|
||||
@@ -155,11 +156,7 @@ type PendingConsultation = {
|
||||
const undoWindowMs = 2_500;
|
||||
const china = chinaLocations.country;
|
||||
|
||||
const themes: Array<{ id: Exclude<Theme, "general">; label: string; prompt: string }> = [
|
||||
{ id: "career", label: "事业", prompt: "未来一年,事业和收入该关注什么?" },
|
||||
{ id: "marriage", label: "关系", prompt: "我的关系模式是什么?" },
|
||||
{ id: "timing", label: "时运", prompt: "未来哪些阶段值得把握?" },
|
||||
];
|
||||
const themes = defaultGuidedJyotishTopics;
|
||||
|
||||
const accountDialogTitles = {
|
||||
profile: "个人资料",
|
||||
@@ -2482,7 +2479,7 @@ export default function Home() {
|
||||
const theme = themes.find((candidate) => candidate.id === item.theme);
|
||||
return (
|
||||
<button key={`${item.theme}-${item.text}`} type="button" disabled={!hydrated || Boolean(pendingSessionId) || cancellationPending || !account || !modelCatalog} onClick={() => chooseSuggestedQuestion(item.text, item.theme)}>
|
||||
<span className="starter-content"><b>{theme?.label || "开始"}</b><span>{item.text}</span></span>
|
||||
<span className="starter-content"><b>{theme?.label || "开始"}</b><span>{item.text}</span>{theme && <small>{theme.evidencePreview.join(" / ")} · {theme.claimBoundary}</small>}</span>
|
||||
<ArrowUpRight className="starter-arrow" aria-hidden="true" />
|
||||
</button>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import type { ConsultationTheme } from "./consultation-workflow-request";
|
||||
|
||||
export type GuidedJyotishTopic = {
|
||||
id: ConsultationTheme;
|
||||
label: string;
|
||||
prompt: string;
|
||||
strictWorkflowRoute: string;
|
||||
evidencePreview: string[];
|
||||
confidenceCap: "low" | "medium";
|
||||
claimBoundary: string;
|
||||
};
|
||||
|
||||
export const defaultGuidedJyotishTopics: GuidedJyotishTopic[] = [
|
||||
{
|
||||
id: "career",
|
||||
label: "事业",
|
||||
prompt: "未来一年,事业和收入该关注什么?",
|
||||
strictWorkflowRoute: "career",
|
||||
evidencePreview: ["D1", "D10", "A10", "Vimshottari", "Narayana", "Transit"],
|
||||
confidenceCap: "medium",
|
||||
claimBoundary: "输出职业结构与阶段判断;具体日/月只作为候选窗口。",
|
||||
},
|
||||
{
|
||||
id: "marriage",
|
||||
label: "关系",
|
||||
prompt: "我的关系模式是什么?",
|
||||
strictWorkflowRoute: "marriage",
|
||||
evidencePreview: ["D1", "D9", "7宫", "DK", "UL", "Vimshottari", "Narayana"],
|
||||
confidenceCap: "medium",
|
||||
claimBoundary: "输出关系模式与宽窗口;不承诺某日必然发生事件。",
|
||||
},
|
||||
{
|
||||
id: "wealth",
|
||||
label: "财富",
|
||||
prompt: "我的财富增长方式和风险点是什么?",
|
||||
strictWorkflowRoute: "wealth",
|
||||
evidencePreview: ["D1", "D2", "D11", "2/11宫", "财富 Yoga", "Ashtakavarga"],
|
||||
confidenceCap: "medium",
|
||||
claimBoundary: "输出财富结构和风险类型;不替代投资建议。",
|
||||
},
|
||||
{
|
||||
id: "timing",
|
||||
label: "时运",
|
||||
prompt: "未来哪些阶段值得把握?",
|
||||
strictWorkflowRoute: "timing",
|
||||
evidencePreview: ["Dasha", "Narayana", "Transit", "Varga"],
|
||||
confidenceCap: "low",
|
||||
claimBoundary: "精确月/日仍是探索性候选,未通过独立 holdout 前不升级。",
|
||||
},
|
||||
];
|
||||
@@ -5,6 +5,7 @@ import test from "node:test";
|
||||
const pageSource = readFileSync(new URL("../src/app/page.tsx", import.meta.url), "utf8");
|
||||
const globalStyles = readFileSync(new URL("../src/app/globals.css", import.meta.url), "utf8");
|
||||
const appSidebarSource = readFileSync(new URL("../src/components/app-sidebar.tsx", import.meta.url), "utf8");
|
||||
const guidedTopicsSource = readFileSync(new URL("../src/lib/guided-jyotish-topics.ts", import.meta.url), "utf8");
|
||||
|
||||
function sourceBetween(source: string, startMarker: string, endMarker: string) {
|
||||
const start = source.indexOf(startMarker);
|
||||
@@ -28,6 +29,20 @@ test("keeps starter questions visible while the user edits a draft", () => {
|
||||
assert.doesNotMatch(starterVisibilityGuard, /\bdraft\b/);
|
||||
});
|
||||
|
||||
test("default starter questions are guided Jyotish topics with evidence and claim boundaries", () => {
|
||||
assert.match(pageSource, /defaultGuidedJyotishTopics/);
|
||||
assert.match(pageSource, /theme\.evidencePreview\.join/);
|
||||
assert.match(pageSource, /theme\.claimBoundary/);
|
||||
assert.match(guidedTopicsSource, /strictWorkflowRoute/);
|
||||
assert.match(guidedTopicsSource, /evidencePreview/);
|
||||
assert.match(guidedTopicsSource, /confidenceCap/);
|
||||
assert.match(guidedTopicsSource, /claimBoundary/);
|
||||
assert.match(guidedTopicsSource, /D10/);
|
||||
assert.match(guidedTopicsSource, /D9/);
|
||||
assert.match(guidedTopicsSource, /Ashtakavarga/);
|
||||
assert.match(guidedTopicsSource, /独立 holdout/);
|
||||
});
|
||||
|
||||
test("keeps follow-up suggestions visible while the user edits a draft", () => {
|
||||
// Given: the follow-up suggestion block and its render guard.
|
||||
const suggestionGuard = sourceBetween(
|
||||
|
||||
Reference in New Issue
Block a user