diff --git a/.vscode/launch.json b/.vscode/launch.json index 7ca47f985adc..ed880f572ffc 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -14,7 +14,8 @@ "smartStep": true, "sourceMaps": true, "outFiles": [ - "${workspaceFolder}/out/**/*" + "${workspaceFolder}/out/**/*", + "!${workspaceFolder}/**/node_modules**/*" ], "preLaunchTask": "Compile", "skipFiles": [ @@ -43,7 +44,8 @@ "smartStep": true, "sourceMaps": true, "outFiles": [ - "${workspaceFolder}/out/**/*" + "${workspaceFolder}/out/**/*", + "!${workspaceFolder}/**/node_modules**/*" ], "preLaunchTask": "Inject DS WebBrowser UI", "env": { @@ -66,7 +68,8 @@ "smartStep": true, "sourceMaps": true, "outFiles": [ - "${workspaceFolder}/out/**/*" + "${workspaceFolder}/out/**/*", + "!${workspaceFolder}/**/node_modules**/*" ], "preLaunchTask": "Compile" }, @@ -99,7 +102,8 @@ ], "sourceMaps": true, "outFiles": [ - "${workspaceFolder}/out/client/**/*.js" + "${workspaceFolder}/out/client/**/*.js", + "!${workspaceFolder}/**/node_modules**/*" ], "cwd": "${workspaceFolder}", "preLaunchTask": "Compile", @@ -122,7 +126,8 @@ "sourceMaps": true, "smartStep": true, "outFiles": [ - "${workspaceFolder}/out/**/*" + "${workspaceFolder}/out/**/*", + "!${workspaceFolder}/**/node_modules**/*" ], "preLaunchTask": "Compile", "env": { @@ -151,7 +156,8 @@ "stopOnEntry": false, "sourceMaps": true, "outFiles": [ - "${workspaceFolder}/out/**/*.js" + "${workspaceFolder}/out/**/*.js", + "!${workspaceFolder}/**/node_modules**/*" ], "preLaunchTask": "Compile", "skipFiles": [ @@ -175,7 +181,8 @@ "stopOnEntry": false, "sourceMaps": true, "outFiles": [ - "${workspaceFolder}/out/**/*.js" + "${workspaceFolder}/out/**/*.js", + "!${workspaceFolder}/**/node_modules**/*" ], "preLaunchTask": "Compile", "skipFiles": [ @@ -202,7 +209,8 @@ "stopOnEntry": false, "sourceMaps": true, "outFiles": [ - "${workspaceFolder}/out/**/*.js" + "${workspaceFolder}/out/**/*.js", + "!${workspaceFolder}/**/node_modules**/*" ], "preLaunchTask": "Compile", "skipFiles": [ @@ -224,7 +232,8 @@ "sourceMaps": true, "smartStep": true, "outFiles": [ - "${workspaceFolder}/out/**/*" + "${workspaceFolder}/out/**/*", + "!${workspaceFolder}/**/node_modules**/*" ], "preLaunchTask": "Compile", "skipFiles": [ @@ -248,7 +257,8 @@ "--timeout=300000" ], "outFiles": [ - "${workspaceFolder}/out/**/*.js" + "${workspaceFolder}/out/**/*.js", + "!${workspaceFolder}/**/node_modules**/*" ], "preLaunchTask": "Compile", "skipFiles": [ @@ -273,7 +283,8 @@ "--fast" ], "outFiles": [ - "${workspaceFolder}/out/**/*.js" + "${workspaceFolder}/out/**/*.js", + "!${workspaceFolder}/**/node_modules**/*" ], "preLaunchTask": "Compile", "skipFiles": [ @@ -309,7 +320,8 @@ "XDEBUGPY_LOG_DIR": "${workspaceRoot}/tmp/Debug_Output" }, "outFiles": [ - "${workspaceFolder}/out/**/*.js" + "${workspaceFolder}/out/**/*.js", + "!${workspaceFolder}/**/node_modules**/*" ], "preLaunchTask": "Compile", "skipFiles": [ @@ -344,7 +356,8 @@ "XCI_PYTHON_PATH": "" }, "outFiles": [ - "${workspaceFolder}/out/**/*.js" + "${workspaceFolder}/out/**/*.js", + "!${workspaceFolder}/**/node_modules**/*" ], "preLaunchTask": "Compile", "skipFiles": [ diff --git a/build/webpack/webpack.datascience-ui-renderers.config.js b/build/webpack/webpack.datascience-ui-renderers.config.js new file mode 100644 index 000000000000..022a483c113a --- /dev/null +++ b/build/webpack/webpack.datascience-ui-renderers.config.js @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +const builder = require('./webpack.datascience-ui.config.builder'); +module.exports = [builder.renderers]; diff --git a/build/webpack/webpack.datascience-ui.config.builder.js b/build/webpack/webpack.datascience-ui.config.builder.js index 8e1367f14978..69fbb551cc50 100644 --- a/build/webpack/webpack.datascience-ui.config.builder.js +++ b/build/webpack/webpack.datascience-ui.config.builder.js @@ -20,21 +20,28 @@ const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); // Any build on the CI is considered production mode. const isProdBuild = constants.isCI || process.argv.includes('--mode'); -function getEntry(isNotebook) { - if (isNotebook) { - return { - nativeEditor: ['babel-polyfill', `./src/datascience-ui/native-editor/index.tsx`], - interactiveWindow: ['babel-polyfill', `./src/datascience-ui/history-react/index.tsx`] - }; +function getEntry(bundle) { + switch (bundle) { + case 'notebook': + return { + nativeEditor: ['babel-polyfill', `./src/datascience-ui/native-editor/index.tsx`], + interactiveWindow: ['babel-polyfill', `./src/datascience-ui/history-react/index.tsx`] + }; + case 'renderers': + return { + renderers: ['babel-polyfill', `./src/datascience-ui/renderers/index.tsx`] + }; + case 'viewers': + return { + plotViewer: ['babel-polyfill', `./src/datascience-ui/plot/index.tsx`], + dataExplorer: ['babel-polyfill', `./src/datascience-ui/data-explorer/index.tsx`] + }; + default: + throw new Error(`Bundle not supported ${bundle}`); } - - return { - plotViewer: ['babel-polyfill', `./src/datascience-ui/plot/index.tsx`], - dataExplorer: ['babel-polyfill', `./src/datascience-ui/data-explorer/index.tsx`] - }; } -function getPlugins(isNotebook) { +function getPlugins(bundle) { const plugins = [ new ForkTsCheckerWebpackPlugin({ checkSyntacticErrors: true, @@ -44,64 +51,79 @@ function getPlugins(isNotebook) { }) ]; if (isProdBuild) { - plugins.push(...common.getDefaultPlugins(isNotebook ? 'notebook' : 'viewers')); + plugins.push(...common.getDefaultPlugins(bundle)); } - - if (isNotebook) { - plugins.push( - new MonacoWebpackPlugin({ - languages: [] // force to empty so onigasm will be used - }), - new HtmlWebpackPlugin({ - template: path.join(__dirname, '/nativeOrInteractivePicker.html'), - chunks: [], - filename: 'index.html' - }), - new HtmlWebpackPlugin({ - template: 'src/datascience-ui/native-editor/index.html', - chunks: ['monaco', 'commons', 'nativeEditor'], - filename: 'index.nativeEditor.html' - }), - new HtmlWebpackPlugin({ - template: 'src/datascience-ui/history-react/index.html', - chunks: ['monaco', 'commons', 'interactiveWindow'], - filename: 'index.interactiveWindow.html' - }) - ); - } else { - const definePlugin = new webpack.DefinePlugin({ - 'process.env': { - NODE_ENV: JSON.stringify('production') - } - }); - - plugins.push( - ...(isProdBuild ? [definePlugin] : []), - ...[ + switch (bundle) { + case 'notebook': + plugins.push( + new MonacoWebpackPlugin({ + languages: [] // force to empty so onigasm will be used + }), + new HtmlWebpackPlugin({ + template: path.join(__dirname, '/nativeOrInteractivePicker.html'), + chunks: [], + filename: 'index.html' + }), new HtmlWebpackPlugin({ - template: 'src/datascience-ui/plot/index.html', - indexUrl: `${constants.ExtensionRootDir}/out/1`, - chunks: ['commons', 'plotViewer'], - filename: 'index.plotViewer.html' + template: 'src/datascience-ui/native-editor/index.html', + chunks: ['monaco', 'commons', 'nativeEditor'], + filename: 'index.nativeEditor.html' }), new HtmlWebpackPlugin({ - template: 'src/datascience-ui/data-explorer/index.html', - indexUrl: `${constants.ExtensionRootDir}/out/1`, - chunks: ['commons', 'dataExplorer'], - filename: 'index.dataExplorer.html' + template: 'src/datascience-ui/history-react/index.html', + chunks: ['monaco', 'commons', 'interactiveWindow'], + filename: 'index.interactiveWindow.html' }) - ] - ); + ); + break; + case 'renderers': { + const definePlugin = new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: JSON.stringify('production') + } + }); + + plugins.push(...(isProdBuild ? [definePlugin] : [])); + break; + } + case 'viewers': { + const definePlugin = new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: JSON.stringify('production') + } + }); + + plugins.push( + ...(isProdBuild ? [definePlugin] : []), + ...[ + new HtmlWebpackPlugin({ + template: 'src/datascience-ui/plot/index.html', + indexUrl: `${constants.ExtensionRootDir}/out/1`, + chunks: ['commons', 'plotViewer'], + filename: 'index.plotViewer.html' + }), + new HtmlWebpackPlugin({ + template: 'src/datascience-ui/data-explorer/index.html', + indexUrl: `${constants.ExtensionRootDir}/out/1`, + chunks: ['commons', 'dataExplorer'], + filename: 'index.dataExplorer.html' + }) + ] + ); + break; + } + default: + throw new Error(`Bundle not supported ${bundle}`); } return plugins; } -function buildConfiguration(isNotebook) { +function buildConfiguration(bundle) { // Folder inside `datascience-ui` that will be created and where the files will be dumped. - const bundleFolder = isNotebook ? 'notebook' : 'viewers'; + const bundleFolder = bundle; const filesToCopy = []; - if (isNotebook) { + if (bundle === 'notebook') { // Include files only for notebooks. filesToCopy.push( ...[ @@ -116,9 +138,9 @@ function buildConfiguration(isNotebook) { ] ); } - return { + const config = { context: constants.ExtensionRootDir, - entry: getEntry(isNotebook), + entry: getEntry(bundle), output: { path: path.join(constants.ExtensionRootDir, 'out', 'datascience-ui', bundleFolder), filename: '[name].js', @@ -140,7 +162,7 @@ function buildConfiguration(isNotebook) { commons: { name: 'commons', chunks: 'initial', - minChunks: isNotebook ? 2 : 1, // We want at least one shared bundle (2 for notebooks, as we want monago split into another). + minChunks: bundle === 'notebook' ? 2 : 1, // We want at least one shared bundle (2 for notebooks, as we want monago split into another). filename: '[name].initial.bundle.js' }, // Even though nteract has been split up, some of them are large as nteract alone is large. @@ -218,7 +240,7 @@ function buildConfiguration(isNotebook) { new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 100 }), - ...getPlugins(isNotebook) + ...getPlugins(bundle) ], externals: ['log4js'], resolve: { @@ -298,7 +320,13 @@ function buildConfiguration(isNotebook) { ] } }; + + if (bundle === 'renderers') { + delete config.optimization; + } + return config; } -exports.notebooks = buildConfiguration(true); -exports.viewers = buildConfiguration(false); +exports.notebooks = buildConfiguration('notebook'); +exports.viewers = buildConfiguration('viewers'); +exports.renderers = buildConfiguration('renderers'); diff --git a/gulpfile.js b/gulpfile.js index c1758cee7cfb..8e8151bc36db 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -143,6 +143,10 @@ gulp.task('compile-notebooks', async () => { await buildWebPackForDevOrProduction('./build/webpack/webpack.datascience-ui-notebooks.config.js'); }); +gulp.task('compile-renderers', async () => { + await buildWebPackForDevOrProduction('./build/webpack/webpack.datascience-ui-renderers.config.js'); +}); + gulp.task('compile-viewers', async () => { await buildWebPackForDevOrProduction('./build/webpack/webpack.datascience-ui-viewers.config.js'); }); diff --git a/src/client/datascience/notebook/helpers.ts b/src/client/datascience/notebook/helpers.ts index a9a5d158f778..ec70a8d98109 100644 --- a/src/client/datascience/notebook/helpers.ts +++ b/src/client/datascience/notebook/helpers.ts @@ -48,12 +48,13 @@ export function notebookModelToVSCNotebookData(model: INotebookModel): NotebookD 'application/x-nteract-model-debug+json', 'text/html', 'application/javascript', + 'image/gif', 'text/latex', 'text/markdown', - 'application/json', 'image/svg+xml', 'image/png', 'image/jpeg', + 'application/json', 'text/plain' ] } diff --git a/src/client/datascience/notebook/integration.ts b/src/client/datascience/notebook/integration.ts index a2b2d31ffe9c..a8f064e7e0f3 100644 --- a/src/client/datascience/notebook/integration.ts +++ b/src/client/datascience/notebook/integration.ts @@ -11,6 +11,7 @@ import { IDisposableRegistry, IExperimentsManager, IExtensionContext } from '../ import { noop } from '../../common/utils/misc'; import { NotebookContentProvider } from './contentProvider'; import { NotebookKernel } from './notebookKernel'; +import { NotebookOutputRenderer } from './renderer'; /** * This class basically registers the necessary providers and the like with VSC. @@ -27,7 +28,8 @@ export class NotebookIntegration implements IExtensionSingleActivationService { @inject(IExtensionContext) private readonly context: IExtensionContext, @inject(IFileSystem) private readonly fs: IFileSystem, @inject(ICommandManager) private readonly commandManager: ICommandManager, - @inject(NotebookKernel) private readonly notebookKernel: NotebookKernel + @inject(NotebookKernel) private readonly notebookKernel: NotebookKernel, + @inject(NotebookOutputRenderer) private readonly renderer: NotebookOutputRenderer ) {} public async activate(): Promise { // This condition is temporary. @@ -51,7 +53,24 @@ export class NotebookIntegration implements IExtensionSingleActivationService { { viewType: 'jupyter-notebook-renderer', displayName: 'Jupyter Notebook Renderer', - mimeTypes: ['text/latex', 'application/vnd.plotly.v1+json', 'application/vnd.vega.v5+json'] + mimeTypes: [ + 'application/geo+json', + 'application/vdom.v1+json', + 'application/vnd.dataresource+json', + 'application/vnd.plotly.v1+json', + 'application/vnd.vega.v2+json', + 'application/vnd.vega.v3+json', + 'application/vnd.vega.v4+json', + 'application/vnd.vega.v5+json', + 'application/vnd.vegalite.v1+json', + 'application/vnd.vegalite.v2+json', + 'application/vnd.vegalite.v3+json', + 'application/vnd.vegalite.v4+json', + 'application/x-nteract-model-debug+json', + 'image/gif', + 'text/latex', + 'text/vnd.plotly.v1+html' + ] } ]; content.contributes.notebookProvider = [ @@ -78,5 +97,32 @@ export class NotebookIntegration implements IExtensionSingleActivationService { this.disposables.push( this.vscNotebook.registerNotebookKernel('jupyter-notebook', ['**/*.ipynb'], this.notebookKernel) ); + this.disposables.push( + this.vscNotebook.registerNotebookOutputRenderer( + 'jupyter-notebook-renderer', + { + type: 'display_data', + subTypes: [ + 'application/geo+json', + 'application/vdom.v1+json', + 'application/vnd.dataresource+json', + 'application/vnd.plotly.v1+json', + 'application/vnd.vega.v2+json', + 'application/vnd.vega.v3+json', + 'application/vnd.vega.v4+json', + 'application/vnd.vega.v5+json', + 'application/vnd.vegalite.v1+json', + 'application/vnd.vegalite.v2+json', + 'application/vnd.vegalite.v3+json', + 'application/vnd.vegalite.v4+json', + 'application/x-nteract-model-debug+json', + 'image/gif', + 'text/latex', + 'text/vnd.plotly.v1+html' + ] + }, + this.renderer + ) + ); } } diff --git a/src/client/datascience/notebook/renderer.ts b/src/client/datascience/notebook/renderer.ts new file mode 100644 index 000000000000..46294018d56b --- /dev/null +++ b/src/client/datascience/notebook/renderer.ts @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { injectable } from 'inversify'; +import * as path from 'path'; +import * as uuid from 'uuid/v4'; +import { CellOutput, CellOutputKind, NotebookOutputRenderer as VSCNotebookOutputRenderer, Uri } from 'vscode'; +import { EXTENSION_ROOT_DIR } from '../../constants'; + +@injectable() +export class NotebookOutputRenderer implements VSCNotebookOutputRenderer { + get preloads(): Uri[] { + return this._preloads; + } + private _preloads: Uri[] = []; + constructor() { + const renderersFolder = path.join(EXTENSION_ROOT_DIR, 'out', 'datascience-ui', 'renderers'); + this._preloads.push(Uri.file(path.join(renderersFolder, 'pvscDummy.js'))); + this._preloads.push(Uri.file(path.join(renderersFolder, 'main.js'))); + this._preloads.push(Uri.file(path.join(renderersFolder, 'renderers.js'))); + } + + // @ts-ignore + public render(document: NotebookDocument, output: CellOutput, mimeType: string) { + let outputToSend = output; + if (output.outputKind === CellOutputKind.Rich && mimeType in output.data) { + outputToSend = { + ...output, + // Send only what we need & ignore other mimetypes. + data: { + [mimeType]: output.data[mimeType] + } + }; + } + const id = uuid(); + return ` + + + `; + } +} diff --git a/src/client/datascience/notebook/serviceRegistry.ts b/src/client/datascience/notebook/serviceRegistry.ts index 0681c05c7ac1..84d367ef1896 100644 --- a/src/client/datascience/notebook/serviceRegistry.ts +++ b/src/client/datascience/notebook/serviceRegistry.ts @@ -10,6 +10,7 @@ import { NotebookExecutionService } from './executionService'; import { NotebookIntegration } from './integration'; import { NotebookEditorProviderActivation } from './notebookEditorProvider'; import { NotebookKernel } from './notebookKernel'; +import { NotebookOutputRenderer } from './renderer'; import { INotebookExecutionService } from './types'; export function registerTypes(serviceManager: IServiceManager) { @@ -20,6 +21,7 @@ export function registerTypes(serviceManager: IServiceManager) { NotebookIntegration ); serviceManager.addSingleton(NotebookKernel, NotebookKernel); + serviceManager.addSingleton(NotebookOutputRenderer, NotebookOutputRenderer); serviceManager.addSingleton( IExtensionSingleActivationService, NotebookEditorProviderActivation diff --git a/src/datascience-ui/interactive-common/transforms.tsx b/src/datascience-ui/interactive-common/transforms.tsx index 18725852df42..d9ac734a6e72 100644 --- a/src/datascience-ui/interactive-common/transforms.tsx +++ b/src/datascience-ui/interactive-common/transforms.tsx @@ -45,6 +45,10 @@ const mimeTypeToImport: TransformData[] = [ const module = await import(/* webpackChunkName: "vega" */ '@nteract/transform-vega'); return module.VegaLite3; }), + new TransformData('application/vnd.vegalite.v4+json', async () => { + const module = await import(/* webpackChunkName: "vega" */ '@nteract/transform-vega'); + return module.VegaLite3; + }), new TransformData('application/geo+json', async () => { const module = await import(/* webpackChunkName: "geojson" */ '@nteract/transform-geojson'); return module.GeoJSONTransform; diff --git a/src/datascience-ui/renderers/index.tsx b/src/datascience-ui/renderers/index.tsx new file mode 100644 index 000000000000..094281cc0d38 --- /dev/null +++ b/src/datascience-ui/renderers/index.tsx @@ -0,0 +1,94 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +// This must be on top, do not change. Required by webpack. +// tslint:disable-next-line: no-var-requires no-require-imports +// require('../common/main'); +declare let __webpack_public_path__: string; + +// tslint:disable-next-line: no-any +if ((window as any).__PVSC_Public_Path) { + // This variable tells Webpack to this as the root path used to request webpack bundles. + // tslint:disable-next-line: no-any + __webpack_public_path__ = (window as any).__PVSC_Public_Path; +} +// This must be on top, do not change. Required by webpack. + +import type { nbformat } from '@jupyterlab/coreutils'; +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import '../../client/common/extensions'; +import { InteractiveWindowMessages } from '../../client/datascience/interactive-common/interactiveWindowTypes'; +import { handleLinkClick } from '../interactive-common/handlers'; +import type { IVsCodeApi } from '../react-common/postOffice'; +import { CellOutput } from './render'; +export declare function acquireVsCodeApi(): IVsCodeApi; + +/** + * Called from renderer to render output. + * This will be exposed as a public method on window for renderer to render output. + */ +function renderOutput(tag: HTMLScriptElement) { + let container: HTMLElement; + const mimeType = tag.dataset.mimeType as string; + try { + const output = JSON.parse(tag.innerHTML) as nbformat.IExecuteResult | nbformat.IDisplayData; + // tslint:disable-next-line: no-console + console.log(`Rendering mimeType ${mimeType}`); + + // Create an element to render in, or reuse a previous element. + if (tag.nextElementSibling instanceof HTMLDivElement) { + container = tag.nextElementSibling; + // tslint:disable-next-line: no-inner-html + container.innerHTML = ''; + } else { + container = document.createElement('div'); + tag.parentNode?.insertBefore(container, tag.nextSibling); // NOSONAR + } + tag.parentElement?.removeChild(tag); // NOSONAR + + ReactDOM.render(React.createElement(CellOutput, { mimeType, output }, null), container); + } catch (ex) { + // tslint:disable-next-line: no-console + console.error(`Failed to render mime type ${mimeType}`, ex); + } +} + +/** + * Possible the pre-render scripts load late, after we have attempted to render output from notebook. + * At this point look through all such scripts and render the output. + */ +function renderOnLoad() { + document + .querySelectorAll('script[type="application/vscode-jupyter+json"]') + .forEach(renderOutput); +} + +// tslint:disable-next-line: no-any +function postToExtension(type: string, payload: any) { + acquireVsCodeApi().postMessage({ type, payload }); // NOSONAR +} +function linkHandler(href: string) { + if (href.startsWith('data:image/png')) { + postToExtension(InteractiveWindowMessages.SavePng, href); + } else { + postToExtension(InteractiveWindowMessages.OpenLink, href); + } +} + +// tslint:disable-next-line: no-any +function initialize(global: Record) { + // Expose necessary hooks for client renderer to render output. + // tslint:disable-next-line: no-any + Object.assign(global, { 'vscode-jupyter': { renderOutput } }); + document.addEventListener('click', (e) => handleLinkClick(e, linkHandler), true); + // Possible this (pre-render script loaded after notebook attempted to render something). + // At this point we need to go and render the existing output. + renderOnLoad(); +} + +// tslint:disable-next-line: no-console +console.log('Pre-Render scripts loaded'); +initialize(window); diff --git a/src/datascience-ui/renderers/main.ts b/src/datascience-ui/renderers/main.ts new file mode 100644 index 000000000000..df70985ff708 --- /dev/null +++ b/src/datascience-ui/renderers/main.ts @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// Temporary work around for https://github.com/microsoft/vscode/issues/98106 +// tslint:disable + +const scriptSrcs = Array.from(document.querySelectorAll('script')) + .map((item) => item.attributes.getNamedItem('src')) + .filter((item) => (item?.value || '').endsWith('pvscDummy.js')) + .map((item) => item?.value) + .filter((item) => !!item); + +if (scriptSrcs.length) { + try { + const src = scriptSrcs[0]!; + const paths = src.split('/'); + // Remove file name portion from path. + paths.pop(); + Object.assign(window, { __PVSC_Public_Path: `${paths.join('/')}/` }); + console.log(`window.__PVSC_Public_Path = ${(window as any).__PVSC_Public_Path}`); + } catch (ex) { + console.error('Unable to initialize window.__PVSC_Public_Path', ex); + } +} else { + console.error('Unable to initialize window.__PVSC_Public_Path'); +} diff --git a/src/datascience-ui/renderers/pvscDummy.ts b/src/datascience-ui/renderers/pvscDummy.ts new file mode 100644 index 000000000000..b0e3b7e17aa1 --- /dev/null +++ b/src/datascience-ui/renderers/pvscDummy.ts @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// Temporary work around for https://github.com/microsoft/vscode/issues/98106 +// tslint:disable-next-line: no-console +console.log('pvscDummy loaded for renderer'); diff --git a/src/datascience-ui/renderers/render.tsx b/src/datascience-ui/renderers/render.tsx new file mode 100644 index 000000000000..644f581483ce --- /dev/null +++ b/src/datascience-ui/renderers/render.tsx @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import type { nbformat } from '@jupyterlab/coreutils'; +import type { JSONObject } from '@phosphor/coreutils'; +import * as React from 'react'; +import { concatMultilineStringOutput } from '../common'; +import { fixLatexEquations } from '../interactive-common/latexManipulation'; +import { getTransform } from '../interactive-common/transforms'; + +export interface ICellOutputProps { + output: nbformat.IOutput; + mimeType: string; +} + +export class CellOutput extends React.Component { + // tslint:disable-next-line: no-any + constructor(prop: ICellOutputProps) { + super(prop); + } + public render() { + const mimeBundle = this.props.output.data as nbformat.IMimeBundle; // NOSONAR + let data: nbformat.MultilineString | JSONObject = mimeBundle[this.props.mimeType!]; + + // Fixup latex to make sure it has the requisite $$ around it + if (this.props.mimeType! === 'text/latex') { + data = fixLatexEquations(concatMultilineStringOutput(data as nbformat.MultilineString), true); + } + + const Transform = getTransform(this.props.mimeType!); + return ( +
+ +
+ ); + } +}