fix(web): keep consultation thinking above one spoken answer
Per-domain thinking trees were interleaved with sliced analysis, so a finished reply still looked like unfinished checklists. One collapsed thinking panel and one full body restores the reading order. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
const FENCE = /(```[\s\S]*?```)/;
|
||||
const DEFINITION_LINE = /^(.{2,80}?)[::](.+)$/u;
|
||||
|
||||
function isSkippableLine(line: string): boolean {
|
||||
return /^(#{1,6}\s|>\s|[-*+]\s|\d+[.)]\s|\|)/.test(line) || line.startsWith("```");
|
||||
}
|
||||
|
||||
function isDefinitionLine(block: string): boolean {
|
||||
const trimmed = block.trim();
|
||||
if (!trimmed || trimmed.includes("\n") || isSkippableLine(trimmed)) return false;
|
||||
const match = DEFINITION_LINE.exec(trimmed);
|
||||
if (!match) return false;
|
||||
const body = match[2]?.trim() ?? "";
|
||||
return body.length >= 4;
|
||||
}
|
||||
|
||||
function toListItem(block: string): string {
|
||||
const trimmed = block.trim();
|
||||
const match = DEFINITION_LINE.exec(trimmed);
|
||||
if (!match) return trimmed;
|
||||
return `- **${match[1].trim()}**:${match[2].trim()}`;
|
||||
}
|
||||
|
||||
function promoteProse(text: string): string {
|
||||
const blocks = text.split(/\n{2,}/);
|
||||
const out: string[] = [];
|
||||
let index = 0;
|
||||
while (index < blocks.length) {
|
||||
if (isDefinitionLine(blocks[index] ?? "")) {
|
||||
let end = index;
|
||||
while (end < blocks.length && isDefinitionLine(blocks[end] ?? "")) end += 1;
|
||||
if (end - index >= 2) {
|
||||
out.push(blocks.slice(index, end).map((block) => toListItem(block)).join("\n"));
|
||||
index = end;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
const lines = (blocks[index] ?? "").split("\n").map((line) => line.trim()).filter(Boolean);
|
||||
if (lines.length >= 2 && lines.every(isDefinitionLine)) {
|
||||
out.push(lines.map((line) => toListItem(line)).join("\n"));
|
||||
} else {
|
||||
out.push(blocks[index] ?? "");
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
return out.join("\n\n");
|
||||
}
|
||||
|
||||
export function promoteDefinitionLists(text: string): string {
|
||||
if (!text.includes(":") && !text.includes(":")) return text;
|
||||
return text.split(FENCE).map((chunk, index) => (
|
||||
index % 2 === 1 ? chunk : promoteProse(chunk)
|
||||
)).join("");
|
||||
}
|
||||
@@ -170,8 +170,15 @@ export function windowConsultationThinkingPlan(): PublicThinkingSection[] {
|
||||
export function applyThinkingSectionProgress(
|
||||
sections: readonly PublicThinkingSection[],
|
||||
answerText: string,
|
||||
options?: { settled?: boolean },
|
||||
): PublicThinkingSection[] {
|
||||
if (sections.length === 0) return [];
|
||||
if (options?.settled && answerText.trim()) {
|
||||
return sections.map((section) => ({
|
||||
...section,
|
||||
steps: section.steps.map((item) => ({ ...item, status: "done" as const })),
|
||||
}));
|
||||
}
|
||||
const present = new Set(
|
||||
[...answerText.matchAll(/^##\s+(.+?)\s*$/gm)].map((match) => match[1]?.trim() ?? ""),
|
||||
);
|
||||
@@ -234,6 +241,7 @@ export function consultationSpokenHeadingRule(kind: "natal" | "general" | "windo
|
||||
if (kind === "natal") {
|
||||
return [
|
||||
`Write the spoken answer with these exact Markdown H2 headings in order: ## ${REPORT_HEADING.foundation}, then ## {the Chinese label of each executed domain in tool order, such as 事业 / 财富 / 关系}, then ## ${REPORT_HEADING.audit}, then ## ${REPORT_HEADING.wrap}.`,
|
||||
"Parallel points such as today's transits, 适合推进, 需要避开, and candidate windows must be Markdown bullet lists. Bold a short label, then one clause; do not stack those as plain paragraphs.",
|
||||
activity,
|
||||
secrets,
|
||||
].join(" ");
|
||||
|
||||
Reference in New Issue
Block a user