Update formatting for wasm notebook

This commit is contained in:
Noah committed 2020-12-07 12:56:32 -06:00
1 parent 4e5dc3e4f1
commit bdcdcb1917
10 files changed
+309 -302

No files matched your search

+29 -28
View File
@@ -1,42 +1,43 @@
/* CSS */
@import url('https://fonts.googleapis.com/css2?family=Turret+Road:wght@700&display=swap');
h1 { font-family: 'Sen', 'sans-serif'; }
h2 , h3, h4, button {
font-family: 'Turret Road', cursive;
padding-top: 1.25rem;
}
@import url('https://fonts.googleapis.com/css2?family=Turret+Road:wght@700&display=swap');
h1 {
font-size: 1.5rem;
font-family: 'Sen', 'sans-serif';
}
h2, h3, h4, button {
font-family: 'Turret Road', cursive;
padding-top: 1.25rem;
}
h1 {
font-size: 1.5rem;
}
input[type='text'] {
border: 1px solid black;
width: 300px;
height: 25px;
padding: 5px;
font-size: 1.2rem;
font-family: 'Turret Road', cursive;
border: 1px solid black;
width: 300px;
height: 25px;
padding: 5px;
font-size: 1.2rem;
font-family: 'Turret Road', cursive;
}
hr {
border: 0px;
border-bottom: 1px solid black;
width: 50%;
margin-top: 1.5rem;
margin-bottom: 1.5rem;
}
border: 0px;
border-bottom: 1px solid black;
width: 50%;
margin-top: 1.5rem;
margin-bottom: 1.5rem;
}
button {
background-color: black;
color: white;
border: 0px;
font-size: 1.2rem;
height: 30px;
display: block;
margin-top: 10px;
background-color: black;
color: white;
border: 0px;
font-size: 1.2rem;
height: 30px;
display: block;
margin-top: 10px;
}
@@ -2,44 +2,39 @@
// Javascript code go here
function create_ui() {
function create_ui() {
const input = document.createElement('input');
input.type = 'text';
input.id = 'input-1';
document.getElementById('rp-notebook').appendChild(input);
const input = document.createElement("input");
input.type = "text";
input.id = "input-1";
document.getElementById("rp-notebook").appendChild(input);
const btn = document.createElement('button');
btn.type = 'button';
btn.id = 'btn-caclulate';
btn.innerHTML = 'click me';
btn.addEventListener('click', calculate, false);
document.getElementById('rp-notebook').appendChild(btn);
const btn = document.createElement("button");
btn.type = "button";
btn.id = "btn-caclulate";
btn.innerHTML = "click me";
btn.addEventListener('click', calculate, false);
document.getElementById("rp-notebook").appendChild(btn);
const div = document.createElement('div');
div.id = 'result';
div.classList.add('result');
document.getElementById('rp-notebook').appendChild(div);
}
const div = document.createElement("div");
div.id = "result";
div.classList.add("result");
document.getElementById("rp-notebook").appendChild(div);
}
function calculate() {
let target = document.getElementById('result');
target.innerHTML = '';
function calculate() {
let target = document.getElementById("result");
target.innerHTML = "";
let code = 'runModel()';
let input = document.getElementById("input-1").value;
let params = { "data": input };
rp.pyExec(code, {
stdout: (output) => {
target.innerHTML += output;
},
vars: params
});
}
let code = 'runModel()';
let input = document.getElementById('input-1').value;
let params = { data: input };
rp.pyExec(code, {
stdout: (output) => {
target.innerHTML += output;
},
vars: params,
});
}
@@ -3,7 +3,9 @@
# RustPython Notebook: python, javascript, css, markdown and math
Python in the browser is fun. Python and Javascript working together is double the fun 😜. Markdown and math are good for communicating ideas 🤔 (especially scientific ones). Adding css to the mix, makes everything look pretty ✨.
Python in the browser is fun. Python and Javascript working together is double
the fun 😜. Markdown and math are good for communicating ideas 🤔 (especially
scientific ones). Adding css to the mix, makes everything look pretty ✨.
---
@@ -34,9 +36,9 @@ create_ui();
# Python code
def runModel():
input = js_vars['data']
print ("<br>")
print("A little javascript birdie told the python snake your lucky number 🙊")
print ("Now the everyone knows that it is: " + str(input))
input = js_vars['data']
print ("<br>")
print("A little javascript birdie told the python snake your lucky number 🙊")
print ("Now the everyone knows that it is: " + str(input))
+19 -21
View File
@@ -2,7 +2,9 @@
# RustPython Notebook: python, javascript, css, markdown and math
Python in the browser is fun 🤘. Python and Javascript working together is double the fun 😜. Markdown and math are good for communicating ideas 💡(especially scientific ones 🧠). Adding css to the mix, makes everything look pretty ✨.
Python in the browser is fun 🤘. Python and Javascript working together is double
the fun 😜. Markdown and math are good for communicating ideas 💡(especially
scientific ones 🧠). Adding css to the mix, makes everything look pretty ✨.
# Main
@@ -13,7 +15,8 @@ In the notebook, you can write markdown, math, javascript and python.
H(t) \xrightarrow{write} \Big[A(t+1),\ H(t+1)\Big]
%%md
It even highlights properly. You can multi-task with split view mode and create python documents as you need to. In main, use `%%` then:
It even highlights properly. You can multi-task with split view mode and create
python documents as you need to. In main, use `%%` then:
- `%%md` for markdown
- `%%py` for python
- `%%js` for javascript
@@ -40,10 +43,10 @@ Here is a run down of what is happening with this demo. The notebook:
# Python code
def runModel():
input = js_vars['data']
print ("<br>")
print("A little javascript birdie told the python snake your lucky number 🙊")
print ("Now the everyone knows that it is: " + str(input))
input = js_vars['data']
print ("<br>")
print("A little javascript birdie told the python snake your lucky number 🙊")
print ("Now the everyone knows that it is: " + str(input))
%%md
## Calculator
@@ -86,12 +89,12 @@ function calculate() {
let params = { "data": input };
rp.pyExec(code, {
stdout: (output) => {
target.innerHTML += output;
},
vars: params
});
rp.pyExec(code, {
stdout: (output) => {
target.innerHTML += output;
},
vars: params
});
}
@@ -102,12 +105,7 @@ create_ui();
# Python code
def runModel():
input = js_vars['data']
print ("<br>")
print("A little javascript birdie told the python snake your lucky number 🙊")
print ("Now the everyone knows that it is: " + str(input))
input = js_vars['data']
print ("<br>")
print("A little javascript birdie told the python snake your lucky number 🙊")
print ("Now the everyone knows that it is: " + str(input))
@@ -19,7 +19,8 @@ In the notebook, you can write markdown, math and python. use %% then:
- math-inline for an inline math block
I **can** bold things and _italicize_ them. You got the gist, this is standard markdown syntax that you can [google](google.com).
I **can** bold things and _italicize_ them. You got the gist, this is standard
markdown syntax that you can [google](google.com).
<br>
+17 -15
View File
@@ -48,44 +48,46 @@ CodeMirror.defineMode('notebook', function (config, _parserConfig) {
CodeMirror.defineMIME('text/x-notebook', 'notebook');
function selectBuffer(editor, buffers, name) {
var buf = buffers[name];
if (buf.getEditor()) buf = buf.linkedDoc({sharedHist: true});
if (buf.getEditor()) buf = buf.linkedDoc({ sharedHist: true });
var old = editor.swapDoc(buf);
var linked = old.iterLinkedDocs(function(doc) {linked = doc;});
var linked = old.iterLinkedDocs(function (doc) {
linked = doc;
});
if (linked) {
// Make sure the document in buffers is the one the other view is looking at
for (var name in buffers) if (buffers[name] == old) buffers[name] = linked;
old.unlinkDoc(linked);
// Make sure the document in buffers is the one the other view is looking at
for (var name in buffers)
if (buffers[name] == old) buffers[name] = linked;
old.unlinkDoc(linked);
}
editor.focus();
// console.log(editor.getValue());
}
}
function openBuffer(buffers, name, text, mode , buffersDropDown , buffersList) {
function openBuffer(buffers, name, text, mode, buffersDropDown, buffersList) {
buffers[name] = CodeMirror.Doc(text, mode);
let opt = document.createElement("option");
let opt = document.createElement('option');
opt.appendChild(document.createTextNode(name));
buffersDropDown.appendChild(opt);
let li = document.createElement("li");
let li = document.createElement('li');
li.appendChild(document.createTextNode(name));
li.dataset.language = name;
buffersList.appendChild(li);
}
}
function newBuf(buffers, buffersDropDown, buffersList, primaryEditor) {
let name = prompt("Name your tab", "*scratch*");
let name = prompt('Name your tab', '*scratch*');
if (name == null) return;
if (buffers.hasOwnProperty(name)) {
alert("There's already a buffer by that name.");
return;
}
openBuffer(buffers, name, "", "python", buffersDropDown, buffersList);
openBuffer(buffers, name, '', 'python', buffersDropDown, buffersList);
selectBuffer(primaryEditor, buffers, name);
let sel = buffersDropDown;
sel.value = name;
}
export { selectBuffer , openBuffer , newBuf }
export { selectBuffer, openBuffer, newBuf };
+169 -159
View File
@@ -1,5 +1,5 @@
import './style.css';
// Code Mirror
// Code Mirror
// https://github.com/codemirror/codemirror
import CodeMirror from 'codemirror';
import 'codemirror/mode/python/python';
@@ -11,45 +11,38 @@ import 'codemirror/addon/comment/comment';
import 'codemirror/lib/codemirror.css';
import 'codemirror/theme/ayu-mirage.css';
import {
selectBuffer,
openBuffer,
newBuf
} from './editor';
import { selectBuffer, openBuffer, newBuf } from './editor';
import { genericFetch } from './tools'
import { genericFetch } from './tools';
// parsing: copied from the iodide project
// https://github.com/iodide-project/iodide/blob/master/src/editor/iomd-tools/iomd-parser.js
import {
iomdParser
} from './parse';
import { iomdParser } from './parse';
// processing: execute/render editor's content
import {
runPython,
runJS,
runPython,
runJS,
addCSS,
checkCssStatus,
renderMarkdown,
renderMath
renderMarkdown,
renderMath,
} from './process';
let rp;
// A dependency graph that contains any wasm must be imported asynchronously.
import ('rustpython')
.then((rustpy) => {
rp = rustpy;
// so people can play around with it
window.rp = rustpy;
onReady();
})
.catch((e) => {
console.error('Error importing `rustpython`:', e);
document.getElementById('error')
.textContent = e;
});
import('rustpython')
.then((rustpy) => {
rp = rustpy;
// so people can play around with it
window.rp = rustpy;
onReady();
})
.catch((e) => {
console.error('Error importing `rustpython`:', e);
document.getElementById('error').textContent = e;
});
const error = document.getElementById('error');
const notebook = document.getElementById('rp-notebook');
@@ -64,15 +57,18 @@ const notebook = document.getElementById('rp-notebook');
// all new documents are python docs
// adapted/inspired from https://codemirror.net/demo/buffers.html
const primaryEditor = CodeMirror(document.getElementById('primary-editor'), {
theme: 'ayu-mirage',
lineNumbers: true,
theme: 'ayu-mirage',
lineNumbers: true,
lineWrapping: true,
});
const secondaryEditor = CodeMirror(document.getElementById('secondary-editor'), {
lineNumbers: true,
lineWrapping: true
});
const secondaryEditor = CodeMirror(
document.getElementById('secondary-editor'),
{
lineNumbers: true,
lineWrapping: true,
}
);
const buffers = {};
@@ -85,37 +81,61 @@ const buffersDropDown = document.getElementById('buffers-selection');
// By default open 3 buffers, main, tab1 and css
// TODO: add a JS option
// Params for OpenBuffer (buffers object, name of buffer to create, default content, type, link in UI 1, link in UI 2)
openBuffer(buffers, 'main',
'# python code or code blocks that start with %%py, %%md %%math.', 'notebook',
buffersDropDown, buffersList);
openBuffer(buffers, 'python', '# Python code', 'python',
buffersDropDown, buffersList);
openBuffer(buffers, 'js', '// Javascript code go here', 'javascript',
buffersDropDown, buffersList);
openBuffer(
buffers,
'main',
'# python code or code blocks that start with %%py, %%md %%math.',
'notebook',
buffersDropDown,
buffersList
);
openBuffer(
buffers,
'python',
'# Python code',
'python',
buffersDropDown,
buffersList
);
openBuffer(
buffers,
'js',
'// Javascript code go here',
'javascript',
buffersDropDown,
buffersList
);
openBuffer(
buffers,
'css',
'/* CSS goes here */',
'css',
buffersDropDown,
buffersList
);
openBuffer(buffers, 'css', '/* CSS goes here */', 'css', buffersDropDown,
buffersList);
// select main buffer by default and set the main tab to active
selectBuffer(primaryEditor, buffers, 'main');
selectBuffer(secondaryEditor, buffers, 'main');
document.querySelector('ul#buffers-list li:first-child').classList.add('active');
document
.querySelector('ul#buffers-list li:first-child')
.classList.add('active');
function onReady() {
/* By default the notebook has the keyword "loading"
/* By default the notebook has the keyword "loading"
once python and doc is ready:
create an empty div and set the id to 'rp_loaded'
so that the test knows that we're ready */
const readyElement = document.createElement('div');
readyElement.id = 'rp_loaded';
document.head.appendChild(readyElement);
// set the notebook to empty
notebook.innerHTML = '';
const readyElement = document.createElement('div');
readyElement.id = 'rp_loaded';
document.head.appendChild(readyElement);
// set the notebook to empty
notebook.innerHTML = '';
}
document.getElementById('run-btn').addEventListener('click', readEditors);
// on click of run
@@ -123,18 +143,18 @@ document.getElementById('run-btn').addEventListener('click', readEditors);
// 2. get and run content of all tabs (including dynamically added ones)
// 3. run main tab.
function readEditors() {
// Clean the console and errors
notebook.innerHTML = '';
error.textContent = '';
// Clean the console and errors
notebook.innerHTML = '';
error.textContent = '';
// get the content of the css editor
// and add the css to the head
// use dataset.status for a flag to know when to update
let cssCode = buffers['css'].getValue();
let cssStatus = checkCssStatus();
switch (cssStatus) {
switch (cssStatus) {
case 'none':
addCSS(cssCode);
addCSS(cssCode);
break;
case 'modified':
// remove the old style then add the new one
@@ -143,151 +163,141 @@ function readEditors() {
break;
default:
// do nothing
}
//
let jsCode = buffers['js'].getValue();
runJS(jsCode);
}
//
let jsCode = buffers['js'].getValue();
runJS(jsCode);
// get all the buffers, except css, js and main
// css is auto executed at the start
// main is parsed then executed at the end
// main can have md, math and python function calls
let {
css,
main,
js,
...pythonBuffers
} = buffers;
for (const [name] of Object.entries(pythonBuffers)) {
let pythonCode = buffers[name].getValue();
runPython(pythonCode, notebook, error);
// css is auto executed at the start
// main is parsed then executed at the end
// main can have md, math and python function calls
let { css, main, js, ...pythonBuffers } = buffers;
for (const [name] of Object.entries(pythonBuffers)) {
let pythonCode = buffers[name].getValue();
runPython(pythonCode, notebook, error);
}
parseCodeFromMainEditor();
}
// Parses what is the code editor
// either runs python or renders math or markdown
function parseCodeFromMainEditor() {
// TODO: fix how javascript is injected and executed
// Read javascript code from the jsEditor
// Inject JS into DOM, so that functions can be called from python
// let js_code = buffers["js"].getValue();
// runJS(js_code);
// Read javascript code from the jsEditor
// Inject JS into DOM, so that functions can be called from python
// let js_code = buffers["js"].getValue();
// runJS(js_code);
// gets code from main editor
let mainCode = buffers['main'].getValue();
/*
let mainCode = buffers['main'].getValue();
/*
Split code into chunks.
Uses %%keyword or %% keyword as separator
Returned object has:
- chunkContent, chunkType, chunkId,
- evalFlags, startLine, endLine
*/
let parsedCode = iomdParser(mainCode);
parsedCode.forEach(async (chunk) => {
// For each type of chunk, do somthing
// so far have py for python, md for markdown and math for math ;p
let content = chunk.chunkContent;
switch (chunk.chunkType) {
// by default assume this is python code
// so users don't have to type py manually
case '':
case 'py':
runPython(content, notebook, error);
break;
// TODO: fix how js is injected and ran
case 'js':
runJS(content);
break;
case 'md':
notebook.innerHTML += renderMarkdown(content);
break;
case 'math':
notebook.innerHTML += renderMath(content);
break;
default:
// do nothing when we see an unknown chunk for now
}
});
let parsedCode = iomdParser(mainCode);
parsedCode.forEach(async (chunk) => {
// For each type of chunk, do somthing
// so far have py for python, md for markdown and math for math ;p
let content = chunk.chunkContent;
switch (chunk.chunkType) {
// by default assume this is python code
// so users don't have to type py manually
case '':
case 'py':
runPython(content, notebook, error);
break;
// TODO: fix how js is injected and ran
case 'js':
runJS(content);
break;
case 'md':
notebook.innerHTML += renderMarkdown(content);
break;
case 'math':
notebook.innerHTML += renderMath(content);
break;
default:
// do nothing when we see an unknown chunk for now
}
});
}
function updatePopup(type, message) {
document.getElementById('popup').dataset.type = type ;
document.getElementById('popup').dataset.type = type;
document.getElementById('popup-header').textContent = message;
}
// import button
// show a url input + fetch button
// takes a url where there is raw code
document.getElementById('popup-import').addEventListener('click', async function () {
let url = document.getElementById('popup-url').value;
let type = document.getElementById('popup').dataset.type;
let code = await genericFetch(url, type);
primaryEditor.setValue(code);
});
document
.getElementById('popup-import')
.addEventListener('click', async function () {
let url = document.getElementById('popup-url').value;
let type = document.getElementById('popup').dataset.type;
let code = await genericFetch(url, type);
primaryEditor.setValue(code);
});
document.getElementById('import-code').addEventListener('click' , function() {
document.getElementById('import-code').addEventListener('click', function () {
updatePopup('python', 'URL (raw text format)');
});
// click on an item in the list
CodeMirror.on(buffersList, 'click', function(e) {
selectBuffer(primaryEditor, buffers, e.target.dataset.language);
CodeMirror.on(buffersList, 'click', function (e) {
selectBuffer(primaryEditor, buffers, e.target.dataset.language);
});
// select an item in the dropdown
CodeMirror.on(buffersDropDown, 'change', function() {
selectBuffer(secondaryEditor, buffers, buffersDropDown.options[
buffersDropDown.selectedIndex].value);
CodeMirror.on(buffersDropDown, 'change', function () {
selectBuffer(
secondaryEditor,
buffers,
buffersDropDown.options[buffersDropDown.selectedIndex].value
);
});
// when css code editor changes
// update data attribute flag to modified
CodeMirror.on(buffers['css'], 'change', function() {
CodeMirror.on(buffers['css'], 'change', function () {
let style = document.getElementsByTagName('style')[0];
if (style) {
style.dataset.status = 'modified';
style.dataset.status = 'modified';
}
});
document.getElementById('buffers-list').addEventListener('click' , function(event) {
let elem = document.querySelector('.active');
if (elem) {
elem.classList.remove('active');
}
event.target.classList.add('active');
document
.getElementById('buffers-list')
.addEventListener('click', function (event) {
let elem = document.querySelector('.active');
if (elem) {
elem.classList.remove('active');
}
event.target.classList.add('active');
});
// new tab, new buffer
document.getElementById('new-tab').addEventListener('click', function () {
newBuf(buffers, buffersDropDown, buffersList, primaryEditor);
});
// new tab, new buffer
document.getElementById('new-tab')
.addEventListener('click', function() {
newBuf(buffers, buffersDropDown, buffersList, primaryEditor);
});
// TODO: those three addEventListener can be re-written into one thing probably
document.getElementById('split-view')
.addEventListener('click', function() {
document.getElementById('primary-editor')
.classList.remove('d-none');
document.getElementById('secondary-editor')
.classList.remove('d-none');
});
document.getElementById('reader-view')
.addEventListener('click', function() {
document.getElementById('primary-editor')
.classList.add('d-none');
document.getElementById('secondary-editor')
.classList.add('d-none');
});
document.getElementById('default-view')
.addEventListener('click', function() {
document.getElementById('primary-editor')
.classList.remove('d-none');
document.getElementById('secondary-editor')
.classList.add('d-none');
});
document.getElementById('split-view').addEventListener('click', function () {
document.getElementById('primary-editor').classList.remove('d-none');
document.getElementById('secondary-editor').classList.remove('d-none');
});
document.getElementById('reader-view').addEventListener('click', function () {
document.getElementById('primary-editor').classList.add('d-none');
document.getElementById('secondary-editor').classList.add('d-none');
});
document.getElementById('default-view').addEventListener('click', function () {
document.getElementById('primary-editor').classList.remove('d-none');
document.getElementById('secondary-editor').classList.add('d-none');
});
+11 -11
View File
@@ -30,7 +30,7 @@ function runPython(code, target, error) {
rp.pyExec(code, {
stdout: (output) => {
target.innerHTML += output;
}
},
});
} catch (err) {
if (err instanceof WebAssembly.RuntimeError) {
@@ -40,19 +40,19 @@ function runPython(code, target, error) {
}
}
function addCSS(code) {
function addCSS(code) {
let style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = code;
// add a data attribute to check if css already loaded
style.dataset.status = "loaded";
style.dataset.status = 'loaded';
document.getElementsByTagName('head')[0].appendChild(style);
}
function checkCssStatus() {
let style = document.getElementsByTagName('style')[0];
if (!style) {
return "none";
return 'none';
} else {
return style.dataset.status;
}
@@ -66,12 +66,12 @@ function runJS(code) {
script.src = url;
doc.appendChild(script);
try {
URL.revokeObjectURL(url);
doc.removeChild(script);
URL.revokeObjectURL(url);
doc.removeChild(script);
} catch (e) {
// ignore if body is changed and script is detached
console.log(e);
// ignore if body is changed and script is detached
console.log(e);
}
}
export { runPython, runJS, renderMarkdown, renderMath, addCSS, checkCssStatus }
}
export { runPython, runJS, renderMarkdown, renderMath, addCSS, checkCssStatus };
+2 -4
View File
@@ -59,7 +59,6 @@ h6 {
color: #fff;
}
.item-center {
flex-grow: 1;
text-align: center;
@@ -112,7 +111,7 @@ ul.list-inline li {
line-height: 30px;
}
.active {
.active {
background-color: #1f2430;
color: #fff;
}
@@ -138,7 +137,7 @@ ul.list-inline li {
height: calc(90vh - 60px);
}
.CodeMirror , .CodeMirror-wrap {
.CodeMirror, .CodeMirror-wrap {
height: 100% !important;
}
#rp-notebook {
@@ -196,7 +195,6 @@ input[type='url'] {
border-left: 1px solid rgb(221, 221, 221);
}
/* css popup */
.overlay {
position: fixed;
+21 -21
View File
@@ -1,24 +1,24 @@
export const getResponseTypeFromFetchType = fetchEntry => {
if (fetchEntry === "python") return "text";
if (fetchEntry === "javascript") return "text";
if (fetchEntry === "css") return "text";
if (fetchEntry === "js") return "blob";
if (fetchEntry === "plugin") return "text";
if (fetchEntry === "bytes") return "arrayBuffer";
export const getResponseTypeFromFetchType = (fetchEntry) => {
if (fetchEntry === 'python') return 'text';
if (fetchEntry === 'javascript') return 'text';
if (fetchEntry === 'css') return 'text';
if (fetchEntry === 'js') return 'blob';
if (fetchEntry === 'plugin') return 'text';
if (fetchEntry === 'bytes') return 'arrayBuffer';
return fetchEntry;
};
export function genericFetch(path, fetchType) {
};
export function genericFetch(path, fetchType) {
const responseType = getResponseTypeFromFetchType(fetchType);
return fetch(path)
.then(r => {
if (!r.ok) throw new Error(`${r.status} ${r.statusText} (${path})`);
return r[responseType]();
})
.then(r => {
if (fetchType === "bytes") {
return new Uint8Array(r);
}
return r;
});
}
.then((r) => {
if (!r.ok) throw new Error(`${r.status} ${r.statusText} (${path})`);
return r[responseType]();
})
.then((r) => {
if (fetchType === 'bytes') {
return new Uint8Array(r);
}
return r;
});
}