@jseeio/jsee 0.2.8 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -3
- package/README.md +42 -11
- package/dist/jsee.js +1 -1
- package/dist/jsee.runtime.js +1 -1
- package/jest.config.js +1 -0
- package/package.json +28 -25
- package/src/app.js +26 -16
- package/src/main.js +678 -0
- package/src/utils.js +91 -12
- package/src/worker.js +56 -60
- package/templates/bulma-app.vue +32 -7
- package/templates/bulma-input.vue +11 -5
- package/templates/common-inputs.js +3 -0
- package/test/class.html +22 -0
- package/test/importw.html +28 -0
- package/test/minimal.html +1 -1
- package/test/minimal4.html +1 -1
- package/test/pipeline.html +52 -0
- package/test/python.html +41 -0
- package/{test.js → test/test-basic.test.js} +94 -17
- package/test/test-python.test.js +26 -0
- package/webpack.config.js +2 -1
- package/main.js +0 -621
- package/test/example-async-function.js +0 -0
- package/test/example-async-init.js +0 -0
package/src/utils.js
CHANGED
|
@@ -1,21 +1,92 @@
|
|
|
1
|
-
|
|
1
|
+
// https://stackoverflow.com/questions/8511281/check-if-a-value-is-an-object-in-javascript
|
|
2
|
+
function isObject (item) {
|
|
3
|
+
return (typeof item === 'object' && !Array.isArray(item) && item !== null)
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
async function getModelFuncJS (model, target, app) {
|
|
7
|
+
let modelFunc
|
|
2
8
|
switch (model.type) {
|
|
3
9
|
case 'class':
|
|
4
|
-
log('Init class')
|
|
10
|
+
app.log('Init class')
|
|
5
11
|
const modelClass = new target()
|
|
6
|
-
|
|
12
|
+
modelFunc = (...a) => {
|
|
7
13
|
return modelClass[model.method || 'predict'](...a)
|
|
8
14
|
}
|
|
15
|
+
break
|
|
9
16
|
case 'async-init':
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
log('> Async init resolved: ', m)
|
|
14
|
-
return m
|
|
15
|
-
})
|
|
17
|
+
app.log('Function with async init')
|
|
18
|
+
modelFunc = await target()
|
|
19
|
+
break
|
|
16
20
|
default:
|
|
17
|
-
log('Init function')
|
|
18
|
-
|
|
21
|
+
app.log('Init function')
|
|
22
|
+
modelFunc = target
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Wrap modelFunc to take into account container
|
|
26
|
+
// Possible cases:
|
|
27
|
+
if (model.container === 'args') {
|
|
28
|
+
return (...a) => {
|
|
29
|
+
if (Array.isArray(a[0]) && a[0].length && a.length === 1) {
|
|
30
|
+
return modelFunc(...a[0])
|
|
31
|
+
} else if (isObject(a[0]) && a.length === 1) {
|
|
32
|
+
return modelFunc(...Object.values(a[0]))
|
|
33
|
+
} else {
|
|
34
|
+
return modelFunc(...a)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
return (...a) => {
|
|
39
|
+
if (isObject(a[0]) && a.length === 1) {
|
|
40
|
+
// In case when we have only one input object
|
|
41
|
+
// Pass log and callback to the model function
|
|
42
|
+
return modelFunc(a[0], app)
|
|
43
|
+
} else {
|
|
44
|
+
return modelFunc(...a)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function getUrl (url) {
|
|
51
|
+
let newUrl
|
|
52
|
+
try {
|
|
53
|
+
newUrl = (new URL(url)).href
|
|
54
|
+
} catch (e) {
|
|
55
|
+
newUrl = (new URL(url, 'https://cdn.jsdelivr.net/npm/')).href
|
|
56
|
+
}
|
|
57
|
+
return newUrl
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function importScriptAsync (url, async=true) {
|
|
61
|
+
url = getUrl(url)
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
try {
|
|
64
|
+
const scriptElement = document.createElement('script')
|
|
65
|
+
scriptElement.type = 'text/javascript'
|
|
66
|
+
scriptElement.async = async
|
|
67
|
+
scriptElement.src = url
|
|
68
|
+
scriptElement.addEventListener('load', (ev) => {
|
|
69
|
+
resolve({ status: true })
|
|
70
|
+
})
|
|
71
|
+
scriptElement.addEventListener('error', (ev) => {
|
|
72
|
+
reject({
|
|
73
|
+
status: false,
|
|
74
|
+
message: `Failed to import ${url}`
|
|
75
|
+
})
|
|
76
|
+
})
|
|
77
|
+
document.body.appendChild(scriptElement);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
reject(error)
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function importScripts (...imports) {
|
|
85
|
+
// Load scripts in parallel
|
|
86
|
+
// return Promise.all(imports.map(importScriptAsync))
|
|
87
|
+
// Load scripts in sequence. Possible ordering issues.
|
|
88
|
+
for (const scriptUrl of imports) {
|
|
89
|
+
await importScriptAsync(scriptUrl);
|
|
19
90
|
}
|
|
20
91
|
}
|
|
21
92
|
|
|
@@ -46,7 +117,15 @@ function getModelFuncAPI (model, log=console.log) {
|
|
|
46
117
|
}
|
|
47
118
|
}
|
|
48
119
|
|
|
120
|
+
async function delay (ms) {
|
|
121
|
+
return new Promise(resolve => setTimeout(resolve, ms || 1))
|
|
122
|
+
}
|
|
123
|
+
|
|
49
124
|
module.exports = {
|
|
125
|
+
isObject,
|
|
50
126
|
getModelFuncJS,
|
|
51
|
-
getModelFuncAPI
|
|
127
|
+
getModelFuncAPI,
|
|
128
|
+
importScripts,
|
|
129
|
+
getUrl,
|
|
130
|
+
delay
|
|
52
131
|
}
|
package/src/worker.js
CHANGED
|
@@ -6,20 +6,47 @@ function log () {
|
|
|
6
6
|
postMessage({_status: 'log', _log: args})
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
function progress (value) {
|
|
10
|
+
postMessage({_status: 'progress', _progress: value})
|
|
11
|
+
}
|
|
12
|
+
|
|
9
13
|
function initTF (model) {
|
|
10
14
|
throw new Error('Tensorflow in worker (not implemented)')
|
|
11
15
|
}
|
|
12
16
|
|
|
13
|
-
function initPython (model) {
|
|
14
|
-
|
|
17
|
+
async function initPython (model) {
|
|
18
|
+
importScripts("https://cdn.jsdelivr.net/pyodide/v0.24.1/full/pyodide.js")
|
|
19
|
+
const pyodide = await loadPyodide()
|
|
20
|
+
if (model.imports && Array.isArray(model.imports) && model.imports.length) {
|
|
21
|
+
await pyodide.loadPackage(model.imports)
|
|
22
|
+
} else {
|
|
23
|
+
await pyodide.loadPackagesFromImports(model.code)
|
|
24
|
+
}
|
|
25
|
+
return async (data) => {
|
|
26
|
+
for (let key in data) {
|
|
27
|
+
self[key] = data[key]
|
|
28
|
+
}
|
|
29
|
+
return await pyodide.runPythonAsync(model.code);
|
|
30
|
+
}
|
|
15
31
|
}
|
|
16
32
|
|
|
17
|
-
function initJS (model) {
|
|
33
|
+
async function initJS (model) {
|
|
18
34
|
log('Init JS')
|
|
19
35
|
this.container = model.container
|
|
20
36
|
|
|
37
|
+
// Load imports
|
|
38
|
+
if (model.imports && model.imports.length) {
|
|
39
|
+
log('Loading imports...')
|
|
40
|
+
for (let imp of model.imports) {
|
|
41
|
+
// Try creating an url
|
|
42
|
+
const url = utils.getUrl(imp)
|
|
43
|
+
log('Importing:', url)
|
|
44
|
+
importScripts(url)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
21
48
|
if (model.code) {
|
|
22
|
-
log('Load code as a string')
|
|
49
|
+
log('Load code as a string', model)
|
|
23
50
|
// https://github.com/altbdoor/blob-worker/blob/master/blobWorker.js
|
|
24
51
|
importScripts(URL.createObjectURL(new Blob([model.code], { type: 'text/javascript' })))
|
|
25
52
|
} else if (model.url) {
|
|
@@ -29,92 +56,61 @@ function initJS (model) {
|
|
|
29
56
|
log('No script provided')
|
|
30
57
|
}
|
|
31
58
|
|
|
32
|
-
|
|
33
59
|
// Related:
|
|
34
60
|
// https://stackoverflow.com/questions/37711603/javascript-es6-class-definition-not-accessible-in-window-global
|
|
35
|
-
const target = model.type === 'class'
|
|
61
|
+
const target = model.type === 'class'
|
|
36
62
|
? eval(model.name)
|
|
37
63
|
: this[model.name]
|
|
64
|
+
|
|
38
65
|
// Need promise here in case of async init
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
this.modelFunc = m
|
|
43
|
-
})
|
|
66
|
+
let modelFunc = await utils.getModelFuncJS(model, target, { log, progress })
|
|
67
|
+
|
|
68
|
+
return modelFunc
|
|
44
69
|
}
|
|
45
70
|
|
|
46
71
|
function initAPI (model) {
|
|
47
72
|
log('Init API')
|
|
48
|
-
|
|
73
|
+
return utils.getModelFuncAPI(model, log)
|
|
49
74
|
}
|
|
50
75
|
|
|
51
|
-
onmessage = function (e) {
|
|
52
|
-
|
|
76
|
+
onmessage = async function (e) {
|
|
53
77
|
var data = e.data
|
|
54
78
|
log('Received message of type:', typeof data)
|
|
55
79
|
|
|
56
80
|
if ((typeof data === 'object') && ((data.url) || (data.code))) {
|
|
57
|
-
|
|
58
|
-
INIT MESSAGE
|
|
59
|
-
*/
|
|
60
|
-
let model = data
|
|
81
|
+
// Init message
|
|
61
82
|
log('Init...')
|
|
62
|
-
|
|
63
|
-
switch (
|
|
83
|
+
let m = data
|
|
84
|
+
switch (m.type) {
|
|
64
85
|
case 'tf':
|
|
65
|
-
initTF(
|
|
86
|
+
self.modelFunc = await initTF(m)
|
|
66
87
|
break
|
|
67
88
|
case 'py':
|
|
68
|
-
initPython(
|
|
89
|
+
self.modelFunc = await initPython(m)
|
|
69
90
|
break
|
|
70
91
|
case 'function':
|
|
71
92
|
case 'class':
|
|
72
93
|
case 'async-init':
|
|
73
94
|
case 'async-function':
|
|
74
|
-
initJS(
|
|
95
|
+
self.modelFunc = await initJS(m)
|
|
75
96
|
break
|
|
76
97
|
case 'get':
|
|
77
98
|
case 'post':
|
|
78
|
-
initAPI(
|
|
99
|
+
self.modelFunc = await initAPI(m)
|
|
79
100
|
break
|
|
101
|
+
default:
|
|
102
|
+
throw new Error(`No type information: ${m.type}`)
|
|
80
103
|
}
|
|
104
|
+
postMessage({_status: 'loaded'})
|
|
81
105
|
} else {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
/*
|
|
91
|
-
const keys = Object.keys(data)
|
|
92
|
-
for (let key of keys) {
|
|
93
|
-
self[key] = data[key];
|
|
94
|
-
}
|
|
95
|
-
self.pyodide.runPythonAsync(this.model, () => {})
|
|
96
|
-
.then((res) => {
|
|
97
|
-
console.log('[Worker] Py results: ', typeof res, res)
|
|
98
|
-
postMessage(res)
|
|
99
|
-
})
|
|
100
|
-
.catch((err) => {
|
|
101
|
-
// self.postMessage({error : err.message});
|
|
102
|
-
})
|
|
103
|
-
*/
|
|
104
|
-
} else {
|
|
105
|
-
// JavaScript model
|
|
106
|
-
log('Calling JavaScript model')
|
|
107
|
-
if (this.container === 'args') {
|
|
108
|
-
log('Applying inputs as arguments')
|
|
109
|
-
res = this.modelFunc.apply(null, data)
|
|
110
|
-
} else {
|
|
111
|
-
// JS object or array
|
|
112
|
-
log('Applying inputs as object/array')
|
|
113
|
-
res = this.modelFunc(data, log)
|
|
114
|
-
}
|
|
115
|
-
// Return promise value or just regular value
|
|
116
|
-
// Promise.resolve handles both cases
|
|
117
|
-
Promise.resolve(res).then(r => { postMessage(r) })
|
|
106
|
+
// Execution
|
|
107
|
+
try {
|
|
108
|
+
log('Run model with data:', data)
|
|
109
|
+
const results = await self.modelFunc(data)
|
|
110
|
+
log('Results:', results)
|
|
111
|
+
postMessage(results)
|
|
112
|
+
} catch (error) {
|
|
113
|
+
postMessage({ _status: 'error', _error: error })
|
|
118
114
|
}
|
|
119
115
|
}
|
|
120
116
|
}
|
package/templates/bulma-app.vue
CHANGED
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
justify-content: right;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
.card-footer .run-button:hover
|
|
88
|
+
.card-footer .run-button:hover {
|
|
89
89
|
background-color: transparent !important;
|
|
90
90
|
background: linear-gradient(270deg, #02dbb2 0%, #fff0 80%);
|
|
91
91
|
box-shadow: 5px 0px 5px -2px #48ffd43b;
|
|
@@ -95,6 +95,13 @@
|
|
|
95
95
|
color: #016c5c !important;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
.example-button {
|
|
99
|
+
margin-top: 3px;
|
|
100
|
+
padding: 5px 10px;
|
|
101
|
+
border-radius: 5px !important;
|
|
102
|
+
height: auto;
|
|
103
|
+
}
|
|
104
|
+
|
|
98
105
|
.field {
|
|
99
106
|
margin-bottom: 5px;
|
|
100
107
|
}
|
|
@@ -129,20 +136,25 @@
|
|
|
129
136
|
<p v-if="$parent.model.description">{{ $parent.model.description }}</p>
|
|
130
137
|
</div>
|
|
131
138
|
</div>
|
|
132
|
-
<div class="columns">
|
|
139
|
+
<div class="columns is-multiline">
|
|
133
140
|
<div class="column" v-bind:class="($parent.design && $parent.design.grid && ($parent.design.grid.length > 0)) ? 'is-' + $parent.design.grid[0] : ''">
|
|
141
|
+
<!-- Inputs -->
|
|
134
142
|
<div class="card bordered">
|
|
135
143
|
<div class="card-content" id="inputs" v-if="$parent.inputs && $parent.inputs.length > 0">
|
|
136
144
|
<ul>
|
|
137
145
|
<li v-for="(input, index) in $parent.inputs">
|
|
138
|
-
<vue-input
|
|
146
|
+
<vue-input
|
|
147
|
+
v-bind:input="input"
|
|
148
|
+
v-if="input.display !== false && $parent.display(index)"
|
|
149
|
+
v-on:inchange="$parent.run()"
|
|
150
|
+
></vue-input>
|
|
139
151
|
</li>
|
|
140
152
|
</ul>
|
|
141
153
|
<pre v-if="$parent.model.debug">{{ $parent.inputs }}</pre>
|
|
142
154
|
<!-- <button class="button is-primary" id="run"><span>▸</span> Run</button> -->
|
|
143
155
|
</div>
|
|
144
156
|
<footer class="card-footer" v-bind:class="{ reset: $parent.dataChanged }">
|
|
145
|
-
<button
|
|
157
|
+
<button
|
|
146
158
|
v-on:click="$parent.reset()"
|
|
147
159
|
v-if="$parent.inputs && $parent.inputs.length > 0 && $parent.dataChanged"
|
|
148
160
|
class="button reset-button icon card-footer-item is-danger is-small"
|
|
@@ -150,8 +162,8 @@
|
|
|
150
162
|
<span class="rest-icon has-text-danger-dark">✕</span>
|
|
151
163
|
<span> Reset</span>
|
|
152
164
|
</button>
|
|
153
|
-
<button
|
|
154
|
-
v-on:click="$parent.run()"
|
|
165
|
+
<button
|
|
166
|
+
v-on:click="$parent.run('run')"
|
|
155
167
|
class="button run-button icon card-footer-item is-primary is-small"
|
|
156
168
|
v-bind:class="{ running: $parent.clickRun }"
|
|
157
169
|
>
|
|
@@ -160,8 +172,21 @@
|
|
|
160
172
|
</button>
|
|
161
173
|
</footer>
|
|
162
174
|
</div>
|
|
175
|
+
<!-- Examples -->
|
|
176
|
+
<div v-if="$parent.examples">
|
|
177
|
+
<p style="margin-top: 20px">Examples</p>
|
|
178
|
+
<div v-for="(example, index) in $parent.examples">
|
|
179
|
+
<button
|
|
180
|
+
v-on:click="$parent.reset(example)"
|
|
181
|
+
class="button is-small example-button"
|
|
182
|
+
>
|
|
183
|
+
{{ example }}
|
|
184
|
+
</button>
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
163
187
|
</div>
|
|
164
|
-
<div class="column" id="outputs">
|
|
188
|
+
<div class="column" id="outputs" v-bind:class="($parent.design && $parent.design.grid && ($parent.design.grid.length > 1)) ? 'is-' + $parent.design.grid[1] : ''">
|
|
189
|
+
<!-- Outputs -->
|
|
165
190
|
<div v-if="$parent.outputs">
|
|
166
191
|
<div v-for="(output, index) in $parent.outputs">
|
|
167
192
|
<vue-output v-bind:output="output" v-on:notification="$parent.notify($event)"></vue-output>
|
|
@@ -85,12 +85,8 @@
|
|
|
85
85
|
type="file"
|
|
86
86
|
>
|
|
87
87
|
<span class="file-cta">
|
|
88
|
-
<span class="file-icon">
|
|
89
|
-
<span v-if="input.file && input.file.name">✓</span>
|
|
90
|
-
<span v-else>↥</span>
|
|
91
|
-
</span>
|
|
92
88
|
<span class="file-label">
|
|
93
|
-
|
|
89
|
+
Open
|
|
94
90
|
</span>
|
|
95
91
|
</span>
|
|
96
92
|
<span class="file-name">
|
|
@@ -102,6 +98,16 @@
|
|
|
102
98
|
</div>
|
|
103
99
|
</div>
|
|
104
100
|
|
|
101
|
+
<div class="field" v-if="input.type == 'action' || input.type == 'button'">
|
|
102
|
+
<button
|
|
103
|
+
v-on:click="$parent.$parent.run(input.name.toLowerCase().replace(/ /g, '_'))"
|
|
104
|
+
class="button is-small"
|
|
105
|
+
style="width: 100%; padding: 5px 0; margin-top: 8px; text-align: left;"
|
|
106
|
+
>
|
|
107
|
+
<span> {{ input.title ? input.title : input.name }} </span>
|
|
108
|
+
</button>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
105
111
|
<div class="field is-horizontal" v-if="input.type == 'group'">
|
|
106
112
|
<div class="field-body">
|
|
107
113
|
<vue-input v-for="(el, index) in input.elements" v-bind:input="el" ></vue-input>
|
package/test/class.html
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
<html>
|
|
3
|
+
<div id="jsee-container">
|
|
4
|
+
<script src="/dist/jsee.js"></script>
|
|
5
|
+
<script>
|
|
6
|
+
new JSEE({
|
|
7
|
+
schema: {
|
|
8
|
+
'model': {
|
|
9
|
+
'name': 'Doubler',
|
|
10
|
+
'method': 'double',
|
|
11
|
+
'type': 'class',
|
|
12
|
+
'container': 'args',
|
|
13
|
+
'url': '/test/example-class.js',
|
|
14
|
+
},
|
|
15
|
+
'inputs': [
|
|
16
|
+
{ 'name': 'b', 'type': 'int', 'default': 100 }
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
container: '#jsee-container'
|
|
20
|
+
})
|
|
21
|
+
</script>
|
|
22
|
+
</html>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<html>
|
|
2
|
+
<div id="jsee-container">
|
|
3
|
+
<script src="/dist/jsee.js"></script>
|
|
4
|
+
<script>
|
|
5
|
+
|
|
6
|
+
new JSEE({
|
|
7
|
+
schema: {
|
|
8
|
+
"model": {
|
|
9
|
+
"name": "kebab",
|
|
10
|
+
"type": "function",
|
|
11
|
+
"container": "args",
|
|
12
|
+
"code": `
|
|
13
|
+
function kebab (str) {
|
|
14
|
+
return _.kebabCase(str)
|
|
15
|
+
}
|
|
16
|
+
`,
|
|
17
|
+
"worker": false,
|
|
18
|
+
},
|
|
19
|
+
"imports": "lodash@4.17.21/lodash.min.js",
|
|
20
|
+
"inputs": [
|
|
21
|
+
{ "name": "str", "type": "string", "default": 'FooBar' },
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
container: '#jsee-container',
|
|
25
|
+
verbose: true
|
|
26
|
+
})
|
|
27
|
+
</script>
|
|
28
|
+
</html>
|
package/test/minimal.html
CHANGED
package/test/minimal4.html
CHANGED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<html>
|
|
2
|
+
<div id="jsee-container">
|
|
3
|
+
<script src="/dist/jsee.js"></script>
|
|
4
|
+
<script>
|
|
5
|
+
new JSEE({
|
|
6
|
+
schema: {
|
|
7
|
+
"model": [
|
|
8
|
+
{
|
|
9
|
+
"name": "sum",
|
|
10
|
+
"type": "function",
|
|
11
|
+
"container": "args",
|
|
12
|
+
"code": function sum (a, b) {
|
|
13
|
+
return a + b
|
|
14
|
+
},
|
|
15
|
+
"worker": true
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "square",
|
|
19
|
+
"type": "function",
|
|
20
|
+
"container": "args",
|
|
21
|
+
"code": function square (a) {
|
|
22
|
+
return a * a
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"name": "Increment",
|
|
27
|
+
"type": "class",
|
|
28
|
+
"method": "step",
|
|
29
|
+
"container": "args",
|
|
30
|
+
"code": class Increment {
|
|
31
|
+
constructor () {
|
|
32
|
+
this.i = 0
|
|
33
|
+
}
|
|
34
|
+
step (a) {
|
|
35
|
+
this.i += 1
|
|
36
|
+
return a + this.i
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"inputs": [
|
|
42
|
+
{ "name": "a", "type": "int", "default": 2 },
|
|
43
|
+
{ "name": "b", "type": "int", "default": 3 }
|
|
44
|
+
],
|
|
45
|
+
"outputs": [
|
|
46
|
+
{ "name": "square", "type": "int" }
|
|
47
|
+
],
|
|
48
|
+
"autorun": false
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
</script>
|
|
52
|
+
</html>
|
package/test/python.html
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<html>
|
|
2
|
+
<div id="jsee-container">
|
|
3
|
+
<script src="/dist/jsee.js"></script>
|
|
4
|
+
<script>
|
|
5
|
+
new JSEE({
|
|
6
|
+
schema: {
|
|
7
|
+
"model": {
|
|
8
|
+
"name": "pytest",
|
|
9
|
+
"type": "py",
|
|
10
|
+
"description": "Testing pyodide project",
|
|
11
|
+
"container": "object",
|
|
12
|
+
"code": `
|
|
13
|
+
import numpy as np
|
|
14
|
+
from js import a, b
|
|
15
|
+
np.mean([a, b])
|
|
16
|
+
`.replace(/^\s+/gm, ''),
|
|
17
|
+
"autorun": false,
|
|
18
|
+
"worker": true,
|
|
19
|
+
"imports": [
|
|
20
|
+
"numpy"
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
"inputs": [
|
|
24
|
+
{
|
|
25
|
+
"type": "int",
|
|
26
|
+
"name": "a"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"type": "int",
|
|
30
|
+
"name": "b"
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"outputs": [
|
|
34
|
+
{
|
|
35
|
+
"type": "int"
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
</script>
|
|
41
|
+
</html>
|