pmx-canvas 0.1.10 → 0.1.12
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 +131 -0
- package/dist/canvas/index.js +30 -30
- package/dist/json-render/index.js +115 -115
- package/dist/types/json-render/catalog.d.ts +10 -0
- package/dist/types/json-render/charts/components.d.ts +18 -0
- package/dist/types/json-render/charts/definitions.d.ts +4 -0
- package/dist/types/json-render/charts/extra-components.d.ts +3 -0
- package/dist/types/json-render/charts/extra-definitions.d.ts +6 -0
- package/dist/types/json-render/server.d.ts +4 -0
- package/dist/types/mcp/canvas-access.d.ts +87 -0
- package/dist/types/server/canvas-operations.d.ts +2 -0
- package/dist/types/server/index.d.ts +2 -0
- package/dist/types/server/server.d.ts +1 -0
- package/package.json +1 -1
- package/skills/pmx-canvas/SKILL.md +9 -0
- package/src/cli/agent.ts +78 -2
- package/src/cli/index.ts +6 -3
- package/src/client/canvas/CanvasNode.tsx +3 -1
- package/src/client/canvas/auto-fit.ts +1 -1
- package/src/json-render/charts/components.tsx +18 -10
- package/src/json-render/charts/definitions.ts +4 -0
- package/src/json-render/charts/extra-components.tsx +23 -16
- package/src/json-render/charts/extra-definitions.ts +6 -0
- package/src/json-render/server.ts +11 -0
- package/src/mcp/canvas-access.ts +651 -0
- package/src/mcp/server.ts +170 -95
- package/src/server/canvas-operations.ts +21 -1
- package/src/server/canvas-schema.ts +5 -0
- package/src/server/diagram-presets.ts +44 -12
- package/src/server/index.ts +9 -3
- package/src/server/server.ts +37 -3
|
@@ -84,6 +84,8 @@ interface PieChartProps {
|
|
|
84
84
|
nameKey: string;
|
|
85
85
|
valueKey: string;
|
|
86
86
|
height?: number | null;
|
|
87
|
+
showLegend?: boolean | null;
|
|
88
|
+
showLabels?: boolean | null;
|
|
87
89
|
}
|
|
88
90
|
|
|
89
91
|
export const axisStyle = {
|
|
@@ -99,6 +101,11 @@ export const tooltipStyle = {
|
|
|
99
101
|
fontSize: 13,
|
|
100
102
|
};
|
|
101
103
|
|
|
104
|
+
export const chartMargin = { top: 14, right: 28, bottom: 28, left: 10 };
|
|
105
|
+
export const polarChartMargin = { top: 18, right: 40, bottom: 30, left: 40 };
|
|
106
|
+
export const axisTickMargin = 8;
|
|
107
|
+
export const legendMargin = { top: 10 };
|
|
108
|
+
|
|
102
109
|
/** Shared wrapper for cartesian charts (Line + Bar). */
|
|
103
110
|
export function CartesianChart({
|
|
104
111
|
props,
|
|
@@ -127,10 +134,10 @@ function ChartLineChart({ props }: BaseComponentProps<CartesianChartProps>) {
|
|
|
127
134
|
return (
|
|
128
135
|
<CartesianChart props={props} className="pmx-chart--line">
|
|
129
136
|
{(data) => (
|
|
130
|
-
<RechartsLineChart data={data}>
|
|
137
|
+
<RechartsLineChart data={data} margin={chartMargin}>
|
|
131
138
|
<CartesianGrid strokeDasharray="3 3" stroke="var(--border, #e5e5e5)" />
|
|
132
|
-
<XAxis dataKey={props.xKey} tick={axisStyle} />
|
|
133
|
-
<YAxis tick={axisStyle} />
|
|
139
|
+
<XAxis dataKey={props.xKey} tick={axisStyle} tickMargin={axisTickMargin} />
|
|
140
|
+
<YAxis tick={axisStyle} tickMargin={axisTickMargin} />
|
|
134
141
|
<Tooltip contentStyle={tooltipStyle} />
|
|
135
142
|
<Line
|
|
136
143
|
type="monotone"
|
|
@@ -151,10 +158,10 @@ function ChartBarChart({ props }: BaseComponentProps<CartesianChartProps>) {
|
|
|
151
158
|
return (
|
|
152
159
|
<CartesianChart props={props} className="pmx-chart--bar">
|
|
153
160
|
{(data) => (
|
|
154
|
-
<RechartsBarChart data={data}>
|
|
161
|
+
<RechartsBarChart data={data} margin={chartMargin}>
|
|
155
162
|
<CartesianGrid strokeDasharray="3 3" stroke="var(--border, #e5e5e5)" />
|
|
156
|
-
<XAxis dataKey={props.xKey} tick={axisStyle} />
|
|
157
|
-
<YAxis tick={axisStyle} />
|
|
163
|
+
<XAxis dataKey={props.xKey} tick={axisStyle} tickMargin={axisTickMargin} />
|
|
164
|
+
<YAxis tick={axisStyle} tickMargin={axisTickMargin} />
|
|
158
165
|
<Tooltip contentStyle={tooltipStyle} cursor={false} />
|
|
159
166
|
<Bar dataKey={props.yKey} fill={fill} radius={[4, 4, 0, 0]} />
|
|
160
167
|
</RechartsBarChart>
|
|
@@ -171,17 +178,18 @@ function ChartPieChart({ props }: BaseComponentProps<PieChartProps>) {
|
|
|
171
178
|
<div className="pmx-chart pmx-chart--pie">
|
|
172
179
|
{props.title && <div className="pmx-chart__title">{props.title}</div>}
|
|
173
180
|
<ResponsiveContainer width="100%" height={h}>
|
|
174
|
-
<RechartsPieChart>
|
|
181
|
+
<RechartsPieChart margin={polarChartMargin}>
|
|
175
182
|
<Tooltip contentStyle={tooltipStyle} />
|
|
176
|
-
<Legend />
|
|
183
|
+
{props.showLegend !== false && <Legend wrapperStyle={legendMargin} />}
|
|
177
184
|
<Pie
|
|
178
185
|
data={data}
|
|
179
186
|
dataKey={props.valueKey}
|
|
180
187
|
nameKey={props.nameKey}
|
|
181
188
|
cx="50%"
|
|
182
189
|
cy="50%"
|
|
183
|
-
outerRadius="
|
|
184
|
-
label
|
|
190
|
+
outerRadius="64%"
|
|
191
|
+
label={props.showLabels !== false}
|
|
192
|
+
labelLine={false}
|
|
185
193
|
>
|
|
186
194
|
{data.map((_, i) => (
|
|
187
195
|
<Cell key={i} fill={CHART_COLORS[i % CHART_COLORS.length]} />
|
|
@@ -63,6 +63,8 @@ export const chartComponentDefinitions = {
|
|
|
63
63
|
nameKey: z.string(),
|
|
64
64
|
valueKey: z.string(),
|
|
65
65
|
height: z.number().nullable(),
|
|
66
|
+
showLegend: z.boolean().optional(),
|
|
67
|
+
showLabels: z.boolean().optional(),
|
|
66
68
|
}),
|
|
67
69
|
description:
|
|
68
70
|
'Pie chart for showing proportions. Provide data as an array of objects with nameKey and valueKey fields.',
|
|
@@ -76,6 +78,8 @@ export const chartComponentDefinitions = {
|
|
|
76
78
|
nameKey: 'name',
|
|
77
79
|
valueKey: 'share',
|
|
78
80
|
height: null,
|
|
81
|
+
showLegend: true,
|
|
82
|
+
showLabels: true,
|
|
79
83
|
},
|
|
80
84
|
},
|
|
81
85
|
} as const;
|
|
@@ -34,7 +34,11 @@ import {
|
|
|
34
34
|
import {
|
|
35
35
|
CHART_COLORS,
|
|
36
36
|
CartesianChart,
|
|
37
|
+
axisTickMargin,
|
|
37
38
|
axisStyle,
|
|
39
|
+
chartMargin,
|
|
40
|
+
legendMargin,
|
|
41
|
+
polarChartMargin,
|
|
38
42
|
tooltipStyle,
|
|
39
43
|
type CartesianChartProps,
|
|
40
44
|
} from './components';
|
|
@@ -47,7 +51,7 @@ function ChartAreaChart({ props }: BaseComponentProps<AreaChartProps>) {
|
|
|
47
51
|
return (
|
|
48
52
|
<CartesianChart props={props} className="pmx-chart--area">
|
|
49
53
|
{(data) => (
|
|
50
|
-
<RechartsAreaChart data={data}>
|
|
54
|
+
<RechartsAreaChart data={data} margin={chartMargin}>
|
|
51
55
|
<defs>
|
|
52
56
|
<linearGradient id={gradientId} x1="0" y1="0" x2="0" y2="1">
|
|
53
57
|
<stop offset="0%" stopColor={stroke} stopOpacity={0.45} />
|
|
@@ -55,8 +59,8 @@ function ChartAreaChart({ props }: BaseComponentProps<AreaChartProps>) {
|
|
|
55
59
|
</linearGradient>
|
|
56
60
|
</defs>
|
|
57
61
|
<CartesianGrid strokeDasharray="3 3" stroke="var(--border, #e5e5e5)" />
|
|
58
|
-
<XAxis dataKey={props.xKey} tick={axisStyle} />
|
|
59
|
-
<YAxis tick={axisStyle} />
|
|
62
|
+
<XAxis dataKey={props.xKey} tick={axisStyle} tickMargin={axisTickMargin} />
|
|
63
|
+
<YAxis tick={axisStyle} tickMargin={axisTickMargin} />
|
|
60
64
|
<Tooltip contentStyle={tooltipStyle} />
|
|
61
65
|
<Area
|
|
62
66
|
type="monotone"
|
|
@@ -91,10 +95,10 @@ function ChartScatterChart({ props }: BaseComponentProps<ScatterChartProps>) {
|
|
|
91
95
|
<div className="pmx-chart pmx-chart--scatter">
|
|
92
96
|
{props.title && <div className="pmx-chart__title">{props.title}</div>}
|
|
93
97
|
<ResponsiveContainer width="100%" height={h}>
|
|
94
|
-
<RechartsScatterChart>
|
|
98
|
+
<RechartsScatterChart margin={chartMargin}>
|
|
95
99
|
<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} />
|
|
100
|
+
<XAxis type="number" dataKey={props.xKey} tick={axisStyle} tickMargin={axisTickMargin} name={props.xKey} />
|
|
101
|
+
<YAxis type="number" dataKey={props.yKey} tick={axisStyle} tickMargin={axisTickMargin} name={props.yKey} />
|
|
98
102
|
{props.zKey && <ZAxis type="number" dataKey={props.zKey} range={[40, 400]} name={props.zKey} />}
|
|
99
103
|
<Tooltip contentStyle={tooltipStyle} cursor={{ strokeDasharray: '3 3' }} />
|
|
100
104
|
<Scatter data={data} fill={fill} />
|
|
@@ -110,6 +114,7 @@ interface RadarChartProps {
|
|
|
110
114
|
axisKey: string;
|
|
111
115
|
metrics: string[];
|
|
112
116
|
height?: number | null;
|
|
117
|
+
showLegend?: boolean | null;
|
|
113
118
|
}
|
|
114
119
|
|
|
115
120
|
function ChartRadarChart({ props }: BaseComponentProps<RadarChartProps>) {
|
|
@@ -121,12 +126,12 @@ function ChartRadarChart({ props }: BaseComponentProps<RadarChartProps>) {
|
|
|
121
126
|
<div className="pmx-chart pmx-chart--radar">
|
|
122
127
|
{props.title && <div className="pmx-chart__title">{props.title}</div>}
|
|
123
128
|
<ResponsiveContainer width="100%" height={h}>
|
|
124
|
-
<RechartsRadarChart data={data} outerRadius="
|
|
129
|
+
<RechartsRadarChart data={data} outerRadius="66%" margin={polarChartMargin}>
|
|
125
130
|
<PolarGrid stroke="var(--border, #e5e5e5)" />
|
|
126
131
|
<PolarAngleAxis dataKey={props.axisKey} tick={axisStyle} />
|
|
127
132
|
<PolarRadiusAxis tick={axisStyle} />
|
|
128
133
|
<Tooltip contentStyle={tooltipStyle} />
|
|
129
|
-
<Legend />
|
|
134
|
+
{props.showLegend !== false && <Legend wrapperStyle={legendMargin} />}
|
|
130
135
|
{metrics.map((metric, i) => {
|
|
131
136
|
const color = CHART_COLORS[i % CHART_COLORS.length];
|
|
132
137
|
return (
|
|
@@ -153,6 +158,7 @@ interface StackedBarChartProps {
|
|
|
153
158
|
series: string[];
|
|
154
159
|
aggregate?: 'sum' | 'count' | 'avg' | null;
|
|
155
160
|
height?: number | null;
|
|
161
|
+
showLegend?: boolean | null;
|
|
156
162
|
}
|
|
157
163
|
|
|
158
164
|
function ChartStackedBarChart({ props }: BaseComponentProps<StackedBarChartProps>) {
|
|
@@ -166,12 +172,12 @@ function ChartStackedBarChart({ props }: BaseComponentProps<StackedBarChartProps
|
|
|
166
172
|
<div className="pmx-chart pmx-chart--stacked-bar">
|
|
167
173
|
{props.title && <div className="pmx-chart__title">{props.title}</div>}
|
|
168
174
|
<ResponsiveContainer width="100%" height={h}>
|
|
169
|
-
<RechartsBarChart data={chartData}>
|
|
175
|
+
<RechartsBarChart data={chartData} margin={chartMargin}>
|
|
170
176
|
<CartesianGrid strokeDasharray="3 3" stroke="var(--border, #e5e5e5)" />
|
|
171
|
-
<XAxis dataKey={props.xKey} tick={axisStyle} />
|
|
172
|
-
<YAxis tick={axisStyle} />
|
|
177
|
+
<XAxis dataKey={props.xKey} tick={axisStyle} tickMargin={axisTickMargin} />
|
|
178
|
+
<YAxis tick={axisStyle} tickMargin={axisTickMargin} />
|
|
173
179
|
<Tooltip contentStyle={tooltipStyle} cursor={false} />
|
|
174
|
-
<Legend />
|
|
180
|
+
{props.showLegend !== false && <Legend wrapperStyle={legendMargin} />}
|
|
175
181
|
{series.map((key, i) => (
|
|
176
182
|
<Bar
|
|
177
183
|
key={key}
|
|
@@ -225,6 +231,7 @@ interface ComposedChartProps {
|
|
|
225
231
|
barColor?: string | null;
|
|
226
232
|
lineColor?: string | null;
|
|
227
233
|
height?: number | null;
|
|
234
|
+
showLegend?: boolean | null;
|
|
228
235
|
}
|
|
229
236
|
|
|
230
237
|
function ChartComposedChart({ props }: BaseComponentProps<ComposedChartProps>) {
|
|
@@ -237,12 +244,12 @@ function ChartComposedChart({ props }: BaseComponentProps<ComposedChartProps>) {
|
|
|
237
244
|
<div className="pmx-chart pmx-chart--composed">
|
|
238
245
|
{props.title && <div className="pmx-chart__title">{props.title}</div>}
|
|
239
246
|
<ResponsiveContainer width="100%" height={h}>
|
|
240
|
-
<RechartsComposedChart data={data}>
|
|
247
|
+
<RechartsComposedChart data={data} margin={chartMargin}>
|
|
241
248
|
<CartesianGrid strokeDasharray="3 3" stroke="var(--border, #e5e5e5)" />
|
|
242
|
-
<XAxis dataKey={props.xKey} tick={axisStyle} />
|
|
243
|
-
<YAxis tick={axisStyle} />
|
|
249
|
+
<XAxis dataKey={props.xKey} tick={axisStyle} tickMargin={axisTickMargin} />
|
|
250
|
+
<YAxis tick={axisStyle} tickMargin={axisTickMargin} />
|
|
244
251
|
<Tooltip contentStyle={tooltipStyle} cursor={false} />
|
|
245
|
-
<Legend />
|
|
252
|
+
{props.showLegend !== false && <Legend wrapperStyle={legendMargin} />}
|
|
246
253
|
<Bar dataKey={props.barKey} fill={barFill} radius={[4, 4, 0, 0]} />
|
|
247
254
|
<Line
|
|
248
255
|
type="monotone"
|
|
@@ -73,6 +73,7 @@ export const extraChartComponentDefinitions = {
|
|
|
73
73
|
axisKey: z.string(),
|
|
74
74
|
metrics: z.array(z.string()),
|
|
75
75
|
height: z.number().nullable(),
|
|
76
|
+
showLegend: z.boolean().optional(),
|
|
76
77
|
}),
|
|
77
78
|
description:
|
|
78
79
|
'Radar chart for comparing multiple metrics across categories. Each metric in `metrics` is plotted as its own polygon.',
|
|
@@ -86,6 +87,7 @@ export const extraChartComponentDefinitions = {
|
|
|
86
87
|
axisKey: 'skill',
|
|
87
88
|
metrics: ['alice', 'bob'],
|
|
88
89
|
height: null,
|
|
90
|
+
showLegend: true,
|
|
89
91
|
},
|
|
90
92
|
},
|
|
91
93
|
|
|
@@ -97,6 +99,7 @@ export const extraChartComponentDefinitions = {
|
|
|
97
99
|
series: z.array(z.string()),
|
|
98
100
|
aggregate: z.enum(['sum', 'count', 'avg']).nullable(),
|
|
99
101
|
height: z.number().nullable(),
|
|
102
|
+
showLegend: z.boolean().optional(),
|
|
100
103
|
}),
|
|
101
104
|
description:
|
|
102
105
|
'Stacked bar chart for compositional data. Each entry in `series` is plotted as its own bar segment per x value.',
|
|
@@ -111,6 +114,7 @@ export const extraChartComponentDefinitions = {
|
|
|
111
114
|
series: ['north', 'south', 'east'],
|
|
112
115
|
aggregate: null,
|
|
113
116
|
height: null,
|
|
117
|
+
showLegend: true,
|
|
114
118
|
},
|
|
115
119
|
},
|
|
116
120
|
|
|
@@ -124,6 +128,7 @@ export const extraChartComponentDefinitions = {
|
|
|
124
128
|
barColor: z.string().nullable(),
|
|
125
129
|
lineColor: z.string().nullable(),
|
|
126
130
|
height: z.number().nullable(),
|
|
131
|
+
showLegend: z.boolean().optional(),
|
|
127
132
|
}),
|
|
128
133
|
description:
|
|
129
134
|
'Combined bar + line chart for paired metrics (e.g. counts + a derived rate) on the same axis.',
|
|
@@ -140,6 +145,7 @@ export const extraChartComponentDefinitions = {
|
|
|
140
145
|
barColor: null,
|
|
141
146
|
lineColor: null,
|
|
142
147
|
height: null,
|
|
148
|
+
showLegend: true,
|
|
143
149
|
},
|
|
144
150
|
},
|
|
145
151
|
} as const;
|
|
@@ -17,6 +17,7 @@ export interface JsonRenderNodeInput {
|
|
|
17
17
|
y?: number;
|
|
18
18
|
width?: number;
|
|
19
19
|
height?: number;
|
|
20
|
+
strictSize?: boolean;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
export interface GraphNodeInput {
|
|
@@ -38,10 +39,13 @@ export interface GraphNodeInput {
|
|
|
38
39
|
barColor?: string;
|
|
39
40
|
lineColor?: string;
|
|
40
41
|
height?: number;
|
|
42
|
+
showLegend?: boolean;
|
|
43
|
+
showLabels?: boolean;
|
|
41
44
|
x?: number;
|
|
42
45
|
y?: number;
|
|
43
46
|
width?: number;
|
|
44
47
|
heightPx?: number;
|
|
48
|
+
strictSize?: boolean;
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
export const JSON_RENDER_NODE_SIZE = { width: 840, height: 620 };
|
|
@@ -514,6 +518,8 @@ export function buildGraphSpec(input: GraphNodeInput): JsonRenderSpec {
|
|
|
514
518
|
case 'PieChart': {
|
|
515
519
|
chartProps.nameKey = input.nameKey ?? 'name';
|
|
516
520
|
chartProps.valueKey = input.valueKey ?? 'value';
|
|
521
|
+
chartProps.showLegend = input.showLegend !== false;
|
|
522
|
+
chartProps.showLabels = input.showLabels !== false;
|
|
517
523
|
break;
|
|
518
524
|
}
|
|
519
525
|
case 'ScatterChart': {
|
|
@@ -533,6 +539,7 @@ export function buildGraphSpec(input: GraphNodeInput): JsonRenderSpec {
|
|
|
533
539
|
}
|
|
534
540
|
chartProps.axisKey = axisKey;
|
|
535
541
|
chartProps.metrics = metrics;
|
|
542
|
+
chartProps.showLegend = input.showLegend !== false;
|
|
536
543
|
break;
|
|
537
544
|
}
|
|
538
545
|
case 'StackedBarChart': {
|
|
@@ -546,6 +553,7 @@ export function buildGraphSpec(input: GraphNodeInput): JsonRenderSpec {
|
|
|
546
553
|
chartProps.xKey = xKey;
|
|
547
554
|
chartProps.series = series;
|
|
548
555
|
chartProps.aggregate = input.aggregate ?? null;
|
|
556
|
+
chartProps.showLegend = input.showLegend !== false;
|
|
549
557
|
break;
|
|
550
558
|
}
|
|
551
559
|
case 'ComposedChart': {
|
|
@@ -556,6 +564,7 @@ export function buildGraphSpec(input: GraphNodeInput): JsonRenderSpec {
|
|
|
556
564
|
?? 'rate';
|
|
557
565
|
chartProps.barColor = input.barColor ?? null;
|
|
558
566
|
chartProps.lineColor = input.lineColor ?? null;
|
|
567
|
+
chartProps.showLegend = input.showLegend !== false;
|
|
559
568
|
break;
|
|
560
569
|
}
|
|
561
570
|
case 'AreaChart':
|
|
@@ -613,6 +622,8 @@ export function buildGraphConfig(input: GraphNodeInput): Record<string, unknown>
|
|
|
613
622
|
...(input.barColor ? { barColor: input.barColor } : {}),
|
|
614
623
|
...(input.lineColor ? { lineColor: input.lineColor } : {}),
|
|
615
624
|
...(typeof input.height === 'number' ? { height: input.height } : {}),
|
|
625
|
+
...(typeof input.showLegend === 'boolean' ? { showLegend: input.showLegend } : {}),
|
|
626
|
+
...(typeof input.showLabels === 'boolean' ? { showLabels: input.showLabels } : {}),
|
|
616
627
|
};
|
|
617
628
|
}
|
|
618
629
|
|