@dbx-tools/appkit-mastra 0.3.20 → 0.3.21
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 +19 -0
- package/package.json +10 -10
- package/src/chart.ts +64 -9
- package/src/config.ts +16 -0
package/README.md
CHANGED
|
@@ -251,6 +251,25 @@ Agents can return `[chart:<id>]` and `[data:<statement_id>]` markers in prose.
|
|
|
251
251
|
The embed route resolves them later, which avoids forcing the language model to
|
|
252
252
|
inline large tables or wait for chart planning before continuing its answer.
|
|
253
253
|
|
|
254
|
+
### Brand The Charts
|
|
255
|
+
|
|
256
|
+
Pass a `brand` to the plugin to theme every generated chart with your brand's
|
|
257
|
+
palette and font; omit it for the default Echarts look.
|
|
258
|
+
|
|
259
|
+
```ts
|
|
260
|
+
import { brand } from "@dbx-tools/shared-core";
|
|
261
|
+
|
|
262
|
+
plugin.mastra({ agents, storage: true, brand: brand.defaultBrandContext });
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
`brand` is the portable `BrandContext` shared across the UI, email, and
|
|
266
|
+
libraries, so charts, email, and the chat UI theme from one source. The chart
|
|
267
|
+
planner derives an Echarts theme from it: a series color cycle seeded from
|
|
268
|
+
`colors.primary` / `colors.accent` (plus a colorblind-friendly spread so
|
|
269
|
+
many-series charts stay legible) and a base text style from `typography.sans` /
|
|
270
|
+
`colors.foreground`. Charts render to canvas, so this is applied server-side on
|
|
271
|
+
the Echarts option rather than through the browser `[data-brand]` CSS bridge.
|
|
272
|
+
|
|
254
273
|
## Model Selection
|
|
255
274
|
|
|
256
275
|
`model.buildModel()` adapts the generic resolver from
|
package/package.json
CHANGED
|
@@ -28,22 +28,22 @@
|
|
|
28
28
|
"express": "^5.1.0",
|
|
29
29
|
"pg": "^8.22.0",
|
|
30
30
|
"zod": "^4.3.6",
|
|
31
|
-
"@dbx-tools/
|
|
32
|
-
"@dbx-tools/
|
|
33
|
-
"@dbx-tools/databricks": "0.3.
|
|
34
|
-
"@dbx-tools/
|
|
35
|
-
"@dbx-tools/
|
|
36
|
-
"@dbx-tools/
|
|
37
|
-
"@dbx-tools/
|
|
38
|
-
"@dbx-tools/shared-mastra": "0.3.
|
|
39
|
-
"@dbx-tools/shared-model": "0.3.
|
|
31
|
+
"@dbx-tools/core": "0.3.21",
|
|
32
|
+
"@dbx-tools/appkit": "0.3.21",
|
|
33
|
+
"@dbx-tools/databricks": "0.3.21",
|
|
34
|
+
"@dbx-tools/shared-genie": "0.3.21",
|
|
35
|
+
"@dbx-tools/model": "0.3.21",
|
|
36
|
+
"@dbx-tools/shared-core": "0.3.21",
|
|
37
|
+
"@dbx-tools/genie": "0.3.21",
|
|
38
|
+
"@dbx-tools/shared-mastra": "0.3.21",
|
|
39
|
+
"@dbx-tools/shared-model": "0.3.21"
|
|
40
40
|
},
|
|
41
41
|
"main": "index.ts",
|
|
42
42
|
"license": "UNLICENSED",
|
|
43
43
|
"publishConfig": {
|
|
44
44
|
"access": "public"
|
|
45
45
|
},
|
|
46
|
-
"version": "0.3.
|
|
46
|
+
"version": "0.3.21",
|
|
47
47
|
"types": "index.ts",
|
|
48
48
|
"type": "module",
|
|
49
49
|
"exports": {
|
package/src/chart.ts
CHANGED
|
@@ -37,7 +37,7 @@ import { z } from "zod";
|
|
|
37
37
|
import type { MastraPluginConfig } from "./config";
|
|
38
38
|
import { buildModel } from "./model";
|
|
39
39
|
import { model } from "@dbx-tools/shared-model";
|
|
40
|
-
import { async, error, hash, log, string } from "@dbx-tools/shared-core";
|
|
40
|
+
import { async, error, hash, log, string, type BrandContext } from "@dbx-tools/shared-core";
|
|
41
41
|
|
|
42
42
|
const logger = log.logger("mastra/chart");
|
|
43
43
|
|
|
@@ -353,7 +353,7 @@ async function runChartPlanner(
|
|
|
353
353
|
...(abortSignal ? { abortSignal } : {}),
|
|
354
354
|
});
|
|
355
355
|
const plan = chartPlanSchema.parse(result.object);
|
|
356
|
-
const option = planToEchartsOption(plan, title);
|
|
356
|
+
const option = planToEchartsOption(plan, title, config.brand);
|
|
357
357
|
return { chartType: plan.chartType, option };
|
|
358
358
|
}
|
|
359
359
|
|
|
@@ -551,6 +551,50 @@ export async function fetchChart(
|
|
|
551
551
|
}
|
|
552
552
|
}
|
|
553
553
|
|
|
554
|
+
/* ----------------------------- echarts theme ----------------------------- */
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* The slice of an Echarts option that carries brand identity: the series
|
|
558
|
+
* color cycle and the base text style (font family + foreground color).
|
|
559
|
+
* Derived from a {@link BrandContext} by {@link brandChartTheme} and merged
|
|
560
|
+
* into every spec by {@link planToEchartsOption}.
|
|
561
|
+
*/
|
|
562
|
+
interface ChartTheme {
|
|
563
|
+
color: string[];
|
|
564
|
+
textStyle: { fontFamily: string; color: string };
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Expand two brand hex colors into a legible categorical cycle. Echarts
|
|
569
|
+
* repeats the `color` array across series / categories, so a good default
|
|
570
|
+
* needs several distinct hues, not two. We seed with the brand primary and
|
|
571
|
+
* accent (the identity colors) and follow with a fixed, colorblind-friendly
|
|
572
|
+
* spread so multi-series / many-slice charts stay readable while the first
|
|
573
|
+
* one or two marks still land on-brand.
|
|
574
|
+
*/
|
|
575
|
+
function brandColorCycle(primary: string, accent: string): string[] {
|
|
576
|
+
const spread = ["#5B8FF9", "#F6BD16", "#E86452", "#6DC8EC", "#945FB9", "#FF9845", "#1E9493"];
|
|
577
|
+
const seen = new Set<string>();
|
|
578
|
+
return [primary, accent, ...spread].filter((c) => {
|
|
579
|
+
const key = c.toLowerCase();
|
|
580
|
+
if (seen.has(key)) return false;
|
|
581
|
+
seen.add(key);
|
|
582
|
+
return true;
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Derive an Echarts {@link ChartTheme} from a brand context: the primary +
|
|
588
|
+
* accent colors seed the series cycle, the sans stack becomes the base font,
|
|
589
|
+
* and the brand foreground becomes the base text color.
|
|
590
|
+
*/
|
|
591
|
+
function brandChartTheme(brand: BrandContext): ChartTheme {
|
|
592
|
+
return {
|
|
593
|
+
color: brandColorCycle(brand.colors.primary, brand.colors.accent),
|
|
594
|
+
textStyle: { fontFamily: brand.typography.sans, color: brand.colors.foreground },
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
|
|
554
598
|
/* ----------------------------- echarts expansion ----------------------------- */
|
|
555
599
|
|
|
556
600
|
/**
|
|
@@ -559,10 +603,21 @@ export async function fetchChart(
|
|
|
559
603
|
* compact plan shape; tooltip / animation / color / grid defaults
|
|
560
604
|
* stay consistent across charts and are easy to tune without
|
|
561
605
|
* retraining model behaviour.
|
|
606
|
+
*
|
|
607
|
+
* When `brand` is set, the resolved spec is themed with the brand's series
|
|
608
|
+
* color cycle and base text style (see {@link brandChartTheme}); otherwise
|
|
609
|
+
* Echarts' defaults apply.
|
|
562
610
|
*/
|
|
563
|
-
function planToEchartsOption(
|
|
611
|
+
function planToEchartsOption(
|
|
612
|
+
plan: ChartPlan,
|
|
613
|
+
fallbackTitle: string,
|
|
614
|
+
brand?: BrandContext,
|
|
615
|
+
): Record<string, unknown> {
|
|
564
616
|
const baseTitle = plan.title ?? fallbackTitle;
|
|
565
617
|
const grid = { left: 48, right: 24, top: 56, bottom: 48, containLabel: true };
|
|
618
|
+
const theme = brand ? brandChartTheme(brand) : undefined;
|
|
619
|
+
const themed = (option: Record<string, unknown>): Record<string, unknown> =>
|
|
620
|
+
theme ? { ...theme, ...option } : option;
|
|
566
621
|
|
|
567
622
|
if (plan.chartType === "pie") {
|
|
568
623
|
// Echarts crashes on null pie slices - filter them out.
|
|
@@ -573,7 +628,7 @@ function planToEchartsOption(plan: ChartPlan, fallbackTitle: string): Record<str
|
|
|
573
628
|
(d): d is { name: string; value: number } =>
|
|
574
629
|
d !== null && typeof d === "object" && !Array.isArray(d),
|
|
575
630
|
);
|
|
576
|
-
return {
|
|
631
|
+
return themed({
|
|
577
632
|
title: { text: baseTitle, left: "center" },
|
|
578
633
|
tooltip: { trigger: "item" },
|
|
579
634
|
legend: { bottom: 0 },
|
|
@@ -585,14 +640,14 @@ function planToEchartsOption(plan: ChartPlan, fallbackTitle: string): Record<str
|
|
|
585
640
|
data: slices,
|
|
586
641
|
},
|
|
587
642
|
],
|
|
588
|
-
};
|
|
643
|
+
});
|
|
589
644
|
}
|
|
590
645
|
|
|
591
646
|
if (plan.chartType === "scatter") {
|
|
592
647
|
// Echarts crashes on null scatter points - keep only valid
|
|
593
648
|
// `[x, y]` tuples. Bare numbers / objects / nulls from a
|
|
594
649
|
// mismatched plan get dropped silently.
|
|
595
|
-
return {
|
|
650
|
+
return themed({
|
|
596
651
|
title: { text: baseTitle, left: "center" },
|
|
597
652
|
tooltip: { trigger: "item" },
|
|
598
653
|
legend: { bottom: 0 },
|
|
@@ -604,13 +659,13 @@ function planToEchartsOption(plan: ChartPlan, fallbackTitle: string): Record<str
|
|
|
604
659
|
type: "scatter",
|
|
605
660
|
data: s.data.filter((d): d is [number, number] => Array.isArray(d) && d.length === 2),
|
|
606
661
|
})),
|
|
607
|
-
};
|
|
662
|
+
});
|
|
608
663
|
}
|
|
609
664
|
|
|
610
665
|
// bar / line / area share the same axis layout.
|
|
611
666
|
const isArea = plan.chartType === "area";
|
|
612
667
|
const seriesType = plan.chartType === "bar" ? "bar" : "line";
|
|
613
|
-
return {
|
|
668
|
+
return themed({
|
|
614
669
|
title: { text: baseTitle, left: "center" },
|
|
615
670
|
tooltip: { trigger: "axis" },
|
|
616
671
|
legend: { bottom: 0 },
|
|
@@ -628,7 +683,7 @@ function planToEchartsOption(plan: ChartPlan, fallbackTitle: string): Record<str
|
|
|
628
683
|
smooth: seriesType === "line",
|
|
629
684
|
...(isArea ? { areaStyle: {} } : {}),
|
|
630
685
|
})),
|
|
631
|
-
};
|
|
686
|
+
});
|
|
632
687
|
}
|
|
633
688
|
|
|
634
689
|
/* ----------------------------- render_data tool ----------------------------- */
|
package/src/config.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { BasePluginConfig } from "@databricks/appkit";
|
|
11
|
+
import type { BrandContext } from "@dbx-tools/shared-core";
|
|
11
12
|
import type { AgentConfig } from "@mastra/core/agent";
|
|
12
13
|
import { MASTRA_RESOURCE_ID_KEY, MASTRA_THREAD_ID_KEY } from "@mastra/core/request-context";
|
|
13
14
|
import type { PgVectorConfig, PostgresStoreConfig } from "@mastra/pg";
|
|
@@ -442,4 +443,19 @@ export interface MastraPluginConfig extends BasePluginConfig {
|
|
|
442
443
|
* surface.
|
|
443
444
|
*/
|
|
444
445
|
apiAccess?: "scoped" | "full";
|
|
446
|
+
/**
|
|
447
|
+
* Optional brand context applied to charts produced by the built-in
|
|
448
|
+
* `render_data` / `prepare_chart` tools. When set, the chart planner's
|
|
449
|
+
* Echarts output is themed with the brand's palette (series colors derived
|
|
450
|
+
* from `colors.primary` / `colors.accent`) and sans font
|
|
451
|
+
* (`typography.sans`) instead of Echarts' defaults. Omit for the default
|
|
452
|
+
* Echarts look.
|
|
453
|
+
*
|
|
454
|
+
* Pass the portable {@link BrandContext} shared across the UI and
|
|
455
|
+
* libraries (e.g. `brand.defaultBrandContext` from `@dbx-tools/shared-core`,
|
|
456
|
+
* or a customer brand) - the same object the email add-on and the UI
|
|
457
|
+
* `BrandProvider` consume, so a host themes charts, email, and UI from one
|
|
458
|
+
* source.
|
|
459
|
+
*/
|
|
460
|
+
brand?: BrandContext;
|
|
445
461
|
}
|