diff --git a/src/app/app.less b/src/app/app.less index fdd9dd781..da93e0f66 100644 --- a/src/app/app.less +++ b/src/app/app.less @@ -373,6 +373,15 @@ a.a-block { } } +.load-error-text { + color: @term-red; + padding-top: 5px; +} + +.view-error { + padding: 10px 20px; +} + @keyframes loader-ring { 0% { transform: rotate(0deg); diff --git a/src/app/app.tsx b/src/app/app.tsx index 4038440f7..003ad822e 100644 --- a/src/app/app.tsx +++ b/src/app/app.tsx @@ -24,6 +24,7 @@ import { TosModal } from "./common/modals/modals"; import { WorkspaceView } from "../app/workspace/workspaceview"; import { MainSideBar } from "./sidebar/MainSideBar"; import { DisconnectedModal, ClientStopModal, AlertModal, WelcomeModal } from "./common/modals/modals"; +import { ErrorBoundary } from "./common/error/errorboundary"; import "./app.less"; dayjs.extend(localizedFormat); @@ -110,9 +111,11 @@ class App extends React.Component<{}, {}> {
- - - + + + + +
diff --git a/src/app/common/error/errorboundary.tsx b/src/app/common/error/errorboundary.tsx new file mode 100644 index 000000000..a87998fd7 --- /dev/null +++ b/src/app/common/error/errorboundary.tsx @@ -0,0 +1,65 @@ +import React, { Component, ReactNode } from "react"; +import { RendererContext } from "../../../plugins/types/types"; +import cn from "classnames"; + +interface ErrorBoundaryState { + hasError: boolean; + error: Error | null; +} + +interface ErrorBoundaryProps { + children: ReactNode; + plugin?: string; + lineContext?: RendererContext; +} + +class ErrorBoundary extends Component { + state: ErrorBoundaryState = { + hasError: false, + error: null, + }; + + static getDerivedStateFromError(error: Error): ErrorBoundaryState { + return { hasError: true, error }; + } + + componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void { + const { plugin, lineContext } = this.props; + + if (plugin && lineContext) { + console.log(`Plugin Name: ${plugin}\n`); + console.log(`Line Context: \n`); + console.log(`${JSON.stringify(lineContext, null, 4)}\n`); + } + + console.log(error); + } + + resetErrorBoundary = (): void => { + this.setState({ hasError: false, error: null }); + }; + + renderFallback() { + const { error } = this.state; + const { plugin } = this.props; + + return ( +
+
{`${error?.message}`}
+ {plugin &&
An error occurred in the {plugin} plugin
} +
+ ); + } + + render() { + const { hasError } = this.state; + + if (hasError) { + return this.renderFallback(); + } + + return this.props.children; + } +} + +export { ErrorBoundary }; diff --git a/src/app/line/linecomps.tsx b/src/app/line/linecomps.tsx index 7a604e1a9..598307db6 100644 --- a/src/app/line/linecomps.tsx +++ b/src/app/line/linecomps.tsx @@ -38,6 +38,7 @@ import { isBlank } from "../../util/util"; import { PluginModel } from "../../plugins/plugins"; import { Prompt } from "../common/prompt/prompt"; import * as lineutil from "./lineutil"; +import { ErrorBoundary } from "../../app/common/error/errorboundary"; import { ReactComponent as CheckIcon } from "../assets/icons/line/check.svg"; import { ReactComponent as CommentIcon } from "../assets/icons/line/comment.svg"; @@ -594,7 +595,6 @@ class LineCmd extends React.Component< let isRunning = cmd.isRunning(); let isExpanded = this.isCmdExpanded.get(); let rsdiff = this.rtnStateDiff.get(); - // console.log("render", "#" + line.linenum, termHeight, usedRows, cmd.getStatus(), (this.rtnStateDiff.get() != null), (!cmd.isRunning() ? "cmd-done" : "running")); let mainDivCn = cn( "line", "line-cmd", @@ -664,39 +664,41 @@ class LineCmd extends React.Component<
- - - - - - - - - + + + + + + + + + + +
{ cmdInputHeight = 110; } let isHidden = GlobalModel.activeMainView.get() != "session"; + return (
- -
- + + +
+ +
); }