@unovis/mcp 1.7.0-mcp.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.
Files changed (153) hide show
  1. package/LICENSE +193 -0
  2. package/README.md +154 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +109 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/codegen/index.d.ts +14 -0
  7. package/dist/codegen/index.js +346 -0
  8. package/dist/codegen/index.js.map +1 -0
  9. package/dist/env/bbox.d.ts +13 -0
  10. package/dist/env/bbox.js +335 -0
  11. package/dist/env/bbox.js.map +1 -0
  12. package/dist/env/canvas.d.ts +9 -0
  13. package/dist/env/canvas.js +55 -0
  14. package/dist/env/canvas.js.map +1 -0
  15. package/dist/env/computed-style.d.ts +38 -0
  16. package/dist/env/computed-style.js +186 -0
  17. package/dist/env/computed-style.js.map +1 -0
  18. package/dist/env/fonts.d.ts +3 -0
  19. package/dist/env/fonts.js +103 -0
  20. package/dist/env/fonts.js.map +1 -0
  21. package/dist/env/index.d.ts +13 -0
  22. package/dist/env/index.js +121 -0
  23. package/dist/env/index.js.map +1 -0
  24. package/dist/env/raf-queue.d.ts +23 -0
  25. package/dist/env/raf-queue.js +51 -0
  26. package/dist/env/raf-queue.js.map +1 -0
  27. package/dist/env/size.d.ts +9 -0
  28. package/dist/env/size.js +30 -0
  29. package/dist/env/size.js.map +1 -0
  30. package/dist/html/document.d.ts +16 -0
  31. package/dist/html/document.js +95 -0
  32. package/dist/html/document.js.map +1 -0
  33. package/dist/index.d.ts +25 -0
  34. package/dist/index.js +18 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/recipes/area.d.ts +66 -0
  37. package/dist/recipes/area.js +76 -0
  38. package/dist/recipes/area.js.map +1 -0
  39. package/dist/recipes/bar.d.ts +67 -0
  40. package/dist/recipes/bar.js +69 -0
  41. package/dist/recipes/bar.js.map +1 -0
  42. package/dist/recipes/boxplot.d.ts +64 -0
  43. package/dist/recipes/boxplot.js +89 -0
  44. package/dist/recipes/boxplot.js.map +1 -0
  45. package/dist/recipes/chord.d.ts +41 -0
  46. package/dist/recipes/chord.js +72 -0
  47. package/dist/recipes/chord.js.map +1 -0
  48. package/dist/recipes/choropleth-regions.d.ts +49 -0
  49. package/dist/recipes/choropleth-regions.js +521 -0
  50. package/dist/recipes/choropleth-regions.js.map +1 -0
  51. package/dist/recipes/choropleth.d.ts +27 -0
  52. package/dist/recipes/choropleth.js +86 -0
  53. package/dist/recipes/choropleth.js.map +1 -0
  54. package/dist/recipes/donut.d.ts +25 -0
  55. package/dist/recipes/donut.js +51 -0
  56. package/dist/recipes/donut.js.map +1 -0
  57. package/dist/recipes/graph.d.ts +55 -0
  58. package/dist/recipes/graph.js +132 -0
  59. package/dist/recipes/graph.js.map +1 -0
  60. package/dist/recipes/heatmap.d.ts +21 -0
  61. package/dist/recipes/heatmap.js +79 -0
  62. package/dist/recipes/heatmap.js.map +1 -0
  63. package/dist/recipes/index.d.ts +5 -0
  64. package/dist/recipes/index.js +35 -0
  65. package/dist/recipes/index.js.map +1 -0
  66. package/dist/recipes/line.d.ts +69 -0
  67. package/dist/recipes/line.js +64 -0
  68. package/dist/recipes/line.js.map +1 -0
  69. package/dist/recipes/nested-donut.d.ts +22 -0
  70. package/dist/recipes/nested-donut.js +47 -0
  71. package/dist/recipes/nested-donut.js.map +1 -0
  72. package/dist/recipes/radial-bar.d.ts +25 -0
  73. package/dist/recipes/radial-bar.js +53 -0
  74. package/dist/recipes/radial-bar.js.map +1 -0
  75. package/dist/recipes/sankey.d.ts +41 -0
  76. package/dist/recipes/sankey.js +61 -0
  77. package/dist/recipes/sankey.js.map +1 -0
  78. package/dist/recipes/scatter.d.ts +68 -0
  79. package/dist/recipes/scatter.js +75 -0
  80. package/dist/recipes/scatter.js.map +1 -0
  81. package/dist/recipes/shared.d.ts +144 -0
  82. package/dist/recipes/shared.js +212 -0
  83. package/dist/recipes/shared.js.map +1 -0
  84. package/dist/recipes/timeline.d.ts +31 -0
  85. package/dist/recipes/timeline.js +123 -0
  86. package/dist/recipes/timeline.js.map +1 -0
  87. package/dist/recipes/treemap.d.ts +19 -0
  88. package/dist/recipes/treemap.js +43 -0
  89. package/dist/recipes/treemap.js.map +1 -0
  90. package/dist/recipes/types.d.ts +16 -0
  91. package/dist/recipes/types.js +2 -0
  92. package/dist/recipes/types.js.map +1 -0
  93. package/dist/render/headless.d.ts +62 -0
  94. package/dist/render/headless.js +158 -0
  95. package/dist/render/headless.js.map +1 -0
  96. package/dist/render/materialize.d.ts +35 -0
  97. package/dist/render/materialize.js +160 -0
  98. package/dist/render/materialize.js.map +1 -0
  99. package/dist/render/mutex.d.ts +9 -0
  100. package/dist/render/mutex.js +14 -0
  101. package/dist/render/mutex.js.map +1 -0
  102. package/dist/render/rasterize.d.ts +10 -0
  103. package/dist/render/rasterize.js +29 -0
  104. package/dist/render/rasterize.js.map +1 -0
  105. package/dist/render/renderer.d.ts +21 -0
  106. package/dist/render/renderer.js +66 -0
  107. package/dist/render/renderer.js.map +1 -0
  108. package/dist/render/spec.d.ts +95 -0
  109. package/dist/render/spec.js +15 -0
  110. package/dist/render/spec.js.map +1 -0
  111. package/dist/server.d.ts +4 -0
  112. package/dist/server.js +44 -0
  113. package/dist/server.js.map +1 -0
  114. package/dist/svg/collect-css.d.ts +14 -0
  115. package/dist/svg/collect-css.js +53 -0
  116. package/dist/svg/collect-css.js.map +1 -0
  117. package/dist/svg/css-vars.d.ts +17 -0
  118. package/dist/svg/css-vars.js +64 -0
  119. package/dist/svg/css-vars.js.map +1 -0
  120. package/dist/svg/header.d.ts +8 -0
  121. package/dist/svg/header.js +81 -0
  122. package/dist/svg/header.js.map +1 -0
  123. package/dist/svg/ids.d.ts +12 -0
  124. package/dist/svg/ids.js +94 -0
  125. package/dist/svg/ids.js.map +1 -0
  126. package/dist/svg/postprocess.d.ts +41 -0
  127. package/dist/svg/postprocess.js +135 -0
  128. package/dist/svg/postprocess.js.map +1 -0
  129. package/dist/tools/register.d.ts +12 -0
  130. package/dist/tools/register.js +127 -0
  131. package/dist/tools/register.js.map +1 -0
  132. package/dist/widget/bundle.js +1777 -0
  133. package/dist/widget/bundle.meta.json +3 -0
  134. package/dist/widget/entry.d.ts +34 -0
  135. package/dist/widget/entry.js +118 -0
  136. package/dist/widget/entry.js.map +1 -0
  137. package/dist/widget/interactions.d.ts +14 -0
  138. package/dist/widget/interactions.js +151 -0
  139. package/dist/widget/interactions.js.map +1 -0
  140. package/dist/widget/unovis-slim.d.ts +33 -0
  141. package/dist/widget/unovis-slim.js +34 -0
  142. package/dist/widget/unovis-slim.js.map +1 -0
  143. package/docs/README.md +56 -0
  144. package/docs/architecture.md +158 -0
  145. package/docs/chart-spec.md +148 -0
  146. package/docs/getting-started.md +175 -0
  147. package/docs/interactive.md +125 -0
  148. package/docs/output-types.md +170 -0
  149. package/docs/programmatic.md +234 -0
  150. package/docs/tools.md +273 -0
  151. package/docs/troubleshooting.md +140 -0
  152. package/fonts/README.md +19 -0
  153. package/package.json +73 -0
