@data360/mcp-viz-core 0.0.1
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/README.md +20 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/prepare-spec.d.ts +31 -0
- package/dist/prepare-spec.d.ts.map +1 -0
- package/dist/prepare-spec.js +105 -0
- package/dist/types.d.ts +91 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/wb-theme.d.ts +84 -0
- package/dist/wb-theme.d.ts.map +1 -0
- package/dist/wb-theme.js +64 -0
- package/package.json +37 -0
package/README.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# @data360/mcp-viz-core
|
|
2
|
+
|
|
3
|
+
Framework-agnostic TypeScript utilities for Data360 MCP **Vega-Lite** tool output: the same `prepareSpec` / `parseSpec` pipeline and World Bank theme used by [`@data360/mcp-ui`](../mcp-ui) (React) and [`@data360/mcp-ui-angular`](../mcp-ui-angular) (Angular).
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @data360/mcp-viz-core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## API
|
|
12
|
+
|
|
13
|
+
- **`prepareSpec(spec, chartHeight?)`** — eight-guard pipeline (inline datasets, responsive sizing, WB theme merge, axis rules, etc.). See [`packages/mcp-ui` README](../mcp-ui/README.md) for the full list.
|
|
14
|
+
- **`parseSpec(spec, palette)`** — legend metadata from the **raw** spec (before `prepareSpec`).
|
|
15
|
+
- **`getMark(spec)`**, **`WB_THEME`**, **`WB_PALETTE`**, **`WB_THEME_URL`**
|
|
16
|
+
- Types: **`VLSpec`**, **`Annotation`**, **`VegaChartCardBaseProps`**, etc.
|
|
17
|
+
|
|
18
|
+
## Version coupling
|
|
19
|
+
|
|
20
|
+
When the MCP server or [`@data360/tool-types`](../tool-types) changes viz JSON shape or Vega-Lite conventions, bump **`@data360/mcp-viz-core`** together with **`@data360/mcp-ui`** and **`@data360/mcp-ui-angular`** so all consumers stay aligned.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type { Annotation, MarkType, VegaChartCardBaseProps, VLEncoding, VLSpec, } from "./types";
|
|
2
|
+
export { WB_PALETTE, WB_THEME, WB_THEME_URL, type WBTheme, } from "./wb-theme";
|
|
3
|
+
export { getMark, parseSpec, prepareSpec, type ParsedSpec, } from "./prepare-spec";
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,UAAU,EACV,QAAQ,EACR,sBAAsB,EACtB,UAAU,EACV,MAAM,GACP,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,UAAU,EACV,QAAQ,EACR,YAAY,EACZ,KAAK,OAAO,GACb,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,OAAO,EACP,SAAS,EACT,WAAW,EACX,KAAK,UAAU,GAChB,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { MarkType, VLSpec } from "./types";
|
|
2
|
+
export declare function getMark(spec: VLSpec): MarkType;
|
|
3
|
+
/**
|
|
4
|
+
* Applies the 8-guard pipeline to make any Data360 Vega-Lite spec compatible
|
|
5
|
+
* with the VegaChartCard component and the WB visual theme.
|
|
6
|
+
*
|
|
7
|
+
* Guards:
|
|
8
|
+
* 1. Inline named dataset → data.values
|
|
9
|
+
* 2. Responsive sizing (width: "container", configurable height)
|
|
10
|
+
* 3. Suppress built-in legend (card renders its own)
|
|
11
|
+
* 3b. Remove top-level title (card header shows it; avoids duplicating Vega’s title)
|
|
12
|
+
* 4. Strip zoom/pan params (conflicts with card controls)
|
|
13
|
+
* 5. Normalize $schema v6 → v5 (vega-embed 6 compatibility)
|
|
14
|
+
* 6. Merge WB theme into config
|
|
15
|
+
* 7. scale.zero — false for line/area/point/tick, true for bar
|
|
16
|
+
* 8. x-axis format — %Y only for temporal; dropped for ordinal/nominal
|
|
17
|
+
*/
|
|
18
|
+
export declare function prepareSpec(spec: VLSpec, chartHeight?: number): VLSpec;
|
|
19
|
+
export interface ParsedSpec {
|
|
20
|
+
rows: Record<string, unknown>[];
|
|
21
|
+
colorField: string | null;
|
|
22
|
+
specTitle: string | null;
|
|
23
|
+
distinctGroups: string[];
|
|
24
|
+
colorMap: Record<string, string>;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Extract legend metadata from a raw (un-prepared) spec.
|
|
28
|
+
* Call this once on the original spec, not the prepared one.
|
|
29
|
+
*/
|
|
30
|
+
export declare function parseSpec(spec: VLSpec, palette: string[]): ParsedSpec;
|
|
31
|
+
//# sourceMappingURL=prepare-spec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prepare-spec.d.ts","sourceRoot":"","sources":["../src/prepare-spec.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAIhD,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAG9C;AAwBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,SAAM,GAAG,MAAM,CAkDnE;AAID,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAChC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,UAAU,CAuBrE"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { WB_THEME } from "./wb-theme";
|
|
2
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
3
|
+
export function getMark(spec) {
|
|
4
|
+
if (!spec.mark)
|
|
5
|
+
return "line";
|
|
6
|
+
return typeof spec.mark === "string" ? spec.mark : (spec.mark.type ?? "line");
|
|
7
|
+
}
|
|
8
|
+
/** Resolve spec.datasets[spec.data.name] → spec.data.values */
|
|
9
|
+
function inlineDataset(spec) {
|
|
10
|
+
const name = spec.data?.name;
|
|
11
|
+
if (name && spec.datasets?.[name]) {
|
|
12
|
+
return { ...spec, data: { values: spec.datasets[name] }, datasets: undefined };
|
|
13
|
+
}
|
|
14
|
+
return spec;
|
|
15
|
+
}
|
|
16
|
+
/** Deep-merge WB theme into spec.config. Spec config values win on conflict. */
|
|
17
|
+
function mergeConfig(specConfig, theme) {
|
|
18
|
+
const base = JSON.parse(JSON.stringify(theme));
|
|
19
|
+
if (!specConfig)
|
|
20
|
+
return base;
|
|
21
|
+
// Shallow-merge top-level keys — spec overrides theme
|
|
22
|
+
return { ...base, ...specConfig };
|
|
23
|
+
}
|
|
24
|
+
// ─── prepareSpec ─────────────────────────────────────────────────────────────
|
|
25
|
+
/**
|
|
26
|
+
* Applies the 8-guard pipeline to make any Data360 Vega-Lite spec compatible
|
|
27
|
+
* with the VegaChartCard component and the WB visual theme.
|
|
28
|
+
*
|
|
29
|
+
* Guards:
|
|
30
|
+
* 1. Inline named dataset → data.values
|
|
31
|
+
* 2. Responsive sizing (width: "container", configurable height)
|
|
32
|
+
* 3. Suppress built-in legend (card renders its own)
|
|
33
|
+
* 3b. Remove top-level title (card header shows it; avoids duplicating Vega’s title)
|
|
34
|
+
* 4. Strip zoom/pan params (conflicts with card controls)
|
|
35
|
+
* 5. Normalize $schema v6 → v5 (vega-embed 6 compatibility)
|
|
36
|
+
* 6. Merge WB theme into config
|
|
37
|
+
* 7. scale.zero — false for line/area/point/tick, true for bar
|
|
38
|
+
* 8. x-axis format — %Y only for temporal; dropped for ordinal/nominal
|
|
39
|
+
*/
|
|
40
|
+
export function prepareSpec(spec, chartHeight = 260) {
|
|
41
|
+
let out = JSON.parse(JSON.stringify(spec));
|
|
42
|
+
const markType = getMark(out);
|
|
43
|
+
// 1. Inline named dataset
|
|
44
|
+
out = inlineDataset(out);
|
|
45
|
+
// 2. Responsive sizing
|
|
46
|
+
out.width = "container";
|
|
47
|
+
out.height = chartHeight;
|
|
48
|
+
// 3. Suppress built-in legend — card renders its own
|
|
49
|
+
if (out.encoding?.color) {
|
|
50
|
+
out.encoding.color.legend = null;
|
|
51
|
+
}
|
|
52
|
+
// 3b. Title is displayed by VegaChartCard (prop / parseSpec); strip from the embedded spec
|
|
53
|
+
delete out.title;
|
|
54
|
+
// 4. Strip zoom/pan params — conflicts with card controls
|
|
55
|
+
delete out.params;
|
|
56
|
+
// 5. Normalize $schema v6 → v5
|
|
57
|
+
out.$schema = "https://vega.github.io/schema/vega-lite/v5.json";
|
|
58
|
+
// 6. Merge WB theme
|
|
59
|
+
out.config = mergeConfig(out.config, WB_THEME);
|
|
60
|
+
// 7. scale.zero — bars must start at zero; everything else benefits from false
|
|
61
|
+
if (out.encoding?.y) {
|
|
62
|
+
if (!out.encoding.y.scale)
|
|
63
|
+
out.encoding.y.scale = {};
|
|
64
|
+
out.encoding.y.scale.zero = markType === "bar";
|
|
65
|
+
}
|
|
66
|
+
// 8. x-axis format — only apply %Y for temporal x
|
|
67
|
+
if (out.encoding?.x) {
|
|
68
|
+
const xType = out.encoding.x.type;
|
|
69
|
+
if (!out.encoding.x.axis)
|
|
70
|
+
out.encoding.x.axis = {};
|
|
71
|
+
const axis = out.encoding.x.axis;
|
|
72
|
+
if (xType === "temporal") {
|
|
73
|
+
axis.format = "%Y";
|
|
74
|
+
axis.title = null;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
// ordinal / nominal (bar with year strings, tick with country on x)
|
|
78
|
+
delete axis.format;
|
|
79
|
+
axis.title = null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Extract legend metadata from a raw (un-prepared) spec.
|
|
86
|
+
* Call this once on the original spec, not the prepared one.
|
|
87
|
+
*/
|
|
88
|
+
export function parseSpec(spec, palette) {
|
|
89
|
+
const name = spec.data?.name;
|
|
90
|
+
const rows = name && spec.datasets?.[name]
|
|
91
|
+
? spec.datasets[name]
|
|
92
|
+
: (spec.data?.values ?? []);
|
|
93
|
+
const colorField = spec.encoding?.color?.field ?? null;
|
|
94
|
+
const specTitle = typeof spec.title === "string"
|
|
95
|
+
? spec.title
|
|
96
|
+
: spec.title?.text ?? null;
|
|
97
|
+
const distinctGroups = colorField
|
|
98
|
+
? [...new Set(rows.map((r) => String(r[colorField])))]
|
|
99
|
+
: [];
|
|
100
|
+
const colorMap = {};
|
|
101
|
+
distinctGroups.forEach((g, i) => {
|
|
102
|
+
colorMap[g] = palette[i % palette.length];
|
|
103
|
+
});
|
|
104
|
+
return { rows, colorField, specTitle, distinctGroups, colorMap };
|
|
105
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
export type MarkType = "line" | "bar" | "point" | "area" | "tick";
|
|
2
|
+
export interface VLEncoding {
|
|
3
|
+
field?: string;
|
|
4
|
+
type?: "temporal" | "quantitative" | "nominal" | "ordinal";
|
|
5
|
+
axis?: Record<string, unknown>;
|
|
6
|
+
scale?: Record<string, unknown>;
|
|
7
|
+
legend?: Record<string, unknown> | null;
|
|
8
|
+
timeUnit?: string;
|
|
9
|
+
[key: string]: unknown;
|
|
10
|
+
}
|
|
11
|
+
export interface VLSpec {
|
|
12
|
+
$schema?: string;
|
|
13
|
+
title?: string | {
|
|
14
|
+
text: string;
|
|
15
|
+
[key: string]: unknown;
|
|
16
|
+
};
|
|
17
|
+
data?: {
|
|
18
|
+
name?: string;
|
|
19
|
+
values?: Record<string, unknown>[];
|
|
20
|
+
};
|
|
21
|
+
datasets?: Record<string, Record<string, unknown>[]>;
|
|
22
|
+
mark?: MarkType | {
|
|
23
|
+
type: MarkType;
|
|
24
|
+
[key: string]: unknown;
|
|
25
|
+
};
|
|
26
|
+
encoding?: {
|
|
27
|
+
x?: VLEncoding;
|
|
28
|
+
y?: VLEncoding;
|
|
29
|
+
color?: VLEncoding;
|
|
30
|
+
tooltip?: VLEncoding[];
|
|
31
|
+
[key: string]: unknown;
|
|
32
|
+
};
|
|
33
|
+
params?: unknown[];
|
|
34
|
+
config?: Record<string, unknown>;
|
|
35
|
+
width?: number | string;
|
|
36
|
+
height?: number | string;
|
|
37
|
+
[key: string]: unknown;
|
|
38
|
+
}
|
|
39
|
+
export interface Annotation {
|
|
40
|
+
/** The circled number shown next to the callout. */
|
|
41
|
+
id: number;
|
|
42
|
+
/** The italic callout text. */
|
|
43
|
+
text: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Props shared by React and Angular chart card implementations.
|
|
47
|
+
* React adds `railTopSlot`; Angular may use a projected content slot instead.
|
|
48
|
+
*/
|
|
49
|
+
export interface VegaChartCardBaseProps {
|
|
50
|
+
/**
|
|
51
|
+
* A valid Vega-Lite spec. Passed through the prepareSpec() pipeline before
|
|
52
|
+
* rendering — named datasets are inlined, WB theme applied, and mark-aware
|
|
53
|
+
* axis/scale guards run automatically.
|
|
54
|
+
*/
|
|
55
|
+
spec: VLSpec;
|
|
56
|
+
/**
|
|
57
|
+
* Card title. If omitted, falls back to spec.title.
|
|
58
|
+
*/
|
|
59
|
+
title?: string;
|
|
60
|
+
/**
|
|
61
|
+
* Secondary line shown below the title (e.g. "Brazil, India · 2018–2022").
|
|
62
|
+
*/
|
|
63
|
+
subtitle?: string;
|
|
64
|
+
/**
|
|
65
|
+
* Attribution line shown below the chart area.
|
|
66
|
+
*/
|
|
67
|
+
source?: string;
|
|
68
|
+
/**
|
|
69
|
+
* Numbered callout annotations shown below the chart.
|
|
70
|
+
* Hidden when the user toggles "Show annotations" off.
|
|
71
|
+
*/
|
|
72
|
+
annotations?: Annotation[];
|
|
73
|
+
/**
|
|
74
|
+
* Card height in pixels. Defaults to 260.
|
|
75
|
+
*/
|
|
76
|
+
chartHeight?: number;
|
|
77
|
+
/**
|
|
78
|
+
* Called when "Download data" is clicked.
|
|
79
|
+
* Receives the raw filtered rows currently visible in the chart.
|
|
80
|
+
* Defaults to a CSV download if omitted.
|
|
81
|
+
*/
|
|
82
|
+
onDownload?: (rows: Record<string, unknown>[]) => void;
|
|
83
|
+
/**
|
|
84
|
+
* Called when the PNG export button is clicked.
|
|
85
|
+
* Receives the PNG data URL. Defaults to a file download if omitted.
|
|
86
|
+
*/
|
|
87
|
+
onExport?: (dataUrl: string) => void;
|
|
88
|
+
/** Extra class name applied to the outer card div. */
|
|
89
|
+
className?: string;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAElE,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,UAAU,GAAG,cAAc,GAAG,SAAS,GAAG,SAAS,CAAC;IAC3D,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,MAAM;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IAC1D,IAAI,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;KAAE,CAAC;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;IACrD,IAAI,CAAC,EAAE,QAAQ,GAAG;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IAC7D,QAAQ,CAAC,EAAE;QACT,CAAC,CAAC,EAAE,UAAU,CAAC;QACf,CAAC,CAAC,EAAE,UAAU,CAAC;QACf,KAAK,CAAC,EAAE,UAAU,CAAC;QACnB,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;QACvB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;IACF,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAID,MAAM,WAAW,UAAU;IACzB,oDAAoD;IACpD,EAAE,EAAE,MAAM,CAAC;IACX,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAE3B;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,IAAI,CAAC;IAEvD;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAErC,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
export declare const WB_THEME_URL = "https://worldbank.github.io/data-visualization-style-guide/vega/wb-vega-theme.json";
|
|
2
|
+
export declare const WB_THEME: {
|
|
3
|
+
readonly background: "#ffffff";
|
|
4
|
+
readonly view: {
|
|
5
|
+
readonly stroke: null;
|
|
6
|
+
};
|
|
7
|
+
readonly arc: {
|
|
8
|
+
readonly fill: "#34A7F2";
|
|
9
|
+
};
|
|
10
|
+
readonly area: {
|
|
11
|
+
readonly fill: "#34A7F2";
|
|
12
|
+
};
|
|
13
|
+
readonly line: {
|
|
14
|
+
readonly stroke: "#34A7F2";
|
|
15
|
+
readonly strokeCap: "round";
|
|
16
|
+
readonly strokeJoin: "round";
|
|
17
|
+
};
|
|
18
|
+
readonly rect: {
|
|
19
|
+
readonly fill: "#34A7F2";
|
|
20
|
+
};
|
|
21
|
+
readonly point: {
|
|
22
|
+
readonly filled: true;
|
|
23
|
+
readonly stroke: "white";
|
|
24
|
+
readonly strokeWidth: 1;
|
|
25
|
+
};
|
|
26
|
+
readonly title: {
|
|
27
|
+
readonly font: "Open Sans, Arial, sans-serif";
|
|
28
|
+
readonly subtitleFont: "Open Sans, Arial, sans-serif";
|
|
29
|
+
readonly anchor: "start";
|
|
30
|
+
readonly fontSize: 18;
|
|
31
|
+
readonly fontWeight: 600;
|
|
32
|
+
readonly offset: 20;
|
|
33
|
+
readonly subtitleFontSize: 15;
|
|
34
|
+
readonly subtitleColor: "#666666";
|
|
35
|
+
readonly subtitlePadding: 6;
|
|
36
|
+
};
|
|
37
|
+
readonly axis: {
|
|
38
|
+
readonly titleFont: "Open Sans, Arial, sans-serif";
|
|
39
|
+
readonly titleFontSize: 13;
|
|
40
|
+
readonly titleFontWeight: 600;
|
|
41
|
+
readonly labelFont: "Open Sans, Arial, sans-serif";
|
|
42
|
+
readonly labelColor: "#666666";
|
|
43
|
+
readonly labelFontSize: 13;
|
|
44
|
+
readonly gridWidth: 1;
|
|
45
|
+
readonly tickColor: "#CED4DE";
|
|
46
|
+
readonly tickWidth: 0.2;
|
|
47
|
+
readonly titleColor: "#111111";
|
|
48
|
+
readonly gridDash: readonly [4, 2];
|
|
49
|
+
readonly gridColor: "#CED4DE";
|
|
50
|
+
readonly labelPadding: 6;
|
|
51
|
+
readonly labelOverlap: true;
|
|
52
|
+
readonly labelFlush: false;
|
|
53
|
+
};
|
|
54
|
+
readonly axisBand: {
|
|
55
|
+
readonly grid: false;
|
|
56
|
+
};
|
|
57
|
+
readonly axisX: {
|
|
58
|
+
readonly grid: true;
|
|
59
|
+
readonly tickSize: 0;
|
|
60
|
+
readonly domain: false;
|
|
61
|
+
};
|
|
62
|
+
readonly axisY: {
|
|
63
|
+
readonly domain: false;
|
|
64
|
+
readonly grid: true;
|
|
65
|
+
readonly tickSize: 0;
|
|
66
|
+
};
|
|
67
|
+
readonly legend: {
|
|
68
|
+
readonly labelFont: "Open Sans, Arial, sans-serif";
|
|
69
|
+
readonly titleFont: "Open Sans, Arial, sans-serif";
|
|
70
|
+
readonly titleFontSize: 15;
|
|
71
|
+
readonly labelFontSize: 13;
|
|
72
|
+
readonly labelColor: "#111111";
|
|
73
|
+
readonly padding: 1;
|
|
74
|
+
readonly symbolSize: 140;
|
|
75
|
+
readonly orient: "bottom";
|
|
76
|
+
readonly direction: "horizontal";
|
|
77
|
+
};
|
|
78
|
+
readonly range: {
|
|
79
|
+
readonly category: readonly ["#34A7F2", "#FF9800", "#664AB6", "#4EC2C0", "#F3578E", "#081079", "#0C7C68"];
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
export type WBTheme = typeof WB_THEME;
|
|
83
|
+
export declare const WB_PALETTE: string[];
|
|
84
|
+
//# sourceMappingURL=wb-theme.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wb-theme.d.ts","sourceRoot":"","sources":["../src/wb-theme.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,uFAC6D,CAAC;AAEvF,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6DX,CAAC;AAEX,MAAM,MAAM,OAAO,GAAG,OAAO,QAAQ,CAAC;AACtC,eAAO,MAAM,UAAU,EAAyC,MAAM,EAAE,CAAC"}
|
package/dist/wb-theme.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export const WB_THEME_URL = "https://worldbank.github.io/data-visualization-style-guide/vega/wb-vega-theme.json";
|
|
2
|
+
export const WB_THEME = {
|
|
3
|
+
background: "#ffffff",
|
|
4
|
+
view: { stroke: null },
|
|
5
|
+
arc: { fill: "#34A7F2" },
|
|
6
|
+
area: { fill: "#34A7F2" },
|
|
7
|
+
line: { stroke: "#34A7F2", strokeCap: "round", strokeJoin: "round" },
|
|
8
|
+
rect: { fill: "#34A7F2" },
|
|
9
|
+
point: { filled: true, stroke: "white", strokeWidth: 1 },
|
|
10
|
+
title: {
|
|
11
|
+
font: "Open Sans, Arial, sans-serif",
|
|
12
|
+
subtitleFont: "Open Sans, Arial, sans-serif",
|
|
13
|
+
anchor: "start",
|
|
14
|
+
fontSize: 18,
|
|
15
|
+
fontWeight: 600,
|
|
16
|
+
offset: 20,
|
|
17
|
+
subtitleFontSize: 15,
|
|
18
|
+
subtitleColor: "#666666",
|
|
19
|
+
subtitlePadding: 6,
|
|
20
|
+
},
|
|
21
|
+
axis: {
|
|
22
|
+
titleFont: "Open Sans, Arial, sans-serif",
|
|
23
|
+
titleFontSize: 13,
|
|
24
|
+
titleFontWeight: 600,
|
|
25
|
+
labelFont: "Open Sans, Arial, sans-serif",
|
|
26
|
+
labelColor: "#666666",
|
|
27
|
+
labelFontSize: 13,
|
|
28
|
+
gridWidth: 1,
|
|
29
|
+
tickColor: "#CED4DE",
|
|
30
|
+
tickWidth: 0.2,
|
|
31
|
+
titleColor: "#111111",
|
|
32
|
+
gridDash: [4, 2],
|
|
33
|
+
gridColor: "#CED4DE",
|
|
34
|
+
labelPadding: 6,
|
|
35
|
+
labelOverlap: true,
|
|
36
|
+
labelFlush: false,
|
|
37
|
+
},
|
|
38
|
+
axisBand: { grid: false },
|
|
39
|
+
axisX: { grid: true, tickSize: 0, domain: false },
|
|
40
|
+
axisY: { domain: false, grid: true, tickSize: 0 },
|
|
41
|
+
legend: {
|
|
42
|
+
labelFont: "Open Sans, Arial, sans-serif",
|
|
43
|
+
titleFont: "Open Sans, Arial, sans-serif",
|
|
44
|
+
titleFontSize: 15,
|
|
45
|
+
labelFontSize: 13,
|
|
46
|
+
labelColor: "#111111",
|
|
47
|
+
padding: 1,
|
|
48
|
+
symbolSize: 140,
|
|
49
|
+
orient: "bottom",
|
|
50
|
+
direction: "horizontal",
|
|
51
|
+
},
|
|
52
|
+
range: {
|
|
53
|
+
category: [
|
|
54
|
+
"#34A7F2",
|
|
55
|
+
"#FF9800",
|
|
56
|
+
"#664AB6",
|
|
57
|
+
"#4EC2C0",
|
|
58
|
+
"#F3578E",
|
|
59
|
+
"#081079",
|
|
60
|
+
"#0C7C68",
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
export const WB_PALETTE = WB_THEME.range.category;
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@data360/mcp-viz-core",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Framework-agnostic Vega-Lite prep and World Bank theme for Data360 MCP viz tools.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"typescript": "^5.5.0"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"data360",
|
|
25
|
+
"mcp",
|
|
26
|
+
"vega-lite",
|
|
27
|
+
"world-bank"
|
|
28
|
+
],
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/worldbank/data360-mcp"
|
|
36
|
+
}
|
|
37
|
+
}
|