Skip to content

Commit 0945c53

Browse files
committed
allows viewer output
1 parent 1c1fa99 commit 0945c53

File tree

2 files changed

+98
-8
lines changed

2 files changed

+98
-8
lines changed

src/inlineScripts.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import * as path from 'path';
2+
import {promises as fs} from "fs";
3+
4+
function getMimeType(ext: string): string {
5+
switch (ext) {
6+
case 'jpg':
7+
case 'jpeg':
8+
return 'image/jpeg';
9+
case 'svg':
10+
return 'image/svg+xml';
11+
case 'gif':
12+
case 'png':
13+
case 'webp':
14+
return `image/${ext}`;
15+
default:
16+
return 'application/octet-stream';
17+
}
18+
};
19+
20+
export async function inlineImages(html: string, htmlDir: string): Promise<string> {
21+
const imgTagRegex = /<img (.* )?src="([\w.\-\/]+)"(.*)>/;
22+
let matches = html.match(new RegExp(imgTagRegex, 'g'));
23+
if (!matches)
24+
return html;
25+
let imgPromises = matches
26+
.map(imgTag => imgTag.match(imgTagRegex)[2])
27+
.map(relImgPath => path.resolve(htmlDir, relImgPath))
28+
.map(imgPath => fs.readFile(imgPath));
29+
let i = 0;
30+
return Promise.all(imgPromises).then(images =>
31+
html.replace(new RegExp(imgTagRegex, 'g'), (_match, p1, p2, p3) =>
32+
`<img ${p1 || ''}src="data:${getMimeType(p2.split('.').pop())};base64, ${images[i++].toString('base64')}"${p3}>`
33+
));
34+
}
35+
36+
export async function inlineHtmlScripts(html: string, htmlDir: string): Promise<string> {
37+
const scriptTagRegex = /<script (?:.* )?src="([\w.\-\/]+)".*><\/script>/;
38+
let matches = html.match(new RegExp(scriptTagRegex, 'g'));
39+
if (!matches)
40+
return html;
41+
let scriptPromises = matches
42+
.map(scriptTag => scriptTag.match(scriptTagRegex)[1])
43+
.map(relScriptPath => path.resolve(htmlDir, relScriptPath))
44+
.map(scriptPath => fs.readFile(scriptPath, 'utf8'));
45+
let i = 0;
46+
return Promise.all(scriptPromises).then(scripts =>
47+
html.replace(new RegExp(scriptTagRegex, 'g'), () =>
48+
`<script>${scripts[i++].replace(/<\/script>/g, '<\\/script>')}</script>`));
49+
}
50+
51+
export async function inlineHtmlStyles(html: string, htmlDir: string): Promise<string> {
52+
const linkTagRegex = /<link (?:.* )?rel="stylesheet"(?:.* )?href="([\w.\-\/]+)".*>|<link (?:.* )?href="([\w.\-\/]+)"(?:.* )?rel="stylesheet".*>/;
53+
let matches = html.match(new RegExp(linkTagRegex, 'g'));
54+
if (!matches)
55+
return html;
56+
let stylesheetPromises = matches
57+
.map(linkTag => {
58+
let m = linkTag.match(linkTagRegex);
59+
return m[1] || m[2];
60+
})
61+
.map(relPath => path.resolve(htmlDir, relPath))
62+
.map(stylesheetPath => fs.readFile(stylesheetPath, 'utf8'));
63+
let i = 0;
64+
return Promise.all(stylesheetPromises).then(stylesheets =>
65+
html.replace(new RegExp(linkTagRegex, 'g'), () =>
66+
`<style>${stylesheets[i++]}</style>`));
67+
}
68+
69+
export async function inlineAll(html: string, htmlDir: string): Promise<string> {
70+
return await inlineHtmlStyles(
71+
await inlineImages(
72+
await inlineHtmlScripts(html, htmlDir),
73+
htmlDir),
74+
htmlDir)
75+
}

src/notebook.ts

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import * as vscode from 'vscode';
22
import net = require('net');
33
import { spawn, ChildProcess } from 'child_process';
44
import { dirname } from 'path';
5+
import { inlineAll } from './inlineScripts';
6+
57

68
interface RKernelRequest {
79
id: number;
@@ -336,6 +338,26 @@ export class RNotebookProvider implements vscode.NotebookContentProvider, vscode
336338
}
337339
}
338340

341+
async renderHtmlOutput(response) {
342+
343+
const html = (await vscode.workspace.fs.readFile(vscode.Uri.parse(response.result))).toString();
344+
const htmlDir = dirname(response.result)
345+
const htmlInline = await inlineAll(html, htmlDir)
346+
const htmlWrapped = `
347+
<iframe id="plotly" frameborder="0" sandbox="allow-scripts allow-forms allow-same-origin"></iframe>
348+
<script>
349+
var iframe = document.getElementById("plotly")
350+
iframe.srcdoc = unescape("${escape(htmlInline)}")
351+
</script>
352+
`
353+
return {
354+
outputKind: vscode.CellOutputKind.Rich,
355+
data: {
356+
'text/html': htmlWrapped
357+
},
358+
}
359+
}
360+
339361
async renderOutput(cell, response) {
340362

341363
switch (response.type) {
@@ -348,14 +370,7 @@ export class RNotebookProvider implements vscode.NotebookContentProvider, vscode
348370
break;
349371
}
350372
case 'viewer': {
351-
cell.outputs = [{
352-
outputKind: vscode.CellOutputKind.Rich,
353-
data: {
354-
// 'text/html': `<ifravscode-webview-resource://${response.result}`,
355-
'text/html': `<a href="vscode-webview-resource:${response.result}">Here</a>`,
356-
// <iframe src="vscode-webview-resource:${response.result}"></iframe>`,
357-
},
358-
}];
373+
cell.outputs = [await this.renderHtmlOutput(response)];
359374
break;
360375
}
361376
case 'browser': {

0 commit comments

Comments
 (0)