76 lines
1.9 KiB
TypeScript
76 lines
1.9 KiB
TypeScript
/**
|
|
* Pure utility functions for parsing report-json blocks from chat text.
|
|
* Extracted from chat-message.tsx for testability.
|
|
*/
|
|
|
|
export type ReportConfig = {
|
|
version: number;
|
|
title: string;
|
|
description?: string;
|
|
panels: Array<{
|
|
id: string;
|
|
title: string;
|
|
type: string;
|
|
sql: string;
|
|
mapping: Record<string, unknown>;
|
|
size?: string;
|
|
}>;
|
|
filters?: Array<{
|
|
id: string;
|
|
type: string;
|
|
label: string;
|
|
column: string;
|
|
sql?: string;
|
|
}>;
|
|
};
|
|
|
|
export type ParsedSegment =
|
|
| { type: "text"; text: string }
|
|
| { type: "report-artifact"; config: ReportConfig };
|
|
|
|
/**
|
|
* Split text containing ```report-json ... ``` fenced blocks into
|
|
* alternating text and report-artifact segments.
|
|
*/
|
|
export function splitReportBlocks(text: string): ParsedSegment[] {
|
|
const reportFenceRegex = /```report-json\s*\n([\s\S]*?)```/g;
|
|
const segments: ParsedSegment[] = [];
|
|
let lastIndex = 0;
|
|
|
|
for (const match of text.matchAll(reportFenceRegex)) {
|
|
const before = text.slice(lastIndex, match.index);
|
|
if (before.trim()) {
|
|
segments.push({ type: "text", text: before });
|
|
}
|
|
|
|
try {
|
|
const config = JSON.parse(match[1]) as ReportConfig;
|
|
if (config.panels && Array.isArray(config.panels)) {
|
|
segments.push({ type: "report-artifact", config });
|
|
} else {
|
|
// Invalid report config -- render as plain text
|
|
segments.push({ type: "text", text: match[0] });
|
|
}
|
|
} catch {
|
|
// Invalid JSON -- render as plain text
|
|
segments.push({ type: "text", text: match[0] });
|
|
}
|
|
|
|
lastIndex = (match.index ?? 0) + match[0].length;
|
|
}
|
|
|
|
const remaining = text.slice(lastIndex);
|
|
if (remaining.trim()) {
|
|
segments.push({ type: "text", text: remaining });
|
|
}
|
|
|
|
return segments;
|
|
}
|
|
|
|
/**
|
|
* Check if text contains any report-json fenced blocks.
|
|
*/
|
|
export function hasReportBlocks(text: string): boolean {
|
|
return text.includes("```report-json");
|
|
}
|