import { CellId } from "@nteract/commutable"; import { CellType } from "@nteract/commutable/src"; import { actions, ContentRef, selectors } from "@nteract/core"; import { Cells, CodeCell, RawCell } from "@nteract/stateful-components"; import CodeMirrorEditor from "@nteract/stateful-components/lib/inputs/connected-editors/codemirror"; import { PassedEditorProps } from "@nteract/stateful-components/lib/inputs/editor"; import * as React from "react"; import { DndProvider } from "react-dnd"; import { HTML5Backend } from "react-dnd-html5-backend"; import { connect } from "react-redux"; import { Dispatch } from "redux"; import { userContext } from "../../../UserContext"; import * as cdbActions from "../NotebookComponent/actions"; import loadTransform from "../NotebookComponent/loadTransform"; import { CdbAppState, SnapshotFragment, SnapshotRequest } from "../NotebookComponent/types"; import { NotebookUtil } from "../NotebookUtil"; import SecurityWarningBar from "../SecurityWarningBar/SecurityWarningBar"; import { AzureTheme } from "./AzureTheme"; import "./base.css"; import CellCreator from "./decorators/CellCreator"; import CellLabeler from "./decorators/CellLabeler"; import HoverableCell from "./decorators/HoverableCell"; import KeyboardShortcuts from "./decorators/kbd-shortcuts"; import "./default.css"; import MarkdownCell from "./markdown-cell"; import "./NotebookRenderer.less"; import SandboxOutputs from "./outputs/SandboxOutputs"; import Prompt from "./Prompt"; import { promptContent } from "./PromptContent"; import StatusBar from "./StatusBar"; import CellToolbar from "./Toolbar"; export interface NotebookRendererBaseProps { contentRef: any; } interface NotebookRendererDispatchProps { storeNotebookSnapshot: (imageSrc: string, requestId: string) => void; notebookSnapshotError: (error: string) => void; } interface StateProps { pendingSnapshotRequest: SnapshotRequest; cellOutputSnapshots: Map; notebookSnapshot: { imageSrc: string; requestId: string }; nbCodeCells: number; } type NotebookRendererProps = NotebookRendererBaseProps & NotebookRendererDispatchProps & StateProps; const decorate = (id: string, contentRef: ContentRef, cell_type: CellType, children: React.ReactNode) => { const Cell = () => ( // TODO Draggable and HijackScroll not working anymore. Fix or remove when reworking MarkdownCell. // // {children} // // ); Cell.defaultProps = { cell_type }; return ; }; class BaseNotebookRenderer extends React.Component { private notebookRendererRef = React.createRef(); componentDidMount() { if (!userContext.features.sandboxNotebookOutputs) { loadTransform(this.props as any); } } async componentDidUpdate(): Promise { // Take a snapshot if there's a pending request and all the outputs are also saved if ( this.props.pendingSnapshotRequest && this.props.pendingSnapshotRequest.type === "notebook" && this.props.pendingSnapshotRequest.notebookContentRef === this.props.contentRef && (!this.props.notebookSnapshot || this.props.pendingSnapshotRequest.requestId !== this.props.notebookSnapshot.requestId) && this.props.cellOutputSnapshots.size === this.props.nbCodeCells ) { try { // Use Html2Canvas because it is much more reliable and fast than dom-to-file const result = await NotebookUtil.takeScreenshotHtml2Canvas( this.notebookRendererRef.current, this.props.pendingSnapshotRequest.aspectRatio, [...this.props.cellOutputSnapshots.values()], this.props.pendingSnapshotRequest.downloadFilename ); this.props.storeNotebookSnapshot(result.imageSrc, this.props.pendingSnapshotRequest.requestId); } catch (error) { this.props.notebookSnapshotError(error.message); } finally { this.setState({ processedSnapshotRequest: undefined }); } } } render(): JSX.Element { return ( <>
{{ code: ({ id, contentRef }: { id: CellId; contentRef: ContentRef }) => decorate( id, contentRef, "code", {{ editor: { codemirror: (props: PassedEditorProps) => ( ), }, prompt: ({ id, contentRef }: { id: CellId; contentRef: ContentRef }) => ( {promptContent} ), toolbar: () => , outputs: userContext.features.sandboxNotebookOutputs ? () => : undefined, }} ), markdown: ({ id, contentRef }: { id: any; contentRef: ContentRef }) => decorate( id, contentRef, "markdown", {{ editor: { codemirror: (props: PassedEditorProps) => ( ), }, toolbar: () => , }} ), raw: ({ id, contentRef }: { id: any; contentRef: ContentRef }) => decorate( id, contentRef, "raw", {{ editor: { codemirror: (props: PassedEditorProps) => ( ), }, toolbar: () => , }} ), }}
); } } export const makeMapStateToProps = ( initialState: CdbAppState, ownProps: NotebookRendererProps ): ((state: CdbAppState) => StateProps) => { const mapStateToProps = (state: CdbAppState): StateProps => { const { contentRef } = ownProps; const model = selectors.model(state, { contentRef }); let nbCodeCells; if (model && model.type === "notebook") { nbCodeCells = NotebookUtil.findCodeCellWithDisplay(model.notebook).length; } const { pendingSnapshotRequest, cellOutputSnapshots, notebookSnapshot } = state.cdb; return { pendingSnapshotRequest, cellOutputSnapshots, notebookSnapshot, nbCodeCells }; }; return mapStateToProps; }; const makeMapDispatchToProps = (initialDispatch: Dispatch, initialProps: NotebookRendererBaseProps) => { const mapDispatchToProps = (dispatch: Dispatch) => { return { addTransform: (transform: React.ComponentType & { MIMETYPE: string }) => dispatch( actions.addTransform({ mediaType: transform.MIMETYPE, component: transform, }) ), storeNotebookSnapshot: (imageSrc: string, requestId: string) => dispatch(cdbActions.storeNotebookSnapshot({ imageSrc, requestId })), notebookSnapshotError: (error: string) => dispatch(cdbActions.notebookSnapshotError({ error })), }; }; return mapDispatchToProps; }; export default connect(makeMapStateToProps, makeMapDispatchToProps)(BaseNotebookRenderer);