@mastra/playground-ui 22.2.0-alpha.9 → 23.0.0-alpha.10

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 CHANGED
@@ -1,5 +1,42 @@
1
1
  # @mastra/playground-ui
2
2
 
3
+ ## 23.0.0-alpha.10
4
+
5
+ ### Minor Changes
6
+
7
+ - Added `ErrorBoundary` component to catch and display runtime errors in the studio. Wraps routes in the local playground so a crash on one page (e.g. an agent editor referencing an unresolved workspace skill) surfaces a friendly recovery UI with **Try again** (in-place React reset), **Reload page** (full browser refresh), and **Report issue** (opens the Mastra GitHub issues page in a new tab) actions, plus a collapsible stack trace — instead of a blank screen. ([#15561](https://github.com/mastra-ai/mastra/pull/15561))
8
+
9
+ The fallback is spatially aware: it fills its parent and the icon, heading, and body text scale up on wider containers via Tailwind container queries. Scope the boundary to a single widget to keep the rest of the UI interactive while one panel fails.
10
+
11
+ **Usage**
12
+
13
+ ```tsx
14
+ import { ErrorBoundary } from '@mastra/playground-ui';
15
+ import { useLocation } from 'react-router';
16
+
17
+ // Route-level: wrap the router outlet, reset when the path changes
18
+ function Layout({ children }) {
19
+ const { pathname } = useLocation();
20
+ return <ErrorBoundary resetKeys={[pathname]}>{children}</ErrorBoundary>;
21
+ }
22
+
23
+ // Scoped: contain the crash to one panel, leave the rest of the tree alone
24
+ <ErrorBoundary variant="inline" title="The editor failed to render">
25
+ <AgentEditor />
26
+ </ErrorBoundary>;
27
+ ```
28
+
29
+ Props: `fallback` (node or render prop with `{ error, errorInfo, reset }`), `onError` for reporting, `resetKeys` for automatic reset, `variant` (`'section'` — fills available space, default; `'inline'` — stays compact), and `title` / `description` overrides.
30
+
31
+ ### Patch Changes
32
+
33
+ - Align BrandLoader geometry with the Mastra logo: match disk positions to the logo path, introduce per-size stroke widths and bubble radii (sm/md/lg), and rebalance the gooey filter for rounder ridge↔disk fillets. Shift the size scale so sm stays, md is now w-8, lg is now w-10, and the old w-16 size is removed. ([#15531](https://github.com/mastra-ai/mastra/pull/15531))
34
+
35
+ - Updated dependencies [[`aba393e`](https://github.com/mastra-ai/mastra/commit/aba393e2da7390c69b80e516a4f153cda6f09376), [`0a5fa1d`](https://github.com/mastra-ai/mastra/commit/0a5fa1d3cb0583889d06687155f26fd7d2edc76c), [`ea43e64`](https://github.com/mastra-ai/mastra/commit/ea43e646dd95d507694b6112b0bf1df22ad552b2), [`00d1b16`](https://github.com/mastra-ai/mastra/commit/00d1b16b401199cb294fa23f43336547db4dca9b), [`af8a57e`](https://github.com/mastra-ai/mastra/commit/af8a57ed9ba9685ad8601d5b71ae3706da6222f9), [`be49755`](https://github.com/mastra-ai/mastra/commit/be4975575e63b38f63af588ea8ce6f4cf5b8ff2c)]:
36
+ - @mastra/core@1.26.0-alpha.10
37
+ - @mastra/client-js@1.14.0-alpha.10
38
+ - @mastra/react@0.2.27-alpha.10
39
+
3
40
  ## 22.2.0-alpha.9
4
41
 
5
42
  ### Minor Changes
package/dist/index.cjs.js CHANGED
@@ -7716,43 +7716,46 @@ Slider.displayName = SliderPrimitive__namespace.Root.displayName;
7716
7716
 
7717
7717
  const sizeClasses$1 = {
7718
7718
  sm: "w-6",
7719
- md: "w-10",
7720
- lg: "w-16"
7719
+ md: "w-8",
7720
+ lg: "w-10"
7721
+ };
7722
+ const strokeBySize = {
7723
+ sm: 2.2,
7724
+ md: 2.5,
7725
+ lg: 2.9
7726
+ };
7727
+ const bubbleBySize = {
7728
+ sm: 4.3,
7729
+ md: 4.4,
7730
+ lg: 4.498
7721
7731
  };
7722
7732
  function BrandLoader({ className, size = "md", "aria-label": ariaLabel = "Loading" }) {
7723
7733
  const reactId = React.useId();
7724
7734
  const filterId = `brand-loader-${reactId.replace(/[^a-zA-Z0-9_-]/g, "")}`;
7735
+ const r = bubbleBySize[size];
7725
7736
  return /* @__PURE__ */ jsxRuntime.jsx(
7726
7737
  "div",
7727
7738
  {
7728
7739
  role: "status",
7729
7740
  "aria-label": ariaLabel,
7730
7741
  className: cn("brand-loader inline-block text-neutral6", sizeClasses$1[size], className),
7731
- children: /* @__PURE__ */ jsxRuntime.jsxs(
7732
- "svg",
7733
- {
7734
- xmlns: "http://www.w3.org/2000/svg",
7735
- viewBox: "0 0 34 21",
7736
- className: "block w-full h-auto overflow-visible",
7737
- "aria-hidden": "true",
7738
- children: [
7739
- /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsxs("filter", { id: filterId, children: [
7740
- /* @__PURE__ */ jsxRuntime.jsx("feGaussianBlur", { in: "SourceGraphic", stdDeviation: "0.55" }),
7741
- /* @__PURE__ */ jsxRuntime.jsx("feColorMatrix", { values: "1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7" })
7742
- ] }) }),
7743
- /* @__PURE__ */ jsxRuntime.jsxs("g", { filter: `url(#${filterId})`, children: [
7744
- /* @__PURE__ */ jsxRuntime.jsx("line", { className: "brand-loader-ln23", x1: "10.4", y1: "4.5", x2: "16.8", y2: "16.2" }),
7745
- /* @__PURE__ */ jsxRuntime.jsx("line", { className: "brand-loader-ln34", x1: "16.8", y1: "16.2", x2: "23.2", y2: "4.5" }),
7746
- /* @__PURE__ */ jsxRuntime.jsx("line", { className: "brand-loader-ln45", x1: "23.2", y1: "4.5", x2: "29.5", y2: "16.2" }),
7747
- /* @__PURE__ */ jsxRuntime.jsx("circle", { className: "brand-loader-b1", cx: "4.5", cy: "16.2", r: "4.5" }),
7748
- /* @__PURE__ */ jsxRuntime.jsx("circle", { className: "brand-loader-b2", cx: "10.4", cy: "4.5", r: "4.5" }),
7749
- /* @__PURE__ */ jsxRuntime.jsx("circle", { className: "brand-loader-b3", cx: "16.8", cy: "16.2", r: "4.5" }),
7750
- /* @__PURE__ */ jsxRuntime.jsx("circle", { className: "brand-loader-b4", cx: "23.2", cy: "4.5", r: "4.5" }),
7751
- /* @__PURE__ */ jsxRuntime.jsx("circle", { className: "brand-loader-b5", cx: "29.5", cy: "16.2", r: "4.5" })
7752
- ] })
7753
- ]
7754
- }
7755
- )
7742
+ style: { ["--brand-loader-stroke"]: strokeBySize[size] },
7743
+ children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 34 21", "aria-hidden": "true", children: [
7744
+ /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsxs("filter", { id: filterId, x: "-5%", y: "-5%", width: "110%", height: "110%", children: [
7745
+ /* @__PURE__ */ jsxRuntime.jsx("feGaussianBlur", { in: "SourceGraphic", stdDeviation: "0.9" }),
7746
+ /* @__PURE__ */ jsxRuntime.jsx("feColorMatrix", { values: "1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 11 -4.3" })
7747
+ ] }) }),
7748
+ /* @__PURE__ */ jsxRuntime.jsxs("g", { filter: `url(#${filterId})`, children: [
7749
+ /* @__PURE__ */ jsxRuntime.jsx("line", { className: "brand-loader-ln23", x1: "10.387", y1: "4.498", x2: "16.899", y2: "16.192" }),
7750
+ /* @__PURE__ */ jsxRuntime.jsx("line", { className: "brand-loader-ln34", x1: "16.899", y1: "16.192", x2: "22.815", y2: "4.56" }),
7751
+ /* @__PURE__ */ jsxRuntime.jsx("line", { className: "brand-loader-ln45", x1: "22.815", y1: "4.56", x2: "28.57", y2: "16.192" }),
7752
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { className: "brand-loader-b1", cx: "4.498", cy: "16.192", r }),
7753
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { className: "brand-loader-b2", cx: "10.387", cy: "4.498", r }),
7754
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { className: "brand-loader-b3", cx: "16.899", cy: "16.192", r }),
7755
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { className: "brand-loader-b4", cx: "22.815", cy: "4.56", r }),
7756
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { className: "brand-loader-b5", cx: "28.57", cy: "16.192", r })
7757
+ ] })
7758
+ ] })
7756
7759
  }
