From 8f11966e21563dc539f307016c17fd6eb141bc1a Mon Sep 17 00:00:00 2001 From: Matt Pua Date: Fri, 22 May 2026 17:37:27 -0400 Subject: [PATCH] fix(inbox): break signal summary sections onto separate lines Insert line breaks around What's happening, Root cause, and How to resolve headers so report summaries are easier to scan. Co-authored-by: Cursor --- .../utils/SignalReportSummaryMarkdown.tsx | 5 ++- .../formatSignalReportSummaryMarkdown.test.ts | 33 +++++++++++++++++ .../formatSignalReportSummaryMarkdown.ts | 35 +++++++++++++++++++ 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 apps/code/src/renderer/features/inbox/utils/formatSignalReportSummaryMarkdown.test.ts create mode 100644 apps/code/src/renderer/features/inbox/utils/formatSignalReportSummaryMarkdown.ts diff --git a/apps/code/src/renderer/features/inbox/components/utils/SignalReportSummaryMarkdown.tsx b/apps/code/src/renderer/features/inbox/components/utils/SignalReportSummaryMarkdown.tsx index d2c3d81d3..a3b49473e 100644 --- a/apps/code/src/renderer/features/inbox/components/utils/SignalReportSummaryMarkdown.tsx +++ b/apps/code/src/renderer/features/inbox/components/utils/SignalReportSummaryMarkdown.tsx @@ -1,4 +1,5 @@ import { MarkdownRenderer } from "@features/editor/components/MarkdownRenderer"; +import { formatSignalReportSummaryMarkdown } from "@features/inbox/utils/formatSignalReportSummaryMarkdown"; import { Box } from "@radix-ui/themes"; interface SignalReportSummaryMarkdownProps { @@ -23,7 +24,9 @@ export function SignalReportSummaryMarkdown({ variant, pending, }: SignalReportSummaryMarkdownProps) { - const raw = content?.trim() ? content : fallback; + const raw = formatSignalReportSummaryMarkdown( + content?.trim() ? content : fallback, + ); /** List rows: only the first line (before first newline); CSS still caps visual lines. */ const listMarkdown = raw.split(/\r?\n/)[0] ?? ""; diff --git a/apps/code/src/renderer/features/inbox/utils/formatSignalReportSummaryMarkdown.test.ts b/apps/code/src/renderer/features/inbox/utils/formatSignalReportSummaryMarkdown.test.ts new file mode 100644 index 000000000..da7b097c8 --- /dev/null +++ b/apps/code/src/renderer/features/inbox/utils/formatSignalReportSummaryMarkdown.test.ts @@ -0,0 +1,33 @@ +import { describe, expect, it } from "vitest"; +import { formatSignalReportSummaryMarkdown } from "./formatSignalReportSummaryMarkdown"; + +describe("formatSignalReportSummaryMarkdown", () => { + it("puts section body text on a new line after the header", () => { + const input = + "**What's happening:** Error tracking issue keyed on `app:dashboard_query`."; + expect(formatSignalReportSummaryMarkdown(input)).toBe( + "**What's happening:**\n\nError tracking issue keyed on `app:dashboard_query`.", + ); + }); + + it("separates consecutive section headers onto their own lines", () => { + const input = + "**What's happening:** Users hit rate limits. **Root cause:** All four rate limiters are contended. **How to resolve:** Reduce blocking."; + expect(formatSignalReportSummaryMarkdown(input)).toBe( + "**What's happening:**\n\nUsers hit rate limits.\n\n**Root cause:**\n\nAll four rate limiters are contended.\n\n**How to resolve:**\n\nReduce blocking.", + ); + }); + + it("separates a section header from preceding intro text", () => { + const input = + "Users on busy orgs are hitting hard limits. **What's happening:** Error tracking issue."; + expect(formatSignalReportSummaryMarkdown(input)).toBe( + "Users on busy orgs are hitting hard limits.\n\n**What's happening:**\n\nError tracking issue.", + ); + }); + + it("leaves content without section headers unchanged", () => { + const input = "Plain summary with no structured sections."; + expect(formatSignalReportSummaryMarkdown(input)).toBe(input); + }); +}); diff --git a/apps/code/src/renderer/features/inbox/utils/formatSignalReportSummaryMarkdown.ts b/apps/code/src/renderer/features/inbox/utils/formatSignalReportSummaryMarkdown.ts new file mode 100644 index 000000000..c83720795 --- /dev/null +++ b/apps/code/src/renderer/features/inbox/utils/formatSignalReportSummaryMarkdown.ts @@ -0,0 +1,35 @@ +const SIGNAL_SUMMARY_SECTION_HEADERS = [ + "What's happening", + "Root cause", + "How to resolve", +] as const; + +function escapeRegExp(value: string): string { + return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +} + +/** + * Inserts line breaks around signal report summary section headers so each + * label and its body render on separate lines (matches agent output like + * `**What's happening:** text`). + */ +export function formatSignalReportSummaryMarkdown(content: string): string { + let result = content; + + for (const header of SIGNAL_SUMMARY_SECTION_HEADERS) { + const escaped = escapeRegExp(header); + const boldHeaderPattern = `\\*\\*${escaped}:\\*\\*`; + + result = result.replace( + new RegExp(`([^\\n])\\s*(${boldHeaderPattern})`, "gi"), + "$1\n\n$2", + ); + + result = result.replace( + new RegExp(`(${boldHeaderPattern})\\s+`, "gi"), + "$1\n\n", + ); + } + + return result; +}