Skip to content

Commit

Permalink
Merge pull request #35 from curvenote/feat/more-outputs
Browse files Browse the repository at this point in the history
feat/more outputs
  • Loading branch information
stevejpurves authored Apr 22, 2022
2 parents 3fe23f7 + 753c767 commit 2c7c507
Show file tree
Hide file tree
Showing 16 changed files with 549 additions and 166 deletions.
564 changes: 459 additions & 105 deletions app/content/demo/outputs.json

Large diffs are not rendered by default.

38 changes: 11 additions & 27 deletions app/myst-to-react/output/jupyter.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useEffect, useMemo, useRef, useState } from 'react';
import { useFetchAnyTruncatedContent } from './hooks';
import { MinifiedOutput } from '@curvenote/nbtx/dist/minify/types';
import { nanoid } from 'nanoid';
import {
selectIFrameReady,
Expand All @@ -9,29 +8,7 @@ import {
import { State } from '~/store';
import { useSelector } from 'react-redux';
import { host, actions } from '@curvenote/connect';
import type { IOutput } from '@jupyterlab/nbformat';

function toIOutputs(minified: MinifiedOutput[]): IOutput[] {
return minified.map((m: MinifiedOutput) => {
switch (m.output_type) {
case 'stream':
case 'error': {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { path, ...rest } = m;
return rest;
}
default: {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
return {
...m,
data: Object.entries(m.data).reduce((acc, [mimetype, payload]) => {
return { ...acc, [mimetype]: payload.content };
}, {}),
};
}
}
});
}
import { MinifiedOutput, convertToIOutputs } from '@curvenote/nbtx';

export const NativeJupyterOutputs = ({
id,
Expand All @@ -57,7 +34,7 @@ export const NativeJupyterOutputs = ({
if (iframeRef.current == null || !rendererReady || !data) return;
host.commsDispatch(
iframeRef.current,
actions.connectHostSendContent(uid, toIOutputs(data)),
actions.connectHostSendContent(uid, convertToIOutputs(data)),
);
}, [id, iframeRef.current, rendererReady]);

Expand All @@ -70,15 +47,22 @@ export const NativeJupyterOutputs = ({
return <div className="text-red-500">Error rendering output: {error.message}</div>;
}

if (process.env.NODE_ENV === 'development')
console.debug('Output connecting to http://localhost:3003');
console.log({ height });
return (
<>
{loading && <div>Loading...</div>}
{loading && <div className="p-2.5">Loading...</div>}
<iframe
ref={iframeRef}
id={uid}
name={uid}
title={uid}
src="https://next.curvenote.run"
src={
process.env.NODE_ENV === 'development' // TODO should this be in config.json?
? 'http://localhost:3003'
: 'https://next.curvenote.run'
}
width={'100%'}
height={height ? height + 25 : 0}
sandbox="allow-scripts"
Expand Down
15 changes: 13 additions & 2 deletions app/myst-to-react/output/output.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { MinifiedMimeOutput, MinifiedOutput } from '@curvenote/nbtx/dist/minify/
import classNames from 'classnames';
import { SafeOutputs } from './safe';
import { NativeJupyterOutputs as JupyterOutputs } from './jupyter';
import { OutputBlock } from './outputBlock';

const DIRECT_OUTPUT_TYPES = new Set(['stream', 'error']);
const DIRECT_OUTPUT_TYPES = new Set(['stream']);

const DIRECT_MIME_TYPES = new Set([
KnownCellOutputMimeTypes.TextPlain,
Expand All @@ -32,9 +33,17 @@ export function allOutputsAreSafe(
}, true);
}

export function anyErrors(outputs: MinifiedOutput[]) {
return outputs.reduce(
(flag, output) => flag || output.output_type === 'error',
false,
);
}

export function Output(node: GenericNode) {
const outputs: MinifiedOutput[] = node.data;
const allSafe = allOutputsAreSafe(outputs, DIRECT_OUTPUT_TYPES, DIRECT_MIME_TYPES);
const hasError = anyErrors(outputs);

let component;
if (allSafe) {
Expand All @@ -54,7 +63,9 @@ export function Output(node: GenericNode) {
'text-right': node.align === 'right',
})}
>
{component}
<OutputBlock allSafe={allSafe} hasError={hasError}>
{component}
</OutputBlock>
</figure>
);
}
24 changes: 24 additions & 0 deletions app/myst-to-react/output/outputBlock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import classNames from 'classnames';

type Props = {
children?: React.ReactNode;
allSafe?: boolean;
hasError?: boolean;
className?: string;
};

export function OutputBlock(props: Props) {
const { children, allSafe, className } = props;

return (
<div
suppressHydrationWarning={!allSafe}
className={classNames('relative group not-prose overflow-auto mb-8', className, {
'p-2.5': allSafe,
'pl-0.5': !allSafe,
})}
>
{children}
</div>
);
}
39 changes: 24 additions & 15 deletions app/myst-to-react/output/safe.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { KnownCellOutputMimeTypes } from '@curvenote/blocks/dist/blocks/types/jupyter';
import { ensureString } from '@curvenote/blocks/dist/helpers';
import {
import type {
MinifiedMimeBundle,
MinifiedMimePayload,
MinifiedOutput,
Expand Down Expand Up @@ -30,7 +30,10 @@ const RENDER_PRIORITY = [
KnownCellOutputMimeTypes.ImageBmp,
];

function OutputImage({ output }: { output: MinifiedOutput }) {
function findSafeMimeOutputs(output: MinifiedOutput): {
image?: MinifiedMimePayload;
text?: MinifiedMimePayload;
} {
const data: MinifiedMimeBundle = output.data as MinifiedMimeBundle;
const image = RENDER_PRIORITY.reduce(
(acc: MinifiedMimePayload | undefined, mimetype) => {
Expand All @@ -39,8 +42,17 @@ function OutputImage({ output }: { output: MinifiedOutput }) {
},
undefined,
);
if (!data || !image) return null;
const text = data['text/plain'];
const text = data && data['text/plain'];
return { image, text };
}

function OutputImage({
image,
text,
}: {
image: MinifiedMimePayload;
text?: MinifiedMimePayload;
}) {
return <img src={image?.path} alt={text?.content ?? 'Image produced in Jupyter'} />;
}

Expand All @@ -51,21 +63,18 @@ function SafeOutput({ output }: { output: MinifiedOutput }) {
<MaybeLongContent
content={ensureString(output.text)}
path={output.path}
render={(content?: string) => <pre>{content}</pre>}
/>
);
case 'error':
return (
<MaybeLongContent
content={ensureString(output.traceback)}
path={output.path}
render={(content?: string) => <pre>{content}</pre>}
render={(content?: string) => <div>{content}</div>}
/>
);
case 'display_data':
case 'execute_result':
case 'update_display_data':
return <OutputImage output={output} />;
case 'update_display_data': {
const { image, text } = findSafeMimeOutputs(output);
if (!image && !text) return null;
if (image) return <OutputImage image={image} text={text} />;
if (text) return <div>{text.content}</div>;
return null;
}
default:
console.warn(`Unknown output_type ${output['output_type']}`);
return null;
Expand Down
28 changes: 14 additions & 14 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"@curvenote/blocks": "^1.3.4",
"@curvenote/components": "^0.3.4",
"@curvenote/connect": "^0.0.3",
"@curvenote/nbtx": "^0.1.4",
"@curvenote/nbtx": "^0.1.6",
"@curvenote/runtime": "^0.2.9",
"@headlessui/react": "^1.5.0",
"@heroicons/react": "^1.0.6",
Expand Down

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

Large diffs are not rendered by default.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

Large diffs are not rendered by default.

0 comments on commit 2c7c507

Please sign in to comment.