7757
7760
  );
7758
7761
  }
@@ -12003,6 +12006,152 @@ const ListSearch = ({
12003
12006
  );
12004
12007
  };
12005
12008
 
12009
+ const INITIAL_STATE = { error: null, errorInfo: null };
12010
+ function keysChanged(prev, next) {
12011
+ if (prev === next) return false;
12012
+ if (!prev || !next) return true;
12013
+ if (prev.length !== next.length) return true;
12014
+ for (let i = 0; i < prev.length; i++) {
12015
+ if (!Object.is(prev[i], next[i])) return true;
12016
+ }
12017
+ return false;
12018
+ }
12019
+ class ErrorBoundary extends React__namespace.Component {
12020
+ state = INITIAL_STATE;
12021
+ static getDerivedStateFromError(error) {
12022
+ return { error };
12023
+ }
12024
+ componentDidCatch(error, errorInfo) {
12025
+ this.setState({ errorInfo });
12026
+ if (this.props.onError) {
12027
+ try {
12028
+ this.props.onError(error, errorInfo);
12029
+ } catch (handlerError) {
12030
+ if (typeof console !== "undefined") {
12031
+ console.error("[ErrorBoundary] onError handler threw:", handlerError);
12032
+ }
12033
+ }
12034
+ }
12035
+ if (typeof console !== "undefined") {
12036
+ console.error("[ErrorBoundary] Uncaught error:", error, errorInfo);
12037
+ }
12038
+ }
12039
+ componentDidUpdate(prevProps) {
12040
+ if (this.state.error && keysChanged(prevProps.resetKeys, this.props.resetKeys)) {
12041
+ this.reset();
12042
+ }
12043
+ }
12044
+ reset = () => {
12045
+ this.setState(INITIAL_STATE);
12046
+ };
12047
+ render() {
12048
+ const { error, errorInfo } = this.state;
12049
+ const { children, fallback, title, description, variant, className } = this.props;
12050
+ if (!error) return children;
12051
+ if (typeof fallback === "function") {
12052
+ return fallback({ error, errorInfo, reset: this.reset });
12053
+ }
12054
+ if (fallback !== void 0) return fallback;
12055
+ return /* @__PURE__ */ jsxRuntime.jsx(
12056
+ DefaultErrorFallback,
12057
+ {
12058
+ error,
12059
+ errorInfo,
12060
+ reset: this.reset,
12061
+ title,
12062
+ description,
12063
+ variant: variant ?? "section",
12064
+ className
12065
+ }
12066
+ );
12067
+ }
12068
+ }
12069
+ function DefaultErrorFallback({
12070
+ error,
12071
+ errorInfo,
12072
+ reset,
12073
+ title,
12074
+ description,
12075
+ variant,
12076
+ className
12077
+ }) {
12078
+ const stack = errorInfo?.componentStack ?? error.stack ?? "";
12079
+ const isInline = variant === "inline";
12080
+ return /* @__PURE__ */ jsxRuntime.jsx(
12081
+ "div",
12082
+ {
12083
+ role: "alert",
12084
+ className: cn(
12085
+ "@container flex w-full items-center justify-center",
12086
+ isInline ? "py-6 px-4" : "h-full min-h-[240px] flex-1 py-10 px-6",
12087
+ className
12088
+ ),
12089
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
12090
+ "div",
12091
+ {
12092
+ className: cn(
12093
+ "flex flex-col items-center text-center",
12094
+ isInline ? "gap-3 max-w-md" : "gap-4 max-w-2xl @md:gap-5 @lg:gap-6"
12095
+ ),
12096
+ children: [
12097
+ /* @__PURE__ */ jsxRuntime.jsx(
12098
+ "div",
12099
+ {
12100
+ className: cn(
12101
+ "rounded-full bg-accent2/10 text-accent2 flex items-center justify-center",
12102
+ isInline ? "h-10 w-10 [&>svg]:h-5 [&>svg]:w-5" : "h-14 w-14 [&>svg]:h-7 [&>svg]:w-7 @md:h-16 @md:w-16 @md:[&>svg]:h-8 @md:[&>svg]:w-8 @lg:h-20 @lg:w-20 @lg:[&>svg]:h-10 @lg:[&>svg]:w-10"
12103
+ ),
12104
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, {})
12105
+ }
12106
+ ),
12107
+ /* @__PURE__ */ jsxRuntime.jsx(
12108
+ "h3",
12109
+ {
12110
+ className: cn(
12111
+ "font-medium text-neutral6",
12112
+ isInline ? "text-ui-md" : "text-ui-lg @md:text-header-md @lg:text-header-lg"
12113
+ ),
12114
+ children: title ?? "Something went wrong"
12115
+ }
12116
+ ),
12117
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: cn("text-neutral3", isInline ? "text-ui-sm" : "text-ui-md @lg:text-ui-lg"), children: description ?? "An unexpected error occurred while rendering this part of the page." }),
12118
+ /* @__PURE__ */ jsxRuntime.jsx(
12119
+ "p",
12120
+ {
12121
+ className: cn(
12122
+ "font-mono text-neutral4 break-words rounded-md bg-surface3 px-3 py-2",
12123
+ isInline ? "text-ui-xs" : "text-ui-sm"
12124
+ ),
12125
+ children: error.message
12126
+ }
12127
+ ),
12128
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex flex-wrap items-center justify-center gap-2", isInline ? "mt-1" : "mt-2"), children: [
12129
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", size: isInline ? "sm" : "default", onClick: reset, children: "Try again" }),
12130
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "default", size: isInline ? "sm" : "default", onClick: () => window.location.reload(), children: "Reload page" }),
12131
+ /* @__PURE__ */ jsxRuntime.jsx(
12132
+ Button,
12133
+ {
12134
+ as: "a",
12135
+ variant: "default",
12136
+ size: isInline ? "sm" : "default",
12137
+ href: "https://github.com/mastra-ai/mastra/issues",
12138
+ target: "_blank",
12139
+ rel: "noopener noreferrer",
12140
+ children: "Report issue"
12141
+ }
12142
+ )
12143
+ ] }),
12144
+ stack ? /* @__PURE__ */ jsxRuntime.jsxs("details", { className: cn("w-full text-left", isInline ? "mt-1" : "mt-2"), children: [
12145
+ /* @__PURE__ */ jsxRuntime.jsx("summary", { className: "cursor-pointer text-ui-sm text-neutral3 hover:text-neutral4", children: "Show error details" }),
12146
+ /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "mt-2 max-h-64 overflow-auto rounded-md bg-surface3 p-3 text-ui-xs text-neutral4 whitespace-pre-wrap break-words", children: stack })
12147
+ ] }) : null
12148
+ ]
12149
+ }
12150
+ )
12151
+ }
12152
+ );
12153
+ }
12154
+
12006
12155
  function ErrorState({ title, message }) {
12007
12156
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-[30vh]", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center text-center py-10 px-6", children: [
12008
12157
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CircleXIcon, { className: "h-8 w-8 text-red-900" }) }),
@@ -14356,6 +14505,7 @@ exports.EntryCell = EntryCell;
14356
14505
  exports.EntryList = EntryList;
14357
14506
  exports.EntryListSkeleton = EntryListSkeleton;
14358
14507
  exports.EnvIcon = EnvIcon;
14508
+ exports.ErrorBoundary = ErrorBoundary;
14359
14509
  exports.ErrorState = ErrorState;
14360
14510
  exports.Field = Field;
14361
14511
  exports.FieldBlock = FieldBlock;