@jseeio/jsee 0.2.1

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/src/app.js ADDED
@@ -0,0 +1,188 @@
1
+ // import Vue from 'vue'
2
+ // import { createApp } from 'vue'
3
+ // import { createApp } from 'vue/dist/vue.esm-bundler'
4
+ // import { createApp, h } from 'vue/dist/vue.runtime.esm-bundler.js'
5
+
6
+ import { createApp, h } from 'vue' // <- resolved in webpack.config based on RUNTIME
7
+
8
+ import bulmaApp from '../templates/bulma-app.vue'
9
+ import bulmaInput from '../templates/bulma-input.vue'
10
+ import bulmaOutput from '../templates/bulma-output.vue'
11
+
12
+ const components = {
13
+ 'bulma': {
14
+ 'app': bulmaApp,
15
+ 'input': bulmaInput,
16
+ 'output': bulmaOutput
17
+ }
18
+ }
19
+
20
+ const filtrex = require('filtrex')
21
+ const JsonViewer = require('vue3-json-viewer').default
22
+
23
+ function log () {
24
+ console.log(`[Vue]`, ...arguments)
25
+ }
26
+
27
+ function resetInputs (inputs) {
28
+ log('Resetting inputs...')
29
+ inputs.forEach(input => {
30
+ if (input.default) {
31
+ input.value = input.default
32
+ } else {
33
+ switch (input.type) {
34
+ case 'int':
35
+ case 'float':
36
+ case 'number':
37
+ input.value = 0
38
+ break
39
+ case 'string':
40
+ case 'text':
41
+ input.value = ''
42
+ break
43
+ case 'color':
44
+ input.value = '#000000'
45
+ break
46
+ case 'categorical':
47
+ case 'select':
48
+ input.value = input.options ? input.options[0] : ''
49
+ break
50
+ case 'bool':
51
+ case 'checkbox':
52
+ input.value = false
53
+ break
54
+ case 'file':
55
+ input.file = null
56
+ input.value = ''
57
+ break
58
+ case 'group':
59
+ resetInputs(input.elements)
60
+ break
61
+ default:
62
+ input.value = ''
63
+ }
64
+ }
65
+ })
66
+ }
67
+
68
+ function createVueApp (env, dataInit, mountedCallback) {
69
+ // Reset input values to default ones
70
+ resetInputs(dataInit.inputs)
71
+
72
+ if (!('outputs' in dataInit)) {
73
+ dataInit.outputs = []
74
+ }
75
+
76
+ function len(s) {
77
+ return s.length;
78
+ }
79
+
80
+ let filtrexOptions = {
81
+ extraFunctions: { len }
82
+ }
83
+
84
+ // Prepare functions that determine if inputs should be displayed
85
+ const displayFunctions = dataInit.inputs.map(input => {
86
+ if (input.display && input.display.length) {
87
+ const f = filtrex.compileExpression(input.display.replace(/\'/g, '"'), filtrexOptions)
88
+ return function DisplayConditionally (data) {
89
+ const inputObj = {}
90
+ data.inputs.filter(input => input.name).forEach(input => {
91
+ inputObj[input.name] = input.value
92
+ })
93
+ return f(inputObj)
94
+ }
95
+ } else {
96
+ return function DisplayAlways () {
97
+ return true
98
+ }
99
+ }
100
+ })
101
+
102
+ // Determine a container for Vue app
103
+ const container = env.params.container
104
+ ? (typeof env.params.container === 'string')
105
+ ? document.querySelector(env.params.container)
106
+ : env.params.container
107
+ : document.body
108
+
109
+ // Determine a template and GUI framework
110
+ const framework = (env.schema.design && typeof env.schema.design.framework !== 'undefined')
111
+ ? env.schema.design.framework
112
+ : 'bulma'
113
+
114
+ let template
115
+ let render
116
+ if (
117
+ env.schema.design
118
+ && env.schema.design.template
119
+ && (
120
+ typeof env.schema.design.template === 'string'
121
+ || env.schema.design.template === false
122
+ )
123
+ ) {
124
+ template = env.schema.design.template
125
+ render = null
126
+ } else {
127
+ template = null //'<vue-app/>'
128
+ render = () => {
129
+ return h(components[framework].app)
130
+ }
131
+ }
132
+
133
+ log('Initializing Vue app...')
134
+ const app = createApp({
135
+ template,
136
+ render,
137
+ data () {
138
+ return dataInit
139
+ },
140
+ watch: {
141
+ inputs: {
142
+ deep: true,
143
+ immediate: false,
144
+ handler (v) {
145
+ if (this.model.autorun) {
146
+ env.run()
147
+ }
148
+ }
149
+ }
150
+ },
151
+ mounted () {
152
+ mountedCallback(container)
153
+ },
154
+ methods: {
155
+ display (index) {
156
+ const res = displayFunctions[index](this.$data)
157
+ return res
158
+ },
159
+ reset () {
160
+ resetInputs(this.inputs)
161
+ },
162
+ run () {
163
+ env.run()
164
+ },
165
+ notify (msg) {
166
+ env.notify(msg)
167
+ }
168
+ },
169
+ })
170
+
171
+ if (framework !== false) {
172
+ app.component('vue-app', components[framework].app)
173
+ app.component('vue-input', components[framework].input)
174
+ app.component('vue-output', components[framework].output)
175
+ }
176
+
177
+ // Json viewer
178
+ app.use(JsonViewer)
179
+
180
+ // Load Vue framework if present
181
+ if (framework in window) {
182
+ app.use(window[framework])
183
+ }
184
+
185
+ return app.mount(container) // After app.mount() it's not the same app
186
+ }
187
+
188
+ export { createVueApp }
package/src/overlay.js ADDED
@@ -0,0 +1,33 @@
1
+ class Overlay {
2
+ constructor (parent) {
3
+ this.element = document.createElement('div')
4
+ this.element.id = 'overlay'
5
+ this.element.className = 'valign-wrapper'
6
+ this.element.innerHTML = `
7
+ <div class="center-align" style="width:100%">
8
+ <div class="preloader-wrapper small active">
9
+ <div class="spinner-layer spinner-green-only">
10
+ <div class="circle-clipper left">
11
+ <div class="circle"></div>
12
+ </div><div class="gap-patch">
13
+ <div class="circle"></div>
14
+ </div><div class="circle-clipper right">
15
+ <div class="circle"></div>
16
+ </div>
17
+ </div>
18
+ </div>
19
+ </div>
20
+ `
21
+ parent.appendChild(this.element)
22
+ }
23
+
24
+ show () {
25
+ this.element.style.display = 'flex'
26
+ }
27
+
28
+ hide () {
29
+ this.element.style.display = 'none'
30
+ }
31
+ }
32
+
33
+ module.exports = Overlay
package/src/worker.js ADDED
@@ -0,0 +1,113 @@
1
+ function log () {
2
+ const args = Array.prototype.slice.call(arguments);
3
+ args.unshift('[Worker]')
4
+ console.log(args)
5
+ postMessage({_status: 'log', _log: args})
6
+ }
7
+
8
+ onmessage = function (e) {
9
+ var data = e.data
10
+ log('Received message of type:', typeof data)
11
+
12
+ if ((typeof data === 'object') && ((data.url) || (data.code))) {
13
+ /*
14
+ INIT MESSAGE
15
+ */
16
+ let model = data
17
+
18
+ if (model.type === 'py') {
19
+ // Python with Pyodide
20
+ importScripts('https://pyodide.cdn.iodide.io/pyodide.js')
21
+ // Check when all's loaded
22
+ /*
23
+ TODO: Implement same loaded as port
24
+ let pyCheck = setInterval(() => {
25
+ if (self.pyodide && self.pyodide.runPythonAsync && this.model && this.model.length) {
26
+ console.log('[Worker] Pyodide lib and model loaded. Try running to preload all imports')
27
+ self.pyodide.runPythonAsync(this.model, () => {})
28
+ .then((res) => {
29
+ postMessage({_status: 'loaded'})
30
+ })
31
+ .catch((err) => {
32
+ postMessage({_status: 'loaded'})
33
+ })
34
+ clearInterval(pyCheck)
35
+ }
36
+ }, 500)
37
+ */
38
+ } else {
39
+ // Javascript
40
+ this.container = model.container
41
+
42
+ if (model.code) {
43
+ log('Load code from schema')
44
+ // https://github.com/altbdoor/blob-worker/blob/master/blobWorker.js
45
+ importScripts(URL.createObjectURL(new Blob([model.code], { type: 'text/javascript' })))
46
+ } else if (model.url) {
47
+ log('Load script from URL:', model.url)
48
+ importScripts(model.url)
49
+ } else {
50
+ log('No script provided')
51
+ }
52
+
53
+ if (model.type === 'class') {
54
+ log('[Worker] Init class')
55
+ // this.modelFunc = (new this[model.name]())[model.method || 'predict']
56
+
57
+ const modelClass = new this[model.name]()
58
+ this.modelFunc = (...a) => {
59
+ return modelClass[model.method || 'predict'](...a)
60
+ }
61
+ } else if (model.type === 'async-init') {
62
+ log('Init function with promise')
63
+ log(this[model.name])
64
+ this[model.name]().then((m) => {
65
+ log('Async init resolved: ', m)
66
+ this.modelFunc = m
67
+ })
68
+ } else {
69
+ log('Init function')
70
+ this.modelFunc = this[model.name]
71
+ }
72
+
73
+ postMessage({_status: 'loaded'})
74
+ }
75
+ } else {
76
+ /*
77
+ CALL MESSAGE
78
+ */
79
+ var res
80
+ if (typeof this.modelFunc === 'string') {
81
+ // Python model:
82
+ log('Calling Python model')
83
+ /*
84
+ const keys = Object.keys(data)
85
+ for (let key of keys) {
86
+ self[key] = data[key];
87
+ }
88
+ self.pyodide.runPythonAsync(this.model, () => {})
89
+ .then((res) => {
90
+ console.log('[Worker] Py results: ', typeof res, res)
91
+ postMessage(res)
92
+ })
93
+ .catch((err) => {
94
+ // self.postMessage({error : err.message});
95
+ })
96
+ */
97
+ } else {
98
+ // JavaScript model
99
+ log('Calling JavaScript model')
100
+ if (this.container === 'args') {
101
+ log('Applying inputs as arguments')
102
+ res = this.modelFunc.apply(null, data)
103
+ } else {
104
+ // JS object or array
105
+ log('Applying inputs as object/array')
106
+ res = this.modelFunc(data, log)
107
+ }
108
+ // Return promise value or just regular value
109
+ // Promise.resolve handles both cases
110
+ Promise.resolve(res).then(r => { postMessage(r) })
111
+ }
112
+ }
113
+ }
@@ -0,0 +1,65 @@
1
+ <style lang="scss" scoped>
2
+ :deep(.jsee-app) {
3
+ @import "../node_modules/vue3-json-viewer/dist/index.css";
4
+ @import "../node_modules/bulma/sass/base/_all.sass";
5
+ @import "../node_modules/bulma/sass/utilities/_all.sass";
6
+ @import "../node_modules/bulma/sass/form/_all.sass";
7
+ @import "../node_modules/bulma/sass/grid/_all.sass";
8
+ @import "../node_modules/bulma/sass/elements/_all.sass";
9
+ @import "../node_modules/bulma/sass/helpers/_all.sass";
10
+ @import "../node_modules/bulma/sass/components/card.sass";
11
+ @import "../node_modules/bulma/sass/layout/section.sass";
12
+
13
+ font-family: sans-serif;
14
+ }
15
+ </style>
16
+ <template>
17
+ <section>
18
+ <div class="jsee-app">
19
+ <div class="columns">
20
+ <div class="column is-full" id="$parent.model" v-if="$parent.model">
21
+ <h2 class="title is-2" v-if="$parent.model.title">{{ $parent.model.title }}</h2>
22
+ <p v-if="$parent.model.description">{{ $parent.model.description }}</p>
23
+ </div>
24
+ </div>
25
+ <div class="columns">
26
+ <div class="column" v-bind:class="($parent.design && $parent.design.grid && ($parent.design.grid.length > 0)) ? 'is-' + $parent.design.grid[0] : ''">
27
+ <div class="card bordered">
28
+ <div class="card-content" id="inputs">
29
+ <ul>
30
+ <li v-for="(input, index) in $parent.inputs">
31
+ <vue-input v-bind:input="input" v-if="$parent.display(index)" v-on:inchange="$parent.run()"></vue-input>
32
+ </li>
33
+ </ul>
34
+ <pre v-if="$parent.model.debug">{{ $parent.inputs }}</pre>
35
+ <!-- <button class="button is-primary" id="run"><span>▸</span>&nbsp;&nbsp;Run</button> -->
36
+ </div>
37
+ <div class="card">
38
+ <footer class="card-footer">
39
+ <button v-on:click="$parent.reset()" class="button icon card-footer-item is-danger is-small">
40
+ <span class="has-text-danger-dark">✕</span>
41
+ <span>&nbsp; Reset</span>
42
+ </button>
43
+ <button v-on:click="$parent.run()" class="button icon card-footer-item is-primary is-small">
44
+ <span class="has-text-primary-dark">▸</span>
45
+ <span>&nbsp; Run</span>
46
+ </button>
47
+ </footer>
48
+ </div>
49
+ </div>
50
+ </div>
51
+ <div class="column" id="outputs">
52
+ <div v-if="$parent.outputs">
53
+ <div v-for="(output, index) in $parent.outputs">
54
+ <vue-output v-bind:output="output" v-on:notification="$parent.notify($event)"></vue-output>
55
+ </div>
56
+ <pre v-if="$parent.model.debug">{{ $parent.outputs }}</pre>
57
+ </div>
58
+ </div>
59
+ </div>
60
+ </div>
61
+ </section>
62
+ </template>
63
+ <script>
64
+ export default {}
65
+ </script>
@@ -0,0 +1,114 @@
1
+ <template>
2
+ <div class="field" v-if="input.type == 'int' || input.type == 'float' || input.type == 'number'">
3
+ <label v-bind:for="input.name" class="is-size-7">{{ input.name }}</label>
4
+ <div class="control">
5
+ <input
6
+ v-model="input.value"
7
+ v-bind:id="input.name"
8
+ v-bind:step="input.type == 'int' ? 1 : 0.001"
9
+ v-bind:placeholder="input.placeholder ? input.placeholder : input.name"
10
+ v-bind:min="input.min"
11
+ v-bind:max="input.max"
12
+ v-on:change="changeHandler"
13
+ class="input"
14
+ type="number"
15
+ >
16
+ </div>
17
+ </div>
18
+
19
+ <div class="field" v-if="input.type == 'string' || input.type == 'color'">
20
+ <label v-bind:for="input.name" class="is-size-7">{{ input.name }}</label>
21
+ <div class="control">
22
+ <input
23
+ v-model="input.value"
24
+ v-bind:id="input.name"
25
+ v-bind:placeholder="input.placeholder ? input.placeholder : input.name"
26
+ v-on:change="changeHandler"
27
+ class="input"
28
+ >
29
+ </div>
30
+ </div>
31
+
32
+ <div class="field" v-if="input.type == 'text'">
33
+ <label v-bind:for="input.name" class="is-size-7">{{ input.name }}</label>
34
+ <div class="control">
35
+ <textarea
36
+ v-model="input.value"
37
+ v-bind:id="input.name"
38
+ v-bind:placeholder="input.placeholder ? input.placeholder : input.name"
39
+ v-on:change="changeHandler"
40
+ class="textarea"
41
+ ></textarea>
42
+ </div>
43
+ </div>
44
+
45
+ <div class="field" v-if="input.type == 'checkbox' || input.type == 'bool'">
46
+ <div class="control">
47
+ <label class="checkbox is-size-7">
48
+ <input
49
+ v-model="input.value"
50
+ v-bind:id="input.name"
51
+ v-on:change="changeHandler"
52
+ type="checkbox"
53
+ >
54
+ {{ input.name }}
55
+ </label>
56
+ </div>
57
+ </div>
58
+
59
+ <div class="field" v-if="input.type == 'categorical' || input.type == 'select'">
60
+ <label v-bind:for="input.name" class="is-size-7">{{ input.name }}</label>
61
+ <div class="control">
62
+ <div class="select is-fullwidth">
63
+ <select
64
+ v-model="input.value"
65
+ v-bind:id="input.name"
66
+ v-on:change="changeHandler"
67
+ >
68
+ <option
69
+ v-for="(option, oi) in input.options"
70
+ >{{ option }}</option>
71
+ </select>
72
+ </div>
73
+ </div>
74
+ </div>
75
+
76
+ <div class="field" v-if="input.type == 'file'">
77
+ <label v-bind:for="input.name" class="is-size-7">{{ input.name }}</label>
78
+ <div class="control">
79
+ <div class="file has-name is-fullwidth" v-bind:class="{ 'is-primary': !input.file }">
80
+ <label class="file-label">
81
+ <input
82
+ v-bind:id="input.name"
83
+ v-on:change="loadFile"
84
+ class="file-input"
85
+ type="file"
86
+ >
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
+ <span class="file-label">
93
+ Choose a file…
94
+ </span>
95
+ </span>
96
+ <span class="file-name">
97
+ <span v-if="input.file && input.file.name">{{ input.file.name }}</span>
98
+ <span v-else>No file selected</span>
99
+ </span>
100
+ </label>
101
+ </div>
102
+ </div>
103
+ </div>
104
+
105
+ <div class="field is-horizontal" v-if="input.type == 'group'">
106
+ <div class="field-body">
107
+ <vue-input v-for="(el, index) in input.elements" v-bind:input="el" ></vue-input>
108
+ </div>
109
+ </div>
110
+ </template>
111
+
112
+ <script>
113
+ export { component as default } from "./common-inputs.js"
114
+ </script>
@@ -0,0 +1,38 @@
1
+ <template>
2
+ <div class="card mb-5" v-if="output.value">
3
+ <header class="card-header">
4
+ <p class="card-header-title is-size-6" v-if="output.name">
5
+ {{ output.name }}
6
+ </p>
7
+ <p class="card-header-icon">
8
+ <button class="button icon is-small" v-on:click="save()">Save</button>
9
+ <button class="button icon is-small" v-on:click="copy()">Copy</button>
10
+ </p>
11
+ </header>
12
+ <div class="card-content">
13
+ <div class="content" v-if="output.type == 'svg'">
14
+ <div v-html="output.value"></div>
15
+ </div>
16
+ <div class="content" v-else-if="output.type == 'object'">
17
+ <json-viewer :value="output.value" copyable sort />
18
+ </div>
19
+ <div class="content" v-if="output.type == 'code'">
20
+ <pre>{{ output.value }}</pre>
21
+ </div>
22
+ <div class="content" v-else>
23
+ <pre>{{ output.value }}</pre>
24
+ </div>
25
+ </div>
26
+ <!--
27
+ <footer class="card-footer" v-if="output.filename">
28
+ <a v-on:click="save()" class="card-footer-item">Save</a>
29
+ <a v-on:click="copy()" class="card-footer-item">Copy</a>
30
+ </footer>
31
+ -->
32
+ </div>
33
+ </template>
34
+
35
+ <script>
36
+ export { component as default } from "./common-outputs.js"
37
+ </script>
38
+
@@ -0,0 +1,28 @@
1
+ const FileReader = window['FileReader']
2
+
3
+ const component = {
4
+ props: ['input'],
5
+ emits: ['inchange'],
6
+ methods: {
7
+ changeHandler () {
8
+ if (this.input.reactive) {
9
+ this.$emit('inchange')
10
+ }
11
+ },
12
+ loadFile (e) {
13
+ const reader = new FileReader()
14
+ this.input.file = e.target.files[0]
15
+ reader.readAsText(this.input.file)
16
+ reader.onload = () => {
17
+ this.input.value = reader.result
18
+ if (typeof this.input.cb !== 'undefined') {
19
+ this.input.cb.run()
20
+ } else if (this.input.reactive) {
21
+ this.$emit('inchange')
22
+ }
23
+ }
24
+ }
25
+ }
26
+ }
27
+
28
+ export { component }
@@ -0,0 +1,39 @@
1
+ import { saveAs } from 'file-saver'
2
+
3
+ const Blob = window['Blob']
4
+
5
+ function stringify (v) {
6
+ return typeof v === 'string'
7
+ ? v
8
+ : JSON.stringify(v)
9
+ }
10
+
11
+ const component = {
12
+ props: ['output'],
13
+ emits: ['notification'],
14
+ methods: {
15
+ save () {
16
+ // Prepare filename
17
+ let filename
18
+ if (this.output.filename) {
19
+ filename = this.output.filename
20
+ } else {
21
+ let name = this.output.name ? this.output.name : 'output'
22
+ let extension = this.output.type === 'svg' ? 'svg': 'txt'
23
+ filename = name + '.' + extension
24
+ }
25
+
26
+ // Prepare blob
27
+ let value = stringify(this.output.value)
28
+ let blob = new Blob([value], {type: 'text/plain;charset=utf-8'})
29
+ saveAs(blob, filename)
30
+ },
31
+ copy () {
32
+ let value = stringify(this.output.value)
33
+ navigator.clipboard.writeText(value)
34
+ this.$emit('notification', 'Copied')
35
+ }
36
+ },
37
+ }
38
+
39
+ export { component }