reorg skill folder
This commit is contained in:
@@ -313,8 +313,8 @@ export function ChatInterface() {
|
||||
}): MessageViz => {
|
||||
const rows = Array.isArray(payload.result) ? payload.result : [];
|
||||
const chart = payload.chart ?? undefined;
|
||||
const canVisualize = Boolean(chart?.can_visualize);
|
||||
const chartSpec = canVisualize ? (chart?.chart_spec ?? null) : null;
|
||||
const canVisualize = chart?.can_visualize ?? Boolean(chart?.chart_spec);
|
||||
const chartSpec = chart?.chart_spec ?? null;
|
||||
return {
|
||||
sql: typeof payload.sql === "string" ? payload.sql : "",
|
||||
rows,
|
||||
@@ -553,12 +553,12 @@ export function ChatInterface() {
|
||||
let renderedText = "";
|
||||
|
||||
const flushAssistant = (force = false) => {
|
||||
if (streamedText === renderedText) return;
|
||||
if (streamedText === renderedText && !force) return;
|
||||
if (force) {
|
||||
renderedText = streamedText;
|
||||
setMessagesForSession(targetSessionKey, (prev) =>
|
||||
prev.map((msg) =>
|
||||
msg.id === assistantId ? { ...msg, content: streamedText, awaitingFirstToken: false } : msg
|
||||
msg.id === assistantId ? { ...msg, content: streamedText, awaitingFirstToken: false, viz: streamedViz ?? msg.viz } : msg
|
||||
)
|
||||
);
|
||||
return;
|
||||
@@ -571,7 +571,7 @@ export function ChatInterface() {
|
||||
renderedText = streamedText;
|
||||
setMessagesForSession(targetSessionKey, (prev) =>
|
||||
prev.map((msg) =>
|
||||
msg.id === assistantId ? { ...msg, content: streamedText, awaitingFirstToken: false } : msg
|
||||
msg.id === assistantId ? { ...msg, content: streamedText, awaitingFirstToken: false, viz: streamedViz ?? msg.viz } : msg
|
||||
)
|
||||
);
|
||||
});
|
||||
@@ -645,11 +645,7 @@ export function ChatInterface() {
|
||||
if (payload.type === "viz") {
|
||||
pushProgressLog("可视化结果已生成");
|
||||
streamedViz = buildMessageViz(payload);
|
||||
setMessagesForSession(targetSessionKey, (prev) =>
|
||||
prev.map((msg) =>
|
||||
msg.id === assistantId ? { ...msg, viz: streamedViz || undefined } : msg
|
||||
)
|
||||
);
|
||||
flushAssistant(true); // 立即把 viz 状态刷入 messages
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,8 +171,8 @@ export function InlineVisualizationCard({ viz }: InlineVisualizationCardProps) {
|
||||
</div>
|
||||
|
||||
{view === "chart" ? (
|
||||
viz.canVisualize && viz.chartSpec && objectRows.length > 0 ? (
|
||||
<div className="w-full h-80 rounded-xl border border-zinc-100 p-2">
|
||||
viz.chartSpec && objectRows.length > 0 ? (
|
||||
<div className="w-full h-80 min-h-[320px] rounded-xl border border-zinc-100 p-2">
|
||||
<VegaChart data={objectRows} spec={viz.chartSpec} />
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -30,23 +30,27 @@ export const VegaChart: React.FC<VegaChartProps> = ({ data, spec }) => {
|
||||
}, []);
|
||||
|
||||
const vegaSpec: any = useMemo(() => {
|
||||
// Clone spec and ensure tooltip is enabled in mark if not already specified
|
||||
const baseSpec = { ...spec };
|
||||
// Deep clone spec to avoid mutating React state/props
|
||||
const baseSpec = JSON.parse(JSON.stringify(spec));
|
||||
|
||||
// Ensure tooltip is enabled in mark if not already specified
|
||||
if (typeof baseSpec.mark === 'string') {
|
||||
baseSpec.mark = { type: baseSpec.mark, tooltip: true };
|
||||
} else if (typeof baseSpec.mark === 'object' && baseSpec.mark !== null) {
|
||||
baseSpec.mark = { ...baseSpec.mark, tooltip: true };
|
||||
baseSpec.mark.tooltip = true;
|
||||
}
|
||||
|
||||
// Add highlight effect: hover over an element makes others transparent
|
||||
// 1. Define hover param
|
||||
if (!baseSpec.params) {
|
||||
baseSpec.params = [
|
||||
{
|
||||
name: "highlight",
|
||||
select: { type: "point", on: "mouseover", clear: "mouseout" }
|
||||
}
|
||||
];
|
||||
baseSpec.params = [];
|
||||
}
|
||||
const hasHighlight = baseSpec.params.some((p: any) => p.name === "highlight");
|
||||
if (!hasHighlight) {
|
||||
baseSpec.params.push({
|
||||
name: "highlight",
|
||||
select: { type: "point", on: "mouseover", clear: "mouseout" }
|
||||
});
|
||||
}
|
||||
|
||||
// 2. Add conditional opacity to encoding
|
||||
@@ -64,7 +68,7 @@ export const VegaChart: React.FC<VegaChartProps> = ({ data, spec }) => {
|
||||
|
||||
// Also add cursor: pointer for marks
|
||||
if (typeof baseSpec.mark === 'object' && baseSpec.mark !== null) {
|
||||
(baseSpec.mark as any).cursor = "pointer";
|
||||
baseSpec.mark.cursor = "pointer";
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -77,12 +81,17 @@ export const VegaChart: React.FC<VegaChartProps> = ({ data, spec }) => {
|
||||
};
|
||||
}, [data, size.height, size.width, spec]);
|
||||
|
||||
const handleError = (error: any) => {
|
||||
console.error("VegaEmbed rendering error:", error, "Spec:", vegaSpec);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full h-full" ref={containerRef}>
|
||||
<div className="w-full h-full min-h-[300px]" ref={containerRef}>
|
||||
<VegaEmbed
|
||||
spec={vegaSpec}
|
||||
options={{ actions: false }}
|
||||
style={{width: '100%', height: '100%'}}
|
||||
onError={handleError}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user