@@ -0,0 +1,76 @@
1
+ import { z } from 'zod';
2
+ import { commonInput, xyInput, dataRecords, fieldName, assertFieldsExist, baseSpec, categoricalX, categoryAxis, field, seriesLegend, timeTickValuesFromData, toArray, xyAxes, xyDecorations, decorationComponents, } from './shared.js';
3
+ /** Series color: custom palette entries cycle; otherwise theme palette variables
4
+ * (resolved to literal colors per theme during SVG post-processing) */
5
+ const seriesColor = (i, colors) => colors?.length ? colors[i % colors.length] : `var(--vis-color${i % 6})`;
6
+ export const areaInputShape = {
7
+ data: dataRecords,
8
+ x: fieldName.describe('Field for X values: numbers, or date strings when xIsTime is true, or category names'),
9
+ y: z.union([fieldName, z.array(fieldName).min(1).max(12)])
10
+ .describe('Field name(s) for Y values. Multiple field names render multiple areas'),
11
+ stacked: z.boolean().default(true)
12
+ .describe('Stack multiple y series on top of each other. When false, series overlap with transparency'),
13
+ curve: z.enum(['linear', 'monotoneX', 'basis', 'natural', 'step', 'stepAfter', 'stepBefore']).default('monotoneX')
14
+ .describe('Area interpolation type'),
15
+ xIsTime: z.boolean().default(false).describe('Treat x values as dates/timestamps (time axis)'),
16
+ seriesLabels: z.array(z.string()).optional().describe('Display names for the y series (legend). Defaults to field names'),
17
+ ...xyDecorations,
18
+ ...xyInput,
19
+ ...commonInput,
20
+ };
21
+ export const areaRecipe = {
22
+ name: 'generate_area_chart',
23
+ title: 'Area chart',
24
+ description: 'Generate an area chart for volumes/magnitudes over a continuous or time dimension. ' +
25
+ 'Multiple y fields are stacked by default; set stacked=false to overlay them with transparency. ' +
26
+ 'Example: data=[{"month":"2024-01","mobile":120,"desktop":80},...], x="month", xIsTime=true, y=["mobile","desktop"].',
27
+ inputShape: areaInputShape,
28
+ toSpec: (input) => {
29
+ const data = input.data;
30
+ const yFields = toArray(input.y);
31
+ assertFieldsExist(data, [input.x, ...yFields]);
32
+ const { x, categories } = categoricalX(data, input.x, input.xIsTime);
33
+ const axes = xyAxes(input);
34
+ const labels = input.seriesLabels?.length ? input.seriesLabels : yFields;
35
+ // Stacked: one Area component with multiple y accessors (colored by the
36
+ // palette per stack layer). Overlaid: one semi-transparent Area component
37
+ // per series — each needs an explicit color, otherwise all would resolve
38
+ // to the first palette color.
39
+ const overlaid = !input.stacked && yFields.length > 1;
40
+ const components = overlaid
41
+ ? yFields.map((f, i) => ({
42
+ type: 'Area',
43
+ config: {
44
+ x,
45
+ y: field(f, 'number'),
46
+ curveType: input.curve,
47
+ opacity: 0.45,
48
+ color: seriesColor(i, input.colors),
49
+ },
50
+ }))
51
+ : [{
52
+ type: 'Area',
53
+ config: {
54
+ x,
55
+ y: yFields.map(f => field(f, 'number')),
56
+ curveType: input.curve,
57
+ },
58
+ }];
59
+ const deco = decorationComponents(input);
60
+ return {
61
+ container: 'xy',
62
+ ...baseSpec(input),
63
+ components: [...deco.bands, ...components, ...deco.lines],
64
+ xAxis: {
65
+ ...(categories ? categoryAxis(axes.xAxis, categories) : axes.xAxis),
66
+ ...(input.xIsTime
67
+ ? { tickFormat: { $dateTickFormat: true }, tickValues: timeTickValuesFromData(data, input.x) }
68
+ : {}),
69
+ },
70
+ yAxis: { ...axes.yAxis, tickFormat: { $numTickFormat: true } },
71
+ legend: seriesLegend(labels, input.legend, input.colors),
72
+ data,
73
+ };
74
+ },
75
+ };
76
+ //# sourceMappingURL=area.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"area.js","sourceRoot":"","sources":["../../src/recipes/area.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ import { z } from 'zod';
2
+ import type { Recipe } from './types.js';
3
+ export declare const barInputShape: {
4
+ width: z.ZodDefault<z.ZodNumber>;
5
+ height: z.ZodDefault<z.ZodNumber>;
6
+ theme: z.ZodDefault<z.ZodEnum<["light", "dark"]>>;
7
+ title: z.ZodOptional<z.ZodString>;
8
+ colors: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
9
+ framework: z.ZodDefault<z.ZodEnum<["ts", "react", "svelte", "vue", "angular", "solid"]>>;
10
+ outputType: z.ZodDefault<z.ZodEnum<["svg", "png", "html", "interactive", "config", "code"]>>;
11
+ outputPath: z.ZodOptional<z.ZodString>;
12
+ scale: z.ZodDefault<z.ZodNumber>;
13
+ xAxisLabel: z.ZodOptional<z.ZodString>;
14
+ yAxisLabel: z.ZodOptional<z.ZodString>;
15
+ showGridLines: z.ZodDefault<z.ZodBoolean>;
16
+ legend: z.ZodDefault<z.ZodBoolean>;
17
+ referenceLines: z.ZodOptional<z.ZodArray<z.ZodObject<{
18
+ axis: z.ZodDefault<z.ZodEnum<["x", "y"]>>;
19
+ value: z.ZodNumber;
20
+ label: z.ZodOptional<z.ZodString>;
21
+ color: z.ZodOptional<z.ZodString>;
22
+ lineWidth: z.ZodDefault<z.ZodNumber>;
23
+ style: z.ZodDefault<z.ZodEnum<["dashed", "solid", "dotted"]>>;
24
+ }, "strip", z.ZodTypeAny, {
25
+ value: number;
26
+ style: "solid" | "dashed" | "dotted";
27
+ axis: "x" | "y";
28
+ lineWidth: number;
29
+ label?: string | undefined;
30
+ color?: string | undefined;
31
+ }, {
32
+ value: number;
33
+ label?: string | undefined;
34
+ color?: string | undefined;
35
+ style?: "solid" | "dashed" | "dotted" | undefined;
36
+ axis?: "x" | "y" | undefined;
37
+ lineWidth?: number | undefined;
38
+ }>, "many">>;
39
+ referenceBands: z.ZodOptional<z.ZodArray<z.ZodObject<{
40
+ axis: z.ZodDefault<z.ZodEnum<["x", "y"]>>;
41
+ from: z.ZodNumber;
42
+ to: z.ZodNumber;
43
+ label: z.ZodOptional<z.ZodString>;
44
+ color: z.ZodOptional<z.ZodString>;
45
+ }, "strip", z.ZodTypeAny, {
46
+ axis: "x" | "y";
47
+ from: number;
48
+ to: number;
49
+ label?: string | undefined;
50
+ color?: string | undefined;
51
+ }, {
52
+ from: number;
53
+ to: number;
54
+ label?: string | undefined;
55
+ color?: string | undefined;
56
+ axis?: "x" | "y" | undefined;
57
+ }>, "many">>;
58
+ data: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>, "many">;
59
+ x: z.ZodString;
60
+ y: z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">]>;
61
+ type: z.ZodDefault<z.ZodEnum<["grouped", "stacked"]>>;
62
+ orientation: z.ZodDefault<z.ZodEnum<["vertical", "horizontal"]>>;
63
+ seriesLabels: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
64
+ roundedCorners: z.ZodDefault<z.ZodBoolean>;
65
+ barPadding: z.ZodOptional<z.ZodNumber>;
66
+ };
67
+ export declare const barRecipe: Recipe<typeof barInputShape>;
@@ -0,0 +1,69 @@
1
+ import { z } from 'zod';
2
+ import { commonInput, xyInput, dataRecords, fieldName, assertFieldsExist, baseSpec, categoricalX, categoryAxis, field, seriesLegend, toArray, xyAxes, xyDecorations, decorationComponents, } from './shared.js';
3
+ export const barInputShape = {
4
+ data: dataRecords,
5
+ x: fieldName.describe('Field for the category dimension (names or numbers)'),
6
+ y: z.union([fieldName, z.array(fieldName).min(1).max(12)])
7
+ .describe('Field name(s) for bar values. Multiple field names render one bar (or stack segment) per series'),
8
+ type: z.enum(['grouped', 'stacked']).default('grouped')
9
+ .describe('How multiple series are arranged: side-by-side groups or stacks'),
10
+ orientation: z.enum(['vertical', 'horizontal']).default('vertical')
11
+ .describe('Bar direction. Horizontal puts categories on the Y axis'),
12
+ seriesLabels: z.array(z.string()).optional().describe('Display names for the series (legend). Defaults to field names'),
13
+ roundedCorners: z.boolean().default(true).describe('Round the outer bar corners'),
14
+ barPadding: z.number().min(0).max(0.9).optional().describe('Padding between bars within a group, 0..0.9'),
15
+ ...xyDecorations,
16
+ ...xyInput,
17
+ ...commonInput,
18
+ };
19
+ export const barRecipe = {
20
+ name: 'generate_bar_chart',
21
+ title: 'Bar chart',
22
+ description: 'Generate a bar chart: single-series, grouped, or stacked; vertical or horizontal. ' +
23
+ 'Use for comparing values across categories. ' +
24
+ 'Example: data=[{"country":"US","gold":9,"silver":8},...], x="country", y=["gold","silver"], type="stacked".',
25
+ inputShape: barInputShape,
26
+ toSpec: (input) => {
27
+ const data = input.data;
28
+ const yFields = toArray(input.y);
29
+ assertFieldsExist(data, [input.x, ...yFields]);
30
+ const horizontal = input.orientation === 'horizontal';
31
+ const { x, categories } = categoricalX(data, input.x);
32
+ const labels = input.seriesLabels?.length ? input.seriesLabels : yFields;
33
+ const axes = xyAxes(input);
34
+ // The category axis is X for vertical bars and Y for horizontal ones —
35
+ // value/category axis labels swap accordingly.
36
+ let xAxis = { ...axes.xAxis };
37
+ let yAxis = { ...axes.yAxis, tickFormat: { $numTickFormat: true } };
38
+ if (categories) {
39
+ const catAxis = categoryAxis(horizontal ? axes.yAxis : axes.xAxis, categories);
40
+ if (horizontal) {
41
+ yAxis = { ...catAxis, gridLine: false };
42
+ xAxis = { ...axes.xAxis, tickFormat: { $numTickFormat: true } };
43
+ }
44
+ else {
45
+ xAxis = { ...catAxis, gridLine: false };
46
+ }
47
+ }
48
+ const deco = decorationComponents(input);
49
+ return {
50
+ container: 'xy',
51
+ ...baseSpec(input),
52
+ components: [...deco.bands, {
53
+ type: input.type === 'stacked' ? 'StackedBar' : 'GroupedBar',
54
+ config: {
55
+ x,
56
+ y: yFields.map(f => field(f, 'number')),
57
+ orientation: input.orientation,
58
+ roundedCorners: input.roundedCorners,
59
+ ...(input.barPadding !== undefined ? { barPadding: input.barPadding } : {}),
60
+ },
61
+ }, ...deco.lines],
62
+ xAxis,
63
+ yAxis,
64
+ legend: seriesLegend(labels, input.legend, input.colors),
65
+ data,
66
+ };
67
+ },
68
+ };
69
+ //# sourceMappingURL=bar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bar.js","sourceRoot":"","sources":["../../src/recipes/bar.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import { z } from 'zod';
2
+ import type { Recipe } from './types.js';
3
+ export declare const boxplotInputShape: {
4
+ width: z.ZodDefault<z.ZodNumber>;
5
+ height: z.ZodDefault<z.ZodNumber>;
6
+ theme: z.ZodDefault<z.ZodEnum<["light", "dark"]>>;
7
+ title: z.ZodOptional<z.ZodString>;
8
+ colors: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
9
+ framework: z.ZodDefault<z.ZodEnum<["ts", "react", "svelte", "vue", "angular", "solid"]>>;
10
+ outputType: z.ZodDefault<z.ZodEnum<["svg", "png", "html", "interactive", "config", "code"]>>;
11
+ outputPath: z.ZodOptional<z.ZodString>;
12
+ scale: z.ZodDefault<z.ZodNumber>;
13
+ referenceLines: z.ZodOptional<z.ZodArray<z.ZodObject<{
14
+ axis: z.ZodDefault<z.ZodEnum<["x", "y"]>>;
15
+ value: z.ZodNumber;
16
+ label: z.ZodOptional<z.ZodString>;
17
+ color: z.ZodOptional<z.ZodString>;
18
+ lineWidth: z.ZodDefault<z.ZodNumber>;
19
+ style: z.ZodDefault<z.ZodEnum<["dashed", "solid", "dotted"]>>;
20
+ }, "strip", z.ZodTypeAny, {
21
+ value: number;
22
+ style: "solid" | "dashed" | "dotted";
23
+ axis: "x" | "y";
24
+ lineWidth: number;
25
+ label?: string | undefined;
26
+ color?: string | undefined;
27
+ }, {
28
+ value: number;
29
+ label?: string | undefined;
30
+ color?: string | undefined;
31
+ style?: "solid" | "dashed" | "dotted" | undefined;
32
+ axis?: "x" | "y" | undefined;
33
+ lineWidth?: number | undefined;
34
+ }>, "many">>;
35
+ referenceBands: z.ZodOptional<z.ZodArray<z.ZodObject<{
36
+ axis: z.ZodDefault<z.ZodEnum<["x", "y"]>>;
37
+ from: z.ZodNumber;
38
+ to: z.ZodNumber;
39
+ label: z.ZodOptional<z.ZodString>;
40
+ color: z.ZodOptional<z.ZodString>;
41
+ }, "strip", z.ZodTypeAny, {
42
+ axis: "x" | "y";
43
+ from: number;
44
+ to: number;
45
+ label?: string | undefined;
46
+ color?: string | undefined;
47
+ }, {
48
+ from: number;
49
+ to: number;
50
+ label?: string | undefined;
51
+ color?: string | undefined;
52
+ axis?: "x" | "y" | undefined;
53
+ }>, "many">>;
54
+ data: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>, "many">;
55
+ groupBy: z.ZodString;
56
+ value: z.ZodString;
57
+ boxPadding: z.ZodDefault<z.ZodNumber>;
58
+ boxMaxWidth: z.ZodOptional<z.ZodNumber>;
59
+ roundedCorners: z.ZodDefault<z.ZodNumber>;
60
+ xAxisLabel: z.ZodOptional<z.ZodString>;
61
+ yAxisLabel: z.ZodOptional<z.ZodString>;
62
+ showGridLines: z.ZodDefault<z.ZodBoolean>;
63
+ };
64
+ export declare const boxplotRecipe: Recipe<typeof boxplotInputShape>;
@@ -0,0 +1,89 @@
1
+ import { z } from 'zod';
2
+ import { commonInput, xyInput, dataRecords, fieldName, assertFieldsExist, baseSpec, categoryAxis, distinctValues, xyAxes, ChartInputError, xyDecorations, decorationComponents, } from './shared.js';
3
+ /** Linear-interpolated quantile of a sorted sample (R-7, matches d3.quantileSorted) */
4
+ const quantileSorted = (sorted, p) => {
5
+ const h = (sorted.length - 1) * p;
6
+ const lo = Math.floor(h);
7
+ const hi = Math.ceil(h);
8
+ return sorted[lo] + (sorted[hi] - sorted[lo]) * (h - lo);
9
+ };
10
+ /** Trim float noise from computed stats (purely cosmetic for the config output) */
11
+ const round6 = (value) => Math.round(value * 1e6) / 1e6;
12
+ export const boxplotInputShape = {
13
+ data: dataRecords,
14
+ groupBy: fieldName.describe('Field with the group/category of each observation (one box per distinct value)'),
15
+ value: fieldName.describe('Field with the numeric value of each observation'),
16
+ boxPadding: z.number().min(0).max(0.9).default(0.25).describe('Fractional padding between boxes, 0..0.9'),
17
+ boxMaxWidth: z.number().min(4).max(400).optional().describe('Maximum box width in pixels'),
18
+ roundedCorners: z.number().min(0).max(20).default(2).describe('Corner radius of the boxes in pixels'),
19
+ xAxisLabel: xyInput.xAxisLabel,
20
+ yAxisLabel: xyInput.yAxisLabel,
21
+ showGridLines: xyInput.showGridLines,
22
+ ...xyDecorations,
23
+ ...commonInput,
24
+ };
25
+ export const boxplotRecipe = {
26
+ name: 'generate_boxplot',
27
+ title: 'Box plot',
28
+ description: 'Generate a box-and-whisker plot comparing the distribution of a numeric value across groups. ' +
29
+ 'Pass raw (long-format) observations; quartiles, median, and Tukey whiskers (1.5×IQR, clamped to the data extent) are computed per group. ' +
30
+ 'Example: data=[{"service":"Auth","latency":132},{"service":"Auth","latency":89},{"service":"Search","latency":210},...], ' +
31
+ 'groupBy="service", value="latency".',
32
+ inputShape: boxplotInputShape,
33
+ toSpec: (input) => {
34
+ const data = input.data;
35
+ assertFieldsExist(data, [input.groupBy, input.value]);
36
+ const groups = distinctValues(data, input.groupBy).map(String);
37
+ if (!groups.length)
38
+ throw new ChartInputError(`Field "${input.groupBy}" has no values to group by`);
39
+ // One record per group with precomputed stats. Quartiles/whiskers are
40
+ // stored as [low, high] arrays in the data (the Boxplot accessors must
41
+ // return tuples, which accessor descriptors can't synthesize).
42
+ const boxes = groups.map(group => {
43
+ const values = data
44
+ .filter(d => d[input.groupBy] !== null && d[input.groupBy] !== undefined && String(d[input.groupBy]) === group)
45
+ .map(d => Number(d[input.value]))
46
+ .filter(v => Number.isFinite(v))
47
+ .sort((a, b) => a - b);
48
+ if (!values.length) {
49
+ throw new ChartInputError(`Group "${group}" has no numeric "${input.value}" values`);
50
+ }
51
+ const q1 = quantileSorted(values, 0.25);
52
+ const median = quantileSorted(values, 0.5);
53
+ const q3 = quantileSorted(values, 0.75);
54
+ const iqr = q3 - q1;
55
+ const whiskerLow = Math.max(values[0], q1 - 1.5 * iqr);
56
+ const whiskerHigh = Math.min(values[values.length - 1], q3 + 1.5 * iqr);
57
+ return {
58
+ group,
59
+ median: round6(median),
60
+ quartiles: [round6(q1), round6(q3)],
61
+ whiskers: [round6(whiskerLow), round6(whiskerHigh)],
62
+ };
63
+ });
64
+ const axes = xyAxes(input);
65
+ const deco = decorationComponents(input);
66
+ return {
67
+ container: 'xy',
68
+ ...baseSpec(input),
69
+ components: [...deco.bands, {
70
+ type: 'Boxplot',
71
+ config: {
72
+ x: { $index: true },
73
+ median: { $field: 'median' },
74
+ quartiles: { $field: 'quartiles' },
75
+ whiskers: { $field: 'whiskers' },
76
+ barPadding: input.boxPadding,
77
+ ...(input.boxMaxWidth !== undefined ? { barMaxWidth: input.boxMaxWidth } : {}),
78
+ roundedCorners: input.roundedCorners,
79
+ },
80
+ }, ...deco.lines],
81
+ // Explicit integer tick values: with few categories, approximate d3
82
+ // tick counts produce fractional ticks that round to duplicate labels
83
+ xAxis: { ...categoryAxis(axes.xAxis, groups), gridLine: false, tickValues: groups.map((_, i) => i) },
84
+ yAxis: { ...axes.yAxis, tickFormat: { $numTickFormat: true } },
85
+ data: boxes,
86
+ };
87
+ },
88
+ };
89
+ //# sourceMappingURL=boxplot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boxplot.js","sourceRoot":"","sources":["../../src/recipes/boxplot.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import { z } from 'zod';
2
+ import type { Recipe } from './types.js';
3
+ export declare const chordInputShape: {
4
+ width: z.ZodDefault<z.ZodNumber>;
5
+ height: z.ZodDefault<z.ZodNumber>;
6
+ theme: z.ZodDefault<z.ZodEnum<["light", "dark"]>>;
7
+ title: z.ZodOptional<z.ZodString>;
8
+ colors: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
9
+ framework: z.ZodDefault<z.ZodEnum<["ts", "react", "svelte", "vue", "angular", "solid"]>>;
10
+ outputType: z.ZodDefault<z.ZodEnum<["svg", "png", "html", "interactive", "config", "code"]>>;
11
+ outputPath: z.ZodOptional<z.ZodString>;
12
+ scale: z.ZodDefault<z.ZodNumber>;
13
+ links: z.ZodArray<z.ZodObject<{
14
+ source: z.ZodString;
15
+ target: z.ZodString;
16
+ value: z.ZodNumber;
17
+ }, "strip", z.ZodTypeAny, {
18
+ value: number;
19
+ source: string;
20
+ target: string;
21
+ }, {
22
+ value: number;
23
+ source: string;
24
+ target: string;
25
+ }>, "many">;
26
+ nodes: z.ZodOptional<z.ZodArray<z.ZodObject<{
27
+ id: z.ZodString;
28
+ label: z.ZodOptional<z.ZodString>;
29
+ }, "strip", z.ZodTypeAny, {
30
+ id: string;
31
+ label?: string | undefined;
32
+ }, {
33
+ id: string;
34
+ label?: string | undefined;
35
+ }>, "many">>;
36
+ nodeWidth: z.ZodDefault<z.ZodNumber>;
37
+ padAngle: z.ZodDefault<z.ZodNumber>;
38
+ cornerRadius: z.ZodDefault<z.ZodNumber>;
39
+ labelAlignment: z.ZodDefault<z.ZodEnum<["along", "perpendicular"]>>;
40
+ };
41
+ export declare const chordRecipe: Recipe<typeof chordInputShape>;
@@ -0,0 +1,72 @@
1
+ import { z } from 'zod';
2
+ import { commonInput, baseSpec, ChartInputError } from './shared.js';
3
+ const chordNode = z.object({
4
+ id: z.string().min(1),
5
+ label: z.string().optional().describe('Display name (defaults to id)'),
6
+ }).describe('A node; optional if all nodes can be derived from links');
7
+ const chordLink = z.object({
8
+ source: z.string().min(1).describe('Source node id'),
9
+ target: z.string().min(1).describe('Target node id'),
10
+ value: z.number().positive().describe('Connection strength / flow volume'),
11
+ });
12
+ /** Size of the default Unovis palette (--vis-color0..5) */
13
+ const PALETTE_SIZE = 6;
14
+ export const chordInputShape = {
15
+ links: z.array(chordLink).min(1)
16
+ .describe('Weighted connections between nodes, e.g. [{"source":"A","target":"B","value":10}]'),
17
+ nodes: z.array(chordNode).optional().describe('Node definitions; derived from links when omitted'),
18
+ nodeWidth: z.number().min(2).max(100).default(15).describe('Thickness of the node arcs in pixels'),
19
+ padAngle: z.number().min(0).max(0.5).default(0.02).describe('Angular padding between nodes, in radians'),
20
+ cornerRadius: z.number().min(0).max(20).default(2).describe('Corner radius of the node arcs in pixels'),
21
+ labelAlignment: z.enum(['along', 'perpendicular']).default('along')
22
+ .describe('Node labels drawn along the arcs, or radiating outwards perpendicular to them'),
23
+ ...commonInput,
24
+ };
25
+ export const chordRecipe = {
26
+ name: 'generate_chord_diagram',
27
+ title: 'Chord diagram',
28
+ description: 'Generate a chord diagram visualizing weighted relationships within one set of entities ' +
29
+ '(e.g. trade between countries, brand switching, team interactions). Provide links as {source, target, value}; ' +
30
+ 'nodes are derived automatically. Self-links (source === target) are not supported. ' +
31
+ 'Example: links=[{"source":"Apple","target":"Samsung","value":8},{"source":"Samsung","target":"Apple","value":14}].',
32
+ inputShape: chordInputShape,
33
+ toSpec: (input) => {
34
+ const declared = new Map((input.nodes ?? []).map(n => [n.id, n]));
35
+ const ids = new Set();
36
+ for (const link of input.links) {
37
+ ids.add(link.source);
38
+ ids.add(link.target);
39
+ // The graph data model silently drops self-referencing links — reject
40
+ // them instead so the output never misrepresents the input
41
+ if (link.source === link.target)
42
+ throw new ChartInputError(`Self-referencing link: "${link.source}" → "${link.target}". Chord diagrams only show links between distinct nodes`);
43
+ }
44
+ for (const id of declared.keys())
45
+ ids.add(id);
46
+ // Cycle the palette per node — the component's default node/label color
47
+ // accessors read the `color` property of each node
48
+ const paletteSize = input.colors?.length || PALETTE_SIZE;
49
+ const nodes = Array.from(ids).map((id, i) => ({
50
+ id,
51
+ label: declared.get(id)?.label ?? id,
52
+ color: `var(--vis-color${i % paletteSize})`,
53
+ }));
54
+ return {
55
+ container: 'single',
56
+ ...baseSpec(input),
57
+ components: [{
58
+ type: 'ChordDiagram',
59
+ config: {
60
+ nodeLabel: { $field: 'label' },
61
+ linkValue: { $field: 'value' },
62
+ nodeWidth: input.nodeWidth,
63
+ padAngle: input.padAngle,
64
+ cornerRadius: input.cornerRadius,
65
+ nodeLabelAlignment: input.labelAlignment,
66
+ },
67
+ }],
68
+ data: { nodes, links: input.links },
69
+ };
70
+ },
71
+ };
72
+ //# sourceMappingURL=chord.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chord.js","sourceRoot":"","sources":["../../src/recipes/chord.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ /** Area id lookup tables for the choropleth recipe.
2
+ *
3
+ * The TopoJSONMap joins area data to map features by comparing the feature's
4
+ * `id` with the datum's `id` (string equality), and each map uses a different
5
+ * id scheme:
6
+ *
7
+ * - world: ISO 3166-1 alpha-2 codes ('US', 'DE', 'BR')
8
+ * - usa: numeric state FIPS codes WITHOUT zero-padding ('6' = California)
9
+ * - germany / france / india: ISO 3166-2 codes ('DE-BY', 'FR-11', 'IN-MH')
10
+ * - uk: GSS statistical region codes ('E15000007' = London)
11
+ * - china: 'CN-XX' codes that do NOT follow ISO 3166-2 (e.g. the map's
12
+ * 'CN-HA' is Hainan while ISO CN-HA is Henan) — join by name
13
+ *
14
+ * Since several of these are unguessable for tool callers, `resolveArea`
15
+ * accepts codes in common formats (USPS state abbreviations, zero-padded
16
+ * FIPS, bare region codes without the country prefix) as well as area names.
17
+ *
18
+ * Rows are generated from the @unovis/ts map files (feature id +
19
+ * `properties.name`) with hand-curated aliases appended:
20
+ * `[featureId, displayName, ...aliases]`.
21
+ */
22
+ export type RegionRow = [string, string, ...string[]];
23
+ export type ChoroplethMapKey = 'world' | 'usa' | 'germany' | 'uk' | 'france' | 'india' | 'china';
24
+ export interface ChoroplethMapDef {
25
+ /** `{ $unovisMap: marker }` name resolved by the renderer */
26
+ marker: string;
27
+ /** Human description of the accepted id formats, used in error messages */
28
+ idFormat: string;
29
+ /** ISO 3166-2 country prefix tried when a bare region code is given */
30
+ codePrefix?: string;
31
+ /** Feature ids are unpadded numeric strings — accept zero-padded input */
32
+ numericIds?: boolean;
33
+ /** MapProjection factory name; maps whose features cross the antimeridian
34
+ * (USA: the Aleutians) need a composite projection to fit correctly */
35
+ projection?: string;
36
+ regions: RegionRow[];
37
+ }
38
+ /** Lowercase and strip diacritics/punctuation for tolerant name matching */
39
+ export declare const normalizeKey: (value: string) => string;
40
+ export declare const choroplethMaps: Record<ChoroplethMapKey, ChoroplethMapDef>;
41
+ export interface ResolvedArea {
42
+ /** The map feature id the TopoJSONMap joins on */
43
+ id: string;
44
+ /** Canonical display name of the area */
45
+ name: string;
46
+ }
47
+ /** Resolve a user-supplied area id (code in common formats, or name) to the
48
+ * map's feature id. Returns undefined when nothing matches. */
49
+ export declare function resolveArea(map: ChoroplethMapKey, rawId: string): ResolvedArea | undefined;