pmx-canvas 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/LICENSE +21 -0
- package/Readme.md +865 -0
- package/dist/canvas/global.css +3173 -0
- package/dist/canvas/index.js +183 -0
- package/dist/json-render/index.css +2 -0
- package/dist/json-render/index.js +389 -0
- package/dist/types/cli/agent.d.ts +13 -0
- package/dist/types/cli/index.d.ts +2 -0
- package/dist/types/cli/watch.d.ts +5 -0
- package/dist/types/client/App.d.ts +1 -0
- package/dist/types/client/canvas/AttentionHistory.d.ts +1 -0
- package/dist/types/client/canvas/AttentionToast.d.ts +1 -0
- package/dist/types/client/canvas/CanvasNode.d.ts +8 -0
- package/dist/types/client/canvas/CanvasViewport.d.ts +8 -0
- package/dist/types/client/canvas/CommandPalette.d.ts +4 -0
- package/dist/types/client/canvas/ContextMenu.d.ts +24 -0
- package/dist/types/client/canvas/ContextPinBar.d.ts +1 -0
- package/dist/types/client/canvas/ContextPinHud.d.ts +1 -0
- package/dist/types/client/canvas/DockedNode.d.ts +4 -0
- package/dist/types/client/canvas/EdgeLayer.d.ts +8 -0
- package/dist/types/client/canvas/ExpandedNodeOverlay.d.ts +1 -0
- package/dist/types/client/canvas/FocusFieldLayer.d.ts +1 -0
- package/dist/types/client/canvas/Minimap.d.ts +23 -0
- package/dist/types/client/canvas/SelectionBar.d.ts +1 -0
- package/dist/types/client/canvas/ShortcutOverlay.d.ts +3 -0
- package/dist/types/client/canvas/SnapshotPanel.d.ts +7 -0
- package/dist/types/client/canvas/snap-guides.d.ts +23 -0
- package/dist/types/client/canvas/use-node-drag.d.ts +15 -0
- package/dist/types/client/canvas/use-node-resize.d.ts +15 -0
- package/dist/types/client/canvas/use-pan-zoom.d.ts +16 -0
- package/dist/types/client/ext-app/bridge.d.ts +161 -0
- package/dist/types/client/icons.d.ts +70 -0
- package/dist/types/client/index.d.ts +1 -0
- package/dist/types/client/nodes/ContextNode.d.ts +34 -0
- package/dist/types/client/nodes/ExtAppFrame.d.ts +18 -0
- package/dist/types/client/nodes/FileNode.d.ts +5 -0
- package/dist/types/client/nodes/GroupNode.d.ts +6 -0
- package/dist/types/client/nodes/ImageNode.d.ts +10 -0
- package/dist/types/client/nodes/InlineFormatBar.d.ts +7 -0
- package/dist/types/client/nodes/InlineMarkdownEditor.d.ts +14 -0
- package/dist/types/client/nodes/LedgerNode.d.ts +4 -0
- package/dist/types/client/nodes/MarkdownNode.d.ts +6 -0
- package/dist/types/client/nodes/McpAppNode.d.ts +4 -0
- package/dist/types/client/nodes/MdFormatBar.d.ts +8 -0
- package/dist/types/client/nodes/PromptNode.d.ts +5 -0
- package/dist/types/client/nodes/ResponseNode.d.ts +5 -0
- package/dist/types/client/nodes/StatusNode.d.ts +4 -0
- package/dist/types/client/nodes/StatusSummary.d.ts +4 -0
- package/dist/types/client/nodes/TraceNode.d.ts +4 -0
- package/dist/types/client/nodes/WebpageNode.d.ts +5 -0
- package/dist/types/client/nodes/image-warnings.d.ts +6 -0
- package/dist/types/client/nodes/inline-editor-commands.d.ts +11 -0
- package/dist/types/client/nodes/md-format.d.ts +25 -0
- package/dist/types/client/state/attention-bridge.d.ts +3 -0
- package/dist/types/client/state/attention-store.d.ts +25 -0
- package/dist/types/client/state/canvas-store.d.ts +74 -0
- package/dist/types/client/state/intent-bridge.d.ts +158 -0
- package/dist/types/client/state/sse-bridge.d.ts +5 -0
- package/dist/types/client/theme/tokens.d.ts +27 -0
- package/dist/types/client/types.d.ts +40 -0
- package/dist/types/client/utils/ext-app-tool-result.d.ts +1 -0
- package/dist/types/client/utils/placement.d.ts +1 -0
- package/dist/types/client/utils/platform.d.ts +2 -0
- package/dist/types/json-render/catalog.d.ts +815 -0
- package/dist/types/json-render/charts/components.d.ts +54 -0
- package/dist/types/json-render/charts/definitions.d.ts +103 -0
- package/dist/types/json-render/charts/extra-components.d.ts +58 -0
- package/dist/types/json-render/charts/extra-definitions.d.ts +181 -0
- package/dist/types/json-render/renderer/index.d.ts +16 -0
- package/dist/types/json-render/schema.d.ts +46 -0
- package/dist/types/json-render/server.d.ts +55 -0
- package/dist/types/mcp/server.d.ts +22 -0
- package/dist/types/server/agent-context.d.ts +21 -0
- package/dist/types/server/artifact-paths.d.ts +3 -0
- package/dist/types/server/canvas-operations.d.ts +154 -0
- package/dist/types/server/canvas-provenance.d.ts +13 -0
- package/dist/types/server/canvas-schema.d.ts +49 -0
- package/dist/types/server/canvas-serialization.d.ts +25 -0
- package/dist/types/server/canvas-state.d.ts +174 -0
- package/dist/types/server/canvas-validation.d.ts +33 -0
- package/dist/types/server/chart-template.d.ts +29 -0
- package/dist/types/server/code-graph.d.ts +67 -0
- package/dist/types/server/context-cards.d.ts +24 -0
- package/dist/types/server/diagram-presets.d.ts +28 -0
- package/dist/types/server/ext-app-call-registry.d.ts +16 -0
- package/dist/types/server/ext-app-tool-result.d.ts +1 -0
- package/dist/types/server/file-watcher.d.ts +16 -0
- package/dist/types/server/index.d.ts +243 -0
- package/dist/types/server/mcp-app-candidate.d.ts +25 -0
- package/dist/types/server/mcp-app-host.d.ts +65 -0
- package/dist/types/server/mcp-app-runtime.d.ts +47 -0
- package/dist/types/server/mutation-history.d.ts +105 -0
- package/dist/types/server/placement.d.ts +37 -0
- package/dist/types/server/server.d.ts +103 -0
- package/dist/types/server/spatial-analysis.d.ts +87 -0
- package/dist/types/server/trace-manager.d.ts +48 -0
- package/dist/types/server/web-artifacts.d.ts +50 -0
- package/dist/types/server/webpage-node.d.ts +25 -0
- package/dist/types/shared/auto-arrange.d.ts +29 -0
- package/dist/types/shared/ext-app-tool-result.d.ts +9 -0
- package/dist/types/shared/placement.d.ts +26 -0
- package/dist/types/shared/semantic-attention.d.ts +97 -0
- package/package.json +109 -0
- package/skills/data-analysis/SKILL.md +324 -0
- package/skills/doc-coauthoring/SKILL.md +375 -0
- package/skills/frontend-design/SKILL.md +45 -0
- package/skills/json-render-codegen/SKILL.md +112 -0
- package/skills/json-render-core/SKILL.md +265 -0
- package/skills/json-render-ink/SKILL.md +273 -0
- package/skills/json-render-mcp/SKILL.md +132 -0
- package/skills/json-render-react/SKILL.md +264 -0
- package/skills/json-render-shadcn/SKILL.md +159 -0
- package/skills/playwright-cli/SKILL.md +67 -0
- package/skills/pmx-canvas/SKILL.md +668 -0
- package/skills/pmx-canvas/evals/evals.json +186 -0
- package/skills/pmx-canvas-testing/SKILL.md +78 -0
- package/skills/published-consumer-e2e/SKILL.md +43 -0
- package/skills/published-consumer-e2e/scripts/run-published-consumer-e2e.sh +241 -0
- package/skills/web-artifacts-builder/SKILL.md +80 -0
- package/skills/web-artifacts-builder/scripts/bundle-artifact.sh +167 -0
- package/skills/web-artifacts-builder/scripts/init-artifact.sh +425 -0
- package/skills/web-artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
- package/skills/web-design-guidelines/SKILL.md +39 -0
- package/src/cli/agent.ts +2144 -0
- package/src/cli/index.ts +622 -0
- package/src/cli/watch.ts +88 -0
- package/src/client/App.tsx +507 -0
- package/src/client/canvas/AttentionHistory.tsx +81 -0
- package/src/client/canvas/AttentionToast.tsx +19 -0
- package/src/client/canvas/CanvasNode.tsx +363 -0
- package/src/client/canvas/CanvasViewport.tsx +590 -0
- package/src/client/canvas/CommandPalette.tsx +302 -0
- package/src/client/canvas/ContextMenu.tsx +601 -0
- package/src/client/canvas/ContextPinBar.tsx +25 -0
- package/src/client/canvas/ContextPinHud.tsx +22 -0
- package/src/client/canvas/DockedNode.tsx +66 -0
- package/src/client/canvas/EdgeLayer.tsx +280 -0
- package/src/client/canvas/ExpandedNodeOverlay.tsx +260 -0
- package/src/client/canvas/FocusFieldLayer.tsx +107 -0
- package/src/client/canvas/Minimap.tsx +301 -0
- package/src/client/canvas/SelectionBar.tsx +69 -0
- package/src/client/canvas/ShortcutOverlay.tsx +69 -0
- package/src/client/canvas/SnapshotPanel.tsx +236 -0
- package/src/client/canvas/snap-guides.ts +170 -0
- package/src/client/canvas/use-node-drag.ts +51 -0
- package/src/client/canvas/use-node-resize.ts +59 -0
- package/src/client/canvas/use-pan-zoom.ts +191 -0
- package/src/client/ext-app/bridge.ts +542 -0
- package/src/client/icons.tsx +424 -0
- package/src/client/index.tsx +7 -0
- package/src/client/nodes/ContextNode.tsx +412 -0
- package/src/client/nodes/ExtAppFrame.tsx +509 -0
- package/src/client/nodes/FileNode.tsx +256 -0
- package/src/client/nodes/GroupNode.tsx +39 -0
- package/src/client/nodes/ImageNode.tsx +160 -0
- package/src/client/nodes/InlineFormatBar.tsx +169 -0
- package/src/client/nodes/InlineMarkdownEditor.tsx +123 -0
- package/src/client/nodes/LedgerNode.tsx +37 -0
- package/src/client/nodes/MarkdownNode.tsx +359 -0
- package/src/client/nodes/McpAppNode.tsx +85 -0
- package/src/client/nodes/MdFormatBar.tsx +109 -0
- package/src/client/nodes/PromptNode.tsx +597 -0
- package/src/client/nodes/ResponseNode.tsx +153 -0
- package/src/client/nodes/StatusNode.tsx +84 -0
- package/src/client/nodes/StatusSummary.tsx +38 -0
- package/src/client/nodes/TraceNode.tsx +120 -0
- package/src/client/nodes/WebpageNode.tsx +288 -0
- package/src/client/nodes/image-warnings.ts +95 -0
- package/src/client/nodes/inline-editor-commands.ts +37 -0
- package/src/client/nodes/md-format.ts +206 -0
- package/src/client/state/attention-bridge.ts +328 -0
- package/src/client/state/attention-store.ts +73 -0
- package/src/client/state/canvas-store.ts +631 -0
- package/src/client/state/intent-bridge.ts +315 -0
- package/src/client/state/sse-bridge.ts +965 -0
- package/src/client/theme/global.css +3173 -0
- package/src/client/theme/tokens.ts +72 -0
- package/src/client/types-shims.d.ts +5 -0
- package/src/client/types.ts +81 -0
- package/src/client/utils/ext-app-tool-result.ts +4 -0
- package/src/client/utils/placement.ts +4 -0
- package/src/client/utils/platform.ts +2 -0
- package/src/json-render/catalog.ts +256 -0
- package/src/json-render/charts/components.tsx +198 -0
- package/src/json-render/charts/definitions.ts +81 -0
- package/src/json-render/charts/extra-components.tsx +267 -0
- package/src/json-render/charts/extra-definitions.ts +145 -0
- package/src/json-render/renderer/index.css +174 -0
- package/src/json-render/renderer/index.tsx +86 -0
- package/src/json-render/schema.ts +62 -0
- package/src/json-render/server.ts +597 -0
- package/src/mcp/server.ts +1377 -0
- package/src/server/agent-context.ts +242 -0
- package/src/server/artifact-paths.ts +17 -0
- package/src/server/canvas-operations.ts +1279 -0
- package/src/server/canvas-provenance.ts +243 -0
- package/src/server/canvas-schema.ts +432 -0
- package/src/server/canvas-serialization.ts +95 -0
- package/src/server/canvas-state.ts +1134 -0
- package/src/server/canvas-validation.ts +114 -0
- package/src/server/chart-template.ts +449 -0
- package/src/server/code-graph.ts +370 -0
- package/src/server/context-cards.ts +31 -0
- package/src/server/diagram-presets.ts +71 -0
- package/src/server/ext-app-call-registry.ts +77 -0
- package/src/server/ext-app-tool-result.ts +4 -0
- package/src/server/file-watcher.ts +121 -0
- package/src/server/index.ts +647 -0
- package/src/server/mcp-app-candidate.ts +174 -0
- package/src/server/mcp-app-host.ts +814 -0
- package/src/server/mcp-app-runtime.ts +459 -0
- package/src/server/mutation-history.ts +350 -0
- package/src/server/placement.ts +125 -0
- package/src/server/server.ts +3846 -0
- package/src/server/spatial-analysis.ts +356 -0
- package/src/server/trace-manager.ts +333 -0
- package/src/server/web-artifacts/scripts/bundle-artifact.sh +167 -0
- package/src/server/web-artifacts/scripts/init-artifact.sh +426 -0
- package/src/server/web-artifacts/scripts/shadcn-components.tar.gz +0 -0
- package/src/server/web-artifacts.ts +442 -0
- package/src/server/webpage-node.ts +328 -0
- package/src/shared/auto-arrange.ts +439 -0
- package/src/shared/ext-app-tool-result.ts +76 -0
- package/src/shared/placement.ts +81 -0
- package/src/shared/semantic-attention.ts +598 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chart component definitions for json-render catalogs.
|
|
3
|
+
*
|
|
4
|
+
* Provides LineChart, BarChart, and PieChart components built on Recharts.
|
|
5
|
+
* Mirrors the chart definitions from the Vercel json-render chat example.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
|
|
10
|
+
const cartesianProps = z.object({
|
|
11
|
+
title: z.string().nullable(),
|
|
12
|
+
data: z.array(z.record(z.string(), z.unknown())),
|
|
13
|
+
xKey: z.string(),
|
|
14
|
+
yKey: z.string(),
|
|
15
|
+
aggregate: z.enum(['sum', 'count', 'avg']).nullable(),
|
|
16
|
+
color: z.string().nullable(),
|
|
17
|
+
height: z.number().nullable(),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export const chartComponentDefinitions = {
|
|
21
|
+
LineChart: {
|
|
22
|
+
props: cartesianProps,
|
|
23
|
+
description:
|
|
24
|
+
'Line chart for time-series or trend data. Provide data as an array of objects with xKey and yKey fields.',
|
|
25
|
+
example: {
|
|
26
|
+
title: 'Weekly trend',
|
|
27
|
+
data: [
|
|
28
|
+
{ day: 'Mon', value: 10 },
|
|
29
|
+
{ day: 'Tue', value: 25 },
|
|
30
|
+
{ day: 'Wed', value: 18 },
|
|
31
|
+
],
|
|
32
|
+
xKey: 'day',
|
|
33
|
+
yKey: 'value',
|
|
34
|
+
aggregate: null,
|
|
35
|
+
color: null,
|
|
36
|
+
height: null,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
BarChart: {
|
|
41
|
+
props: cartesianProps,
|
|
42
|
+
description:
|
|
43
|
+
'Bar chart for comparing categories. Provide data as an array of objects with xKey and yKey fields.',
|
|
44
|
+
example: {
|
|
45
|
+
title: 'Sales by region',
|
|
46
|
+
data: [
|
|
47
|
+
{ region: 'North', sales: 120 },
|
|
48
|
+
{ region: 'South', sales: 98 },
|
|
49
|
+
{ region: 'East', sales: 150 },
|
|
50
|
+
],
|
|
51
|
+
xKey: 'region',
|
|
52
|
+
yKey: 'sales',
|
|
53
|
+
aggregate: null,
|
|
54
|
+
color: null,
|
|
55
|
+
height: null,
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
PieChart: {
|
|
60
|
+
props: z.object({
|
|
61
|
+
title: z.string().nullable(),
|
|
62
|
+
data: z.array(z.record(z.string(), z.unknown())),
|
|
63
|
+
nameKey: z.string(),
|
|
64
|
+
valueKey: z.string(),
|
|
65
|
+
height: z.number().nullable(),
|
|
66
|
+
}),
|
|
67
|
+
description:
|
|
68
|
+
'Pie chart for showing proportions. Provide data as an array of objects with nameKey and valueKey fields.',
|
|
69
|
+
example: {
|
|
70
|
+
title: 'Market share',
|
|
71
|
+
data: [
|
|
72
|
+
{ name: 'Product A', share: 45 },
|
|
73
|
+
{ name: 'Product B', share: 30 },
|
|
74
|
+
{ name: 'Product C', share: 25 },
|
|
75
|
+
],
|
|
76
|
+
nameKey: 'name',
|
|
77
|
+
valueKey: 'share',
|
|
78
|
+
height: null,
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
} as const;
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/** @jsxImportSource react */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Additional chart components for json-render.
|
|
5
|
+
*
|
|
6
|
+
* Lives alongside ./components.tsx so the original chart set stays
|
|
7
|
+
* unchanged and the merge point in ./catalog.ts is the only contact
|
|
8
|
+
* surface with the upstream `@json-render/*` packages.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { BaseComponentProps } from '@json-render/react';
|
|
12
|
+
import {
|
|
13
|
+
Area,
|
|
14
|
+
AreaChart as RechartsAreaChart,
|
|
15
|
+
Bar,
|
|
16
|
+
BarChart as RechartsBarChart,
|
|
17
|
+
CartesianGrid,
|
|
18
|
+
ComposedChart as RechartsComposedChart,
|
|
19
|
+
Legend,
|
|
20
|
+
Line,
|
|
21
|
+
PolarAngleAxis,
|
|
22
|
+
PolarGrid,
|
|
23
|
+
PolarRadiusAxis,
|
|
24
|
+
Radar,
|
|
25
|
+
RadarChart as RechartsRadarChart,
|
|
26
|
+
ResponsiveContainer,
|
|
27
|
+
Scatter,
|
|
28
|
+
ScatterChart as RechartsScatterChart,
|
|
29
|
+
Tooltip,
|
|
30
|
+
XAxis,
|
|
31
|
+
YAxis,
|
|
32
|
+
ZAxis,
|
|
33
|
+
} from 'recharts';
|
|
34
|
+
import {
|
|
35
|
+
CHART_COLORS,
|
|
36
|
+
CartesianChart,
|
|
37
|
+
axisStyle,
|
|
38
|
+
tooltipStyle,
|
|
39
|
+
type CartesianChartProps,
|
|
40
|
+
} from './components';
|
|
41
|
+
|
|
42
|
+
type AreaChartProps = CartesianChartProps;
|
|
43
|
+
|
|
44
|
+
function ChartAreaChart({ props }: BaseComponentProps<AreaChartProps>) {
|
|
45
|
+
const stroke = props.color ?? CHART_COLORS[0];
|
|
46
|
+
const gradientId = `pmx-area-${props.yKey ?? 'value'}`;
|
|
47
|
+
return (
|
|
48
|
+
<CartesianChart props={props}>
|
|
49
|
+
{(data) => (
|
|
50
|
+
<RechartsAreaChart data={data}>
|
|
51
|
+
<defs>
|
|
52
|
+
<linearGradient id={gradientId} x1="0" y1="0" x2="0" y2="1">
|
|
53
|
+
<stop offset="0%" stopColor={stroke} stopOpacity={0.45} />
|
|
54
|
+
<stop offset="100%" stopColor={stroke} stopOpacity={0.05} />
|
|
55
|
+
</linearGradient>
|
|
56
|
+
</defs>
|
|
57
|
+
<CartesianGrid strokeDasharray="3 3" stroke="var(--border, #e5e5e5)" />
|
|
58
|
+
<XAxis dataKey={props.xKey} tick={axisStyle} />
|
|
59
|
+
<YAxis tick={axisStyle} />
|
|
60
|
+
<Tooltip contentStyle={tooltipStyle} />
|
|
61
|
+
<Area
|
|
62
|
+
type="monotone"
|
|
63
|
+
dataKey={props.yKey}
|
|
64
|
+
stroke={stroke}
|
|
65
|
+
strokeWidth={2}
|
|
66
|
+
fill={`url(#${gradientId})`}
|
|
67
|
+
activeDot={{ r: 5 }}
|
|
68
|
+
/>
|
|
69
|
+
</RechartsAreaChart>
|
|
70
|
+
)}
|
|
71
|
+
</CartesianChart>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
interface ScatterChartProps {
|
|
76
|
+
title?: string | null;
|
|
77
|
+
data: Record<string, unknown>[];
|
|
78
|
+
xKey: string;
|
|
79
|
+
yKey: string;
|
|
80
|
+
zKey?: string | null;
|
|
81
|
+
color?: string | null;
|
|
82
|
+
height?: number | null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function ChartScatterChart({ props }: BaseComponentProps<ScatterChartProps>) {
|
|
86
|
+
const fill = props.color ?? CHART_COLORS[0];
|
|
87
|
+
const data = props.data ?? [];
|
|
88
|
+
const h = props.height ?? 300;
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<div className="pmx-chart">
|
|
92
|
+
{props.title && <div className="pmx-chart__title">{props.title}</div>}
|
|
93
|
+
<ResponsiveContainer width="100%" height={h}>
|
|
94
|
+
<RechartsScatterChart>
|
|
95
|
+
<CartesianGrid strokeDasharray="3 3" stroke="var(--border, #e5e5e5)" />
|
|
96
|
+
<XAxis type="number" dataKey={props.xKey} tick={axisStyle} name={props.xKey} />
|
|
97
|
+
<YAxis type="number" dataKey={props.yKey} tick={axisStyle} name={props.yKey} />
|
|
98
|
+
{props.zKey && <ZAxis type="number" dataKey={props.zKey} range={[40, 400]} name={props.zKey} />}
|
|
99
|
+
<Tooltip contentStyle={tooltipStyle} cursor={{ strokeDasharray: '3 3' }} />
|
|
100
|
+
<Scatter data={data} fill={fill} />
|
|
101
|
+
</RechartsScatterChart>
|
|
102
|
+
</ResponsiveContainer>
|
|
103
|
+
</div>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
interface RadarChartProps {
|
|
108
|
+
title?: string | null;
|
|
109
|
+
data: Record<string, unknown>[];
|
|
110
|
+
axisKey: string;
|
|
111
|
+
metrics: string[];
|
|
112
|
+
height?: number | null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function ChartRadarChart({ props }: BaseComponentProps<RadarChartProps>) {
|
|
116
|
+
const data = props.data ?? [];
|
|
117
|
+
const metrics = (props.metrics ?? []).filter((m) => typeof m === 'string' && m.length > 0);
|
|
118
|
+
const h = props.height ?? 320;
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<div className="pmx-chart">
|
|
122
|
+
{props.title && <div className="pmx-chart__title">{props.title}</div>}
|
|
123
|
+
<ResponsiveContainer width="100%" height={h}>
|
|
124
|
+
<RechartsRadarChart data={data} outerRadius="75%">
|
|
125
|
+
<PolarGrid stroke="var(--border, #e5e5e5)" />
|
|
126
|
+
<PolarAngleAxis dataKey={props.axisKey} tick={axisStyle} />
|
|
127
|
+
<PolarRadiusAxis tick={axisStyle} />
|
|
128
|
+
<Tooltip contentStyle={tooltipStyle} />
|
|
129
|
+
<Legend />
|
|
130
|
+
{metrics.map((metric, i) => {
|
|
131
|
+
const color = CHART_COLORS[i % CHART_COLORS.length];
|
|
132
|
+
return (
|
|
133
|
+
<Radar
|
|
134
|
+
key={metric}
|
|
135
|
+
name={metric}
|
|
136
|
+
dataKey={metric}
|
|
137
|
+
stroke={color}
|
|
138
|
+
fill={color}
|
|
139
|
+
fillOpacity={0.25}
|
|
140
|
+
/>
|
|
141
|
+
);
|
|
142
|
+
})}
|
|
143
|
+
</RechartsRadarChart>
|
|
144
|
+
</ResponsiveContainer>
|
|
145
|
+
</div>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
interface StackedBarChartProps {
|
|
150
|
+
title?: string | null;
|
|
151
|
+
data: Record<string, unknown>[];
|
|
152
|
+
xKey: string;
|
|
153
|
+
series: string[];
|
|
154
|
+
aggregate?: 'sum' | 'count' | 'avg' | null;
|
|
155
|
+
height?: number | null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function ChartStackedBarChart({ props }: BaseComponentProps<StackedBarChartProps>) {
|
|
159
|
+
const series = (props.series ?? []).filter((s) => typeof s === 'string' && s.length > 0);
|
|
160
|
+
const chartData = props.aggregate
|
|
161
|
+
? mergeAggregated(props.data ?? [], props.xKey, series, props.aggregate)
|
|
162
|
+
: props.data ?? [];
|
|
163
|
+
const h = props.height ?? 300;
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<div className="pmx-chart">
|
|
167
|
+
{props.title && <div className="pmx-chart__title">{props.title}</div>}
|
|
168
|
+
<ResponsiveContainer width="100%" height={h}>
|
|
169
|
+
<RechartsBarChart data={chartData}>
|
|
170
|
+
<CartesianGrid strokeDasharray="3 3" stroke="var(--border, #e5e5e5)" />
|
|
171
|
+
<XAxis dataKey={props.xKey} tick={axisStyle} />
|
|
172
|
+
<YAxis tick={axisStyle} />
|
|
173
|
+
<Tooltip contentStyle={tooltipStyle} cursor={false} />
|
|
174
|
+
<Legend />
|
|
175
|
+
{series.map((key, i) => (
|
|
176
|
+
<Bar
|
|
177
|
+
key={key}
|
|
178
|
+
dataKey={key}
|
|
179
|
+
stackId="stack"
|
|
180
|
+
fill={CHART_COLORS[i % CHART_COLORS.length]}
|
|
181
|
+
radius={i === series.length - 1 ? [4, 4, 0, 0] : [0, 0, 0, 0]}
|
|
182
|
+
/>
|
|
183
|
+
))}
|
|
184
|
+
</RechartsBarChart>
|
|
185
|
+
</ResponsiveContainer>
|
|
186
|
+
</div>
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function mergeAggregated(
|
|
191
|
+
data: Record<string, unknown>[],
|
|
192
|
+
xKey: string,
|
|
193
|
+
series: string[],
|
|
194
|
+
aggregate: 'sum' | 'count' | 'avg',
|
|
195
|
+
): Record<string, unknown>[] {
|
|
196
|
+
const grouped = new Map<string, Record<string, number[]>>();
|
|
197
|
+
for (const row of data) {
|
|
198
|
+
const k = String(row[xKey] ?? '');
|
|
199
|
+
if (!grouped.has(k)) grouped.set(k, {});
|
|
200
|
+
const bucket = grouped.get(k)!;
|
|
201
|
+
for (const s of series) {
|
|
202
|
+
const n = Number(row[s] ?? 0);
|
|
203
|
+
if (!bucket[s]) bucket[s] = [];
|
|
204
|
+
bucket[s].push(Number.isNaN(n) ? 0 : n);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
const reducer = aggregate === 'count'
|
|
208
|
+
? (vs: number[]) => vs.length
|
|
209
|
+
: aggregate === 'avg'
|
|
210
|
+
? (vs: number[]) => vs.reduce((a, b) => a + b, 0) / vs.length
|
|
211
|
+
: (vs: number[]) => vs.reduce((a, b) => a + b, 0);
|
|
212
|
+
return Array.from(grouped.entries()).map(([key, buckets]) => {
|
|
213
|
+
const out: Record<string, unknown> = { [xKey]: key };
|
|
214
|
+
for (const s of series) out[s] = reducer(buckets[s] ?? []);
|
|
215
|
+
return out;
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
interface ComposedChartProps {
|
|
220
|
+
title?: string | null;
|
|
221
|
+
data: Record<string, unknown>[];
|
|
222
|
+
xKey: string;
|
|
223
|
+
barKey: string;
|
|
224
|
+
lineKey: string;
|
|
225
|
+
barColor?: string | null;
|
|
226
|
+
lineColor?: string | null;
|
|
227
|
+
height?: number | null;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function ChartComposedChart({ props }: BaseComponentProps<ComposedChartProps>) {
|
|
231
|
+
const data = props.data ?? [];
|
|
232
|
+
const barFill = props.barColor ?? CHART_COLORS[0];
|
|
233
|
+
const lineStroke = props.lineColor ?? CHART_COLORS[3];
|
|
234
|
+
const h = props.height ?? 300;
|
|
235
|
+
|
|
236
|
+
return (
|
|
237
|
+
<div className="pmx-chart">
|
|
238
|
+
{props.title && <div className="pmx-chart__title">{props.title}</div>}
|
|
239
|
+
<ResponsiveContainer width="100%" height={h}>
|
|
240
|
+
<RechartsComposedChart data={data}>
|
|
241
|
+
<CartesianGrid strokeDasharray="3 3" stroke="var(--border, #e5e5e5)" />
|
|
242
|
+
<XAxis dataKey={props.xKey} tick={axisStyle} />
|
|
243
|
+
<YAxis tick={axisStyle} />
|
|
244
|
+
<Tooltip contentStyle={tooltipStyle} cursor={false} />
|
|
245
|
+
<Legend />
|
|
246
|
+
<Bar dataKey={props.barKey} fill={barFill} radius={[4, 4, 0, 0]} />
|
|
247
|
+
<Line
|
|
248
|
+
type="monotone"
|
|
249
|
+
dataKey={props.lineKey}
|
|
250
|
+
stroke={lineStroke}
|
|
251
|
+
strokeWidth={2}
|
|
252
|
+
dot={{ r: 3, fill: lineStroke }}
|
|
253
|
+
activeDot={{ r: 5 }}
|
|
254
|
+
/>
|
|
255
|
+
</RechartsComposedChart>
|
|
256
|
+
</ResponsiveContainer>
|
|
257
|
+
</div>
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export const extraChartComponents = {
|
|
262
|
+
AreaChart: ChartAreaChart,
|
|
263
|
+
ScatterChart: ChartScatterChart,
|
|
264
|
+
RadarChart: ChartRadarChart,
|
|
265
|
+
StackedBarChart: ChartStackedBarChart,
|
|
266
|
+
ComposedChart: ChartComposedChart,
|
|
267
|
+
};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Definitions for the extra chart components in ./extra-components.tsx.
|
|
3
|
+
*
|
|
4
|
+
* Kept separate from ./definitions.ts so the original chart catalog stays
|
|
5
|
+
* untouched and the merge in ./catalog.ts is the only contact surface.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
|
|
10
|
+
const cartesianProps = z.object({
|
|
11
|
+
title: z.string().nullable(),
|
|
12
|
+
data: z.array(z.record(z.string(), z.unknown())),
|
|
13
|
+
xKey: z.string(),
|
|
14
|
+
yKey: z.string(),
|
|
15
|
+
aggregate: z.enum(['sum', 'count', 'avg']).nullable(),
|
|
16
|
+
color: z.string().nullable(),
|
|
17
|
+
height: z.number().nullable(),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export const extraChartComponentDefinitions = {
|
|
21
|
+
AreaChart: {
|
|
22
|
+
props: cartesianProps,
|
|
23
|
+
description:
|
|
24
|
+
'Area chart for cumulative or trend data. Same shape as LineChart but draws a filled area under the line.',
|
|
25
|
+
example: {
|
|
26
|
+
title: 'Daily signups',
|
|
27
|
+
data: [
|
|
28
|
+
{ day: 'Mon', value: 12 },
|
|
29
|
+
{ day: 'Tue', value: 24 },
|
|
30
|
+
{ day: 'Wed', value: 19 },
|
|
31
|
+
{ day: 'Thu', value: 31 },
|
|
32
|
+
],
|
|
33
|
+
xKey: 'day',
|
|
34
|
+
yKey: 'value',
|
|
35
|
+
aggregate: null,
|
|
36
|
+
color: null,
|
|
37
|
+
height: null,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
ScatterChart: {
|
|
42
|
+
props: z.object({
|
|
43
|
+
title: z.string().nullable(),
|
|
44
|
+
data: z.array(z.record(z.string(), z.unknown())),
|
|
45
|
+
xKey: z.string(),
|
|
46
|
+
yKey: z.string(),
|
|
47
|
+
zKey: z.string().nullable(),
|
|
48
|
+
color: z.string().nullable(),
|
|
49
|
+
height: z.number().nullable(),
|
|
50
|
+
}),
|
|
51
|
+
description:
|
|
52
|
+
'Scatter plot for correlation or distribution. Both axes are numeric; optional zKey scales point size.',
|
|
53
|
+
example: {
|
|
54
|
+
title: 'Latency vs payload size',
|
|
55
|
+
data: [
|
|
56
|
+
{ size: 10, latency: 25 },
|
|
57
|
+
{ size: 40, latency: 80 },
|
|
58
|
+
{ size: 80, latency: 110 },
|
|
59
|
+
{ size: 120, latency: 180 },
|
|
60
|
+
],
|
|
61
|
+
xKey: 'size',
|
|
62
|
+
yKey: 'latency',
|
|
63
|
+
zKey: null,
|
|
64
|
+
color: null,
|
|
65
|
+
height: null,
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
RadarChart: {
|
|
70
|
+
props: z.object({
|
|
71
|
+
title: z.string().nullable(),
|
|
72
|
+
data: z.array(z.record(z.string(), z.unknown())),
|
|
73
|
+
axisKey: z.string(),
|
|
74
|
+
metrics: z.array(z.string()),
|
|
75
|
+
height: z.number().nullable(),
|
|
76
|
+
}),
|
|
77
|
+
description:
|
|
78
|
+
'Radar chart for comparing multiple metrics across categories. Each metric in `metrics` is plotted as its own polygon.',
|
|
79
|
+
example: {
|
|
80
|
+
title: 'Skill comparison',
|
|
81
|
+
data: [
|
|
82
|
+
{ skill: 'Speed', alice: 80, bob: 60 },
|
|
83
|
+
{ skill: 'Accuracy', alice: 70, bob: 90 },
|
|
84
|
+
{ skill: 'Stamina', alice: 85, bob: 75 },
|
|
85
|
+
],
|
|
86
|
+
axisKey: 'skill',
|
|
87
|
+
metrics: ['alice', 'bob'],
|
|
88
|
+
height: null,
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
StackedBarChart: {
|
|
93
|
+
props: z.object({
|
|
94
|
+
title: z.string().nullable(),
|
|
95
|
+
data: z.array(z.record(z.string(), z.unknown())),
|
|
96
|
+
xKey: z.string(),
|
|
97
|
+
series: z.array(z.string()),
|
|
98
|
+
aggregate: z.enum(['sum', 'count', 'avg']).nullable(),
|
|
99
|
+
height: z.number().nullable(),
|
|
100
|
+
}),
|
|
101
|
+
description:
|
|
102
|
+
'Stacked bar chart for compositional data. Each entry in `series` is plotted as its own bar segment per x value.',
|
|
103
|
+
example: {
|
|
104
|
+
title: 'Revenue by region',
|
|
105
|
+
data: [
|
|
106
|
+
{ quarter: 'Q1', north: 30, south: 18, east: 22 },
|
|
107
|
+
{ quarter: 'Q2', north: 42, south: 25, east: 28 },
|
|
108
|
+
{ quarter: 'Q3', north: 38, south: 30, east: 26 },
|
|
109
|
+
],
|
|
110
|
+
xKey: 'quarter',
|
|
111
|
+
series: ['north', 'south', 'east'],
|
|
112
|
+
aggregate: null,
|
|
113
|
+
height: null,
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
ComposedChart: {
|
|
118
|
+
props: z.object({
|
|
119
|
+
title: z.string().nullable(),
|
|
120
|
+
data: z.array(z.record(z.string(), z.unknown())),
|
|
121
|
+
xKey: z.string(),
|
|
122
|
+
barKey: z.string(),
|
|
123
|
+
lineKey: z.string(),
|
|
124
|
+
barColor: z.string().nullable(),
|
|
125
|
+
lineColor: z.string().nullable(),
|
|
126
|
+
height: z.number().nullable(),
|
|
127
|
+
}),
|
|
128
|
+
description:
|
|
129
|
+
'Combined bar + line chart for paired metrics (e.g. counts + a derived rate) on the same axis.',
|
|
130
|
+
example: {
|
|
131
|
+
title: 'Visits and conversion',
|
|
132
|
+
data: [
|
|
133
|
+
{ day: 'Mon', visits: 120, conversion: 4.2 },
|
|
134
|
+
{ day: 'Tue', visits: 145, conversion: 3.8 },
|
|
135
|
+
{ day: 'Wed', visits: 160, conversion: 5.1 },
|
|
136
|
+
],
|
|
137
|
+
xKey: 'day',
|
|
138
|
+
barKey: 'visits',
|
|
139
|
+
lineKey: 'conversion',
|
|
140
|
+
barColor: null,
|
|
141
|
+
lineColor: null,
|
|
142
|
+
height: null,
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
} as const;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
@import "tw-animate-css";
|
|
3
|
+
|
|
4
|
+
@source "../../../../node_modules/@json-render/shadcn/dist/index.js";
|
|
5
|
+
|
|
6
|
+
@custom-variant dark (&:is([data-theme="dark"] *));
|
|
7
|
+
|
|
8
|
+
:root {
|
|
9
|
+
--radius: 0.5rem;
|
|
10
|
+
--background: oklch(1 0 0);
|
|
11
|
+
--foreground: oklch(0.1 0 0);
|
|
12
|
+
--card: oklch(0.98 0 0);
|
|
13
|
+
--card-foreground: oklch(0.1 0 0);
|
|
14
|
+
--popover: oklch(0.98 0 0);
|
|
15
|
+
--popover-foreground: oklch(0.1 0 0);
|
|
16
|
+
--primary: oklch(0.1 0 0);
|
|
17
|
+
--primary-foreground: oklch(1 0 0);
|
|
18
|
+
--secondary: oklch(0.92 0 0);
|
|
19
|
+
--secondary-foreground: oklch(0.1 0 0);
|
|
20
|
+
--muted: oklch(0.92 0 0);
|
|
21
|
+
--muted-foreground: oklch(0.45 0 0);
|
|
22
|
+
--accent: oklch(0.92 0 0);
|
|
23
|
+
--accent-foreground: oklch(0.1 0 0);
|
|
24
|
+
--destructive: oklch(0.55 0.2 25);
|
|
25
|
+
--destructive-foreground: oklch(1 0 0);
|
|
26
|
+
--border: oklch(0.85 0 0);
|
|
27
|
+
--input: oklch(0.85 0 0);
|
|
28
|
+
--ring: oklch(0.6 0 0);
|
|
29
|
+
/* Slot 1 mirrors the canvas accent (light theme); slots 2-6 keep
|
|
30
|
+
enough hue variety to distinguish series. */
|
|
31
|
+
--chart-1: #1A7ABF;
|
|
32
|
+
--chart-2: #1a9f55;
|
|
33
|
+
--chart-3: #c89b2a;
|
|
34
|
+
--chart-4: #7c4dff;
|
|
35
|
+
--chart-5: #d32f2f;
|
|
36
|
+
--chart-6: #00838F;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
:root[data-theme='dark'] {
|
|
40
|
+
--background: oklch(0 0 0);
|
|
41
|
+
--foreground: oklch(0.98 0 0);
|
|
42
|
+
--card: oklch(0.08 0 0);
|
|
43
|
+
--card-foreground: oklch(0.98 0 0);
|
|
44
|
+
--popover: oklch(0.08 0 0);
|
|
45
|
+
--popover-foreground: oklch(0.98 0 0);
|
|
46
|
+
--primary: oklch(0.98 0 0);
|
|
47
|
+
--primary-foreground: oklch(0 0 0);
|
|
48
|
+
--secondary: oklch(0.15 0 0);
|
|
49
|
+
--secondary-foreground: oklch(0.98 0 0);
|
|
50
|
+
--muted: oklch(0.15 0 0);
|
|
51
|
+
--muted-foreground: oklch(0.6 0 0);
|
|
52
|
+
--accent: oklch(0.15 0 0);
|
|
53
|
+
--accent-foreground: oklch(0.98 0 0);
|
|
54
|
+
--destructive: oklch(0.65 0.2 25);
|
|
55
|
+
--destructive-foreground: oklch(0.98 0 0);
|
|
56
|
+
--border: oklch(0.25 0 0);
|
|
57
|
+
--input: oklch(0.25 0 0);
|
|
58
|
+
--ring: oklch(0.4 0 0);
|
|
59
|
+
/* Dark theme: slot 1 = canvas cyan accent. */
|
|
60
|
+
--chart-1: #4BBCFF;
|
|
61
|
+
--chart-2: #2fd07f;
|
|
62
|
+
--chart-3: #f4c542;
|
|
63
|
+
--chart-4: #b07aff;
|
|
64
|
+
--chart-5: #ff6a7f;
|
|
65
|
+
--chart-6: #00E5FF;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
:root[data-theme='high-contrast'] {
|
|
69
|
+
--background: oklch(0 0 0);
|
|
70
|
+
--foreground: oklch(1 0 0);
|
|
71
|
+
--card: oklch(0.05 0 0);
|
|
72
|
+
--card-foreground: oklch(1 0 0);
|
|
73
|
+
--popover: oklch(0.05 0 0);
|
|
74
|
+
--popover-foreground: oklch(1 0 0);
|
|
75
|
+
--primary: oklch(1 0 0);
|
|
76
|
+
--primary-foreground: oklch(0 0 0);
|
|
77
|
+
--secondary: oklch(0.12 0 0);
|
|
78
|
+
--secondary-foreground: oklch(1 0 0);
|
|
79
|
+
--muted: oklch(0.12 0 0);
|
|
80
|
+
--muted-foreground: oklch(0.7 0 0);
|
|
81
|
+
--accent: oklch(0.12 0 0);
|
|
82
|
+
--accent-foreground: oklch(1 0 0);
|
|
83
|
+
--destructive: oklch(0.55 0.25 25);
|
|
84
|
+
--destructive-foreground: oklch(1 0 0);
|
|
85
|
+
--border: oklch(0.5 0 0);
|
|
86
|
+
--input: oklch(0.5 0 0);
|
|
87
|
+
--ring: oklch(0.7 0 0);
|
|
88
|
+
/* High-contrast: pure primaries that survive the harsh background. */
|
|
89
|
+
--chart-1: #00ffff;
|
|
90
|
+
--chart-2: #00ff00;
|
|
91
|
+
--chart-3: #ffff00;
|
|
92
|
+
--chart-4: #e040fb;
|
|
93
|
+
--chart-5: #ff8000;
|
|
94
|
+
--chart-6: #ffffff;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@theme inline {
|
|
98
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
99
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
100
|
+
--radius-lg: var(--radius);
|
|
101
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
102
|
+
--color-background: var(--background);
|
|
103
|
+
--color-foreground: var(--foreground);
|
|
104
|
+
--color-card: var(--card);
|
|
105
|
+
--color-card-foreground: var(--card-foreground);
|
|
106
|
+
--color-popover: var(--popover);
|
|
107
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
108
|
+
--color-primary: var(--primary);
|
|
109
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
110
|
+
--color-secondary: var(--secondary);
|
|
111
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
112
|
+
--color-muted: var(--muted);
|
|
113
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
114
|
+
--color-accent: var(--accent);
|
|
115
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
116
|
+
--color-destructive: var(--destructive);
|
|
117
|
+
--color-destructive-foreground: var(--destructive-foreground);
|
|
118
|
+
--color-border: var(--border);
|
|
119
|
+
--color-input: var(--input);
|
|
120
|
+
--color-ring: var(--ring);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
@layer base {
|
|
124
|
+
* {
|
|
125
|
+
@apply border-border;
|
|
126
|
+
box-sizing: border-box;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
html,
|
|
130
|
+
body,
|
|
131
|
+
#root {
|
|
132
|
+
min-height: 100%;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
body {
|
|
136
|
+
@apply bg-background text-foreground antialiased;
|
|
137
|
+
margin: 0;
|
|
138
|
+
font-family: system-ui, sans-serif;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
button {
|
|
143
|
+
cursor: pointer;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* -- Chart components -- */
|
|
147
|
+
|
|
148
|
+
.pmx-chart {
|
|
149
|
+
width: 100%;
|
|
150
|
+
padding: 0.5rem 0;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.pmx-chart__title {
|
|
154
|
+
font-size: 0.875rem;
|
|
155
|
+
font-weight: 600;
|
|
156
|
+
color: var(--foreground);
|
|
157
|
+
margin-bottom: 0.75rem;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/* Recharts overrides for theme integration */
|
|
161
|
+
.recharts-cartesian-grid-horizontal line,
|
|
162
|
+
.recharts-cartesian-grid-vertical line {
|
|
163
|
+
stroke: var(--border);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.recharts-default-tooltip {
|
|
167
|
+
background-color: var(--popover) !important;
|
|
168
|
+
border-color: var(--border) !important;
|
|
169
|
+
color: var(--popover-foreground) !important;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.recharts-legend-item-text {
|
|
173
|
+
color: var(--foreground) !important;
|
|
174
|
+
}
|