reorg skill folder

This commit is contained in:
qixinbo
2026-03-19 12:27:31 +08:00
parent baec21c774
commit cca492cfdb
13 changed files with 232 additions and 81 deletions
+6 -10
View File
@@ -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>
) : (
+20 -11
View File
@@ -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>
);