diff --git a/packages/devtools/src/inspect/ReactLangStreamEventRow.tsx b/packages/devtools/src/inspect/ReactLangStreamEventRow.tsx index 34294f176..fa0ad9aef 100644 --- a/packages/devtools/src/inspect/ReactLangStreamEventRow.tsx +++ b/packages/devtools/src/inspect/ReactLangStreamEventRow.tsx @@ -104,6 +104,8 @@ export function ReactLangStreamEventRow({ }; const openInDebugDisabled = isStreaming || !stream.response || !canOpenInDebug; + const hasOverview = + visibleErrors.length > 0 || statementCount !== undefined || orphaned.length > 0; return (
-
- {visibleErrors.length > 0 ? ( - - error{visibleErrors.length === 1 ? "" : "s"} - - ) : null} - {statementCount !== undefined ? ( - statement{statementCount === 1 ? "" : "s"} - ) : null} - {orphaned.length > 0 ? ( - - orphaned statement{orphaned.length === 1 ? "" : "s"} - - ) : null} -
+ {hasOverview ? ( +
+ {visibleErrors.length > 0 ? ( + + error{visibleErrors.length === 1 ? "" : "s"} + + ) : null} + {statementCount !== undefined ? ( + statement{statementCount === 1 ? "" : "s"} + ) : null} + {orphaned.length > 0 ? ( + + orphaned statement{orphaned.length === 1 ? "" : "s"} + + ) : null} +
+ ) : null} {expanded ? ( diff --git a/packages/devtools/src/ui/ReliabilityBanner.tsx b/packages/devtools/src/ui/ReliabilityBanner.tsx index 4663b7efc..25f198f3c 100644 --- a/packages/devtools/src/ui/ReliabilityBanner.tsx +++ b/packages/devtools/src/ui/ReliabilityBanner.tsx @@ -1,38 +1,60 @@ +import { Check, CircleAlert } from "lucide-react"; import { CSSProperties, useState } from "react"; import { withDevtoolsAttribution } from "../lib/links"; import { ColorMode, FONT, theme, ThemeTokens } from "../theme"; +const FEATURES = [ + "Automatically fix 88% of the errors", + "Track requests & generations for free", +] as const; + export default function ReliabilityBanner({ themeMode }: { themeMode: ColorMode }) { const [bannerHovered, setBannerHovered] = useState(false); - const styles = bannerStyles(theme(themeMode)); + const tokens = theme(themeMode); + const styles = bannerStyles(tokens); return (
- setBannerHovered(true)} - onMouseLeave={() => setBannerHovered(false)} - > - - Want to track and fix errors in production? - - - Learn more +
+ + + + + Your users may see these errors in production - +
+
+ Try OpenUI Autofix +
    + {FEATURES.map((feature) => ( +
  • + + + + {feature} +
  • + ))} +
+
+ setBannerHovered(true)} + onMouseLeave={() => setBannerHovered(false)} + > + Autofix errors + +
+
); } @@ -47,7 +69,7 @@ const bannerStyles = (t: ThemeTokens) => gap: 8, flexShrink: 0, background: t.bg, - padding: "12px 12px 18px", + padding: "6px", }, fade: { position: "absolute", @@ -60,39 +82,102 @@ const bannerStyles = (t: ThemeTokens) => }, banner: { display: "flex", - alignItems: "center", - justifyContent: "space-between", - gap: 12, + flexDirection: "column", + alignItems: "stretch", flexShrink: 0, - textDecoration: "none", + overflow: "hidden", + border: `1px solid ${t.border}`, borderRadius: 12, - background: t.promoBg, + background: t.card, + boxShadow: t.shadowSubtle, color: t.fg, - cursor: "pointer", fontFamily: FONT, textAlign: "left", - padding: "12px 14px", + padding: "6px", + }, + title: { + display: "flex", + alignItems: "flex-start", + gap: 8, + background: t.dangerBg, + color: t.danger, + fontSize: 13, + fontWeight: 600, + lineHeight: 1.3, + padding: "8px 16px 8px 8px", + borderRadius: 8, }, - text: { + body: { display: "flex", flexDirection: "column", + gap: 12, + padding: "18px 12px 12px 12px", }, - title: { + featuresWrapper: { + display: "flex", + flexDirection: "column", + gap: 6, + }, + alertTile: { + display: "inline-flex", + alignItems: "center", + justifyContent: "center", + flexShrink: 0, + width: 18, + height: 18, + marginTop: 1, + borderRadius: 5, + background: t.danger, + color: t.dangerBg, + }, + statement: { fontSize: 12, - fontWeight: 500, + fontWeight: 600, + lineHeight: 1.35, }, - bannerAction: { + features: { + display: "flex", + flexDirection: "column", + gap: 6, + margin: 0, + padding: 0, + listStyle: "none", + }, + feature: { + display: "flex", + alignItems: "center", + gap: 8, + color: t.fg, + fontSize: 12, + lineHeight: 1.35, + }, + checkTile: { display: "inline-flex", alignItems: "center", justifyContent: "center", flexShrink: 0, - border: `1px solid ${t.controlBorder}`, + width: 18, + height: 18, + borderRadius: 5, + background: t.successBg, + }, + check: { + color: t.success, + }, + bannerAction: { + display: "inline-flex", + alignItems: "center", + justifyContent: "center", + alignSelf: "flex-start", + marginTop: 8, + textDecoration: "none", + border: `1px solid ${t.inverted}`, borderRadius: 8, - background: t.controlBg, - color: t.fg, - boxShadow: t.shadowSubtle, + background: t.inverted, + color: t.invertedFg, fontSize: 11, - padding: "5px 12px", + fontWeight: 500, + padding: "7px 12px", transition: "transform 150ms ease", }, bannerActionHover: {