@jseeio/jsee 0.3.3 → 0.3.4
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/README.md +48 -0
- package/bin/jsee +184 -53
- package/dist/jsee.js +1 -1
- package/dist/jsee.runtime.js +1 -1
- package/package.json +4 -2
- package/src/main.js +47 -2
- package/templates/bulma-app.vue +50 -12
- package/templates/bulma-output.vue +6 -2
- package/templates/common-outputs.js +60 -4
- package/CHANGELOG.md +0 -30
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jseeio/jsee",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"description": "JavaScript Execution Environment",
|
|
5
5
|
"main": "dist/jsee.js",
|
|
6
6
|
"unpkg": "dist/jsee.js",
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"@mdi/font": "^6.5.95",
|
|
34
34
|
"bulma": "^0.9.3",
|
|
35
35
|
"csv-parse": "^4.6.1",
|
|
36
|
+
"dom-to-image": "^2.6.0",
|
|
36
37
|
"element-plus": "^1.3.0-beta.1",
|
|
37
38
|
"express": "^4.19.2",
|
|
38
39
|
"file-saver": "^2.0.2",
|
|
@@ -40,7 +41,8 @@
|
|
|
40
41
|
"jsdoc-to-markdown": "^8.0.1",
|
|
41
42
|
"minimist": "^1.2.8",
|
|
42
43
|
"notyf": "^3.10.0",
|
|
43
|
-
"showdown": "^
|
|
44
|
+
"showdown": "^1.9.1",
|
|
45
|
+
"showdown-katex": "^0.8.0",
|
|
44
46
|
"vue": "^3.2.47",
|
|
45
47
|
"vue-style-loader": "^4.1.3",
|
|
46
48
|
"vue3-json-viewer": "^2.2.2",
|
package/src/main.js
CHANGED
|
@@ -347,12 +347,57 @@ export default class JSEE {
|
|
|
347
347
|
this.schema.inputs = getInputs(this.model[0])
|
|
348
348
|
}
|
|
349
349
|
|
|
350
|
-
//
|
|
351
|
-
|
|
350
|
+
// Read URL params, e.g. ?input1=1&input2=2
|
|
351
|
+
const urlParams = new URLSearchParams(window.location.search)
|
|
352
|
+
log('URL params:', urlParams)
|
|
353
|
+
|
|
354
|
+
// Iterate over inputs and set values from URL
|
|
352
355
|
this.schema.inputs.forEach(input => {
|
|
356
|
+
// Set default input type
|
|
353
357
|
if (typeof input.type === 'undefined') {
|
|
354
358
|
input.type = 'string'
|
|
355
359
|
}
|
|
360
|
+
|
|
361
|
+
// Get input value from URL params
|
|
362
|
+
let paramValue = null
|
|
363
|
+
if (urlParams.has(input.name)) {
|
|
364
|
+
paramValue = urlParams.get(input.name);
|
|
365
|
+
} else if (input.alias) {
|
|
366
|
+
// Handle alias as either a string or an array of strings
|
|
367
|
+
if (Array.isArray(input.alias)) {
|
|
368
|
+
for (let alias of input.alias) {
|
|
369
|
+
if (urlParams.has(alias)) {
|
|
370
|
+
paramValue = urlParams.get(alias);
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
} else if (typeof input.alias === 'string' && urlParams.has(input.alias)) {
|
|
375
|
+
paramValue = urlParams.get(input.alias);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
log(`Param value for ${input.name}:`, paramValue)
|
|
379
|
+
|
|
380
|
+
// Set input value from URL param with type conversion
|
|
381
|
+
if (paramValue !== null) {
|
|
382
|
+
switch (input.type) {
|
|
383
|
+
case 'number':
|
|
384
|
+
paramValue = Number(paramValue);
|
|
385
|
+
break;
|
|
386
|
+
case 'boolean':
|
|
387
|
+
paramValue = paramValue === 'true';
|
|
388
|
+
break;
|
|
389
|
+
case 'json':
|
|
390
|
+
try {
|
|
391
|
+
paramValue = JSON.parse(paramValue);
|
|
392
|
+
} catch (e) {
|
|
393
|
+
console.error(`Failed to parse JSON for input ${input.name}:`, e);
|
|
394
|
+
}
|
|
395
|
+
break;
|
|
396
|
+
default:
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
input.default = paramValue
|
|
400
|
+
}
|
|
356
401
|
})
|
|
357
402
|
log('Inputs are:', this.schema.inputs)
|
|
358
403
|
}
|
package/templates/bulma-app.vue
CHANGED
|
@@ -26,6 +26,21 @@
|
|
|
26
26
|
justify-content: center;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
.column {
|
|
30
|
+
box-sizing: border-box;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.card {
|
|
34
|
+
box-sizing: content-box;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
#inputs .column {
|
|
38
|
+
padding: 0 10px;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
#outputs .column {
|
|
42
|
+
padding: 0 10px;
|
|
43
|
+
}
|
|
29
44
|
|
|
30
45
|
.card-header {
|
|
31
46
|
box-shadow: none;
|
|
@@ -140,16 +155,24 @@
|
|
|
140
155
|
<div class="column" v-bind:class="($parent.design && $parent.design.grid && ($parent.design.grid.length > 0)) ? 'is-' + $parent.design.grid[0] : ''">
|
|
141
156
|
<!-- Inputs -->
|
|
142
157
|
<div class="card bordered">
|
|
143
|
-
<div class="card-content" id="inputs" v-if="$parent.inputs && $parent.inputs.length > 0">
|
|
144
|
-
<
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
158
|
+
<div class="card-content columns is-multiline" id="inputs" v-if="$parent.inputs && $parent.inputs.length > 0">
|
|
159
|
+
<div
|
|
160
|
+
v-for="(input, index) in $parent.inputs"
|
|
161
|
+
:key="index"
|
|
162
|
+
v-bind:class="[
|
|
163
|
+
'column',
|
|
164
|
+
input.columns ? 'is-' + input.columns : 'is-12',
|
|
165
|
+
input.columnsMobile ? 'is-' + input.columnsMobile + '-mobile' : '',
|
|
166
|
+
input.columnsTablet ? 'is-' + input.columnsTablet + '-tablet' : '',
|
|
167
|
+
input.columnsDesktop ? 'is-' + input.columnsDesktop + '-desktop' : '',
|
|
168
|
+
]"
|
|
169
|
+
>
|
|
170
|
+
<vue-input
|
|
171
|
+
v-bind:input="input"
|
|
172
|
+
v-if="input.display !== false && $parent.display(index)"
|
|
173
|
+
v-on:inchange="$parent.run()"
|
|
174
|
+
></vue-input>
|
|
175
|
+
</div>
|
|
153
176
|
<pre v-if="$parent.model.debug">{{ $parent.inputs }}</pre>
|
|
154
177
|
<!-- <button class="button is-primary" id="run"><span>▸</span> Run</button> -->
|
|
155
178
|
</div>
|
|
@@ -188,8 +211,23 @@
|
|
|
188
211
|
<div class="column" id="outputs" v-bind:class="($parent.design && $parent.design.grid && ($parent.design.grid.length > 1)) ? 'is-' + $parent.design.grid[1] : ''">
|
|
189
212
|
<!-- Outputs -->
|
|
190
213
|
<div v-if="$parent.outputs">
|
|
191
|
-
<div
|
|
192
|
-
<
|
|
214
|
+
<div class="columns is-multiline">
|
|
215
|
+
<div
|
|
216
|
+
v-for="(output, index) in $parent.outputs"
|
|
217
|
+
:key="index"
|
|
218
|
+
v-bind:class="[
|
|
219
|
+
'column',
|
|
220
|
+
output.columns ? 'is-' + output.columns : 'is-12',
|
|
221
|
+
output.columnsMobile ? 'is-' + output.columnsMobile + '-mobile' : '',
|
|
222
|
+
output.columnsTablet ? 'is-' + output.columnsTablet + '-tablet' : '',
|
|
223
|
+
output.columnsDesktop ? 'is-' + output.columnsDesktop + '-desktop' : '',
|
|
224
|
+
]"
|
|
225
|
+
>
|
|
226
|
+
<vue-output
|
|
227
|
+
v-bind:output="output"
|
|
228
|
+
v-on:notification="$parent.notify($event)"
|
|
229
|
+
></vue-output>
|
|
230
|
+
</div>
|
|
193
231
|
</div>
|
|
194
232
|
<pre v-if="$parent.model.debug">{{ $parent.outputs }}</pre>
|
|
195
233
|
</div>
|
|
@@ -4,21 +4,25 @@
|
|
|
4
4
|
<p class="card-header-title is-size-6" v-if="output.name">
|
|
5
5
|
{{ output.name }}
|
|
6
6
|
</p>
|
|
7
|
+
<!-- <p class="card-header-icon" v-if="output.type != 'function'"> -->
|
|
7
8
|
<p class="card-header-icon">
|
|
8
9
|
<button class="button is-small" v-on:click="save()">Save</button>
|
|
9
10
|
<button class="button is-small" v-on:click="copy()">Copy</button>
|
|
10
11
|
</p>
|
|
11
12
|
</header>
|
|
12
13
|
<div class="card-content">
|
|
13
|
-
<div class="content" v-if="output.type == 'svg'">
|
|
14
|
+
<div class="content" v-if="(output.type == 'svg') || (output.type == 'html')">
|
|
14
15
|
<div v-html="output.value"></div>
|
|
15
16
|
</div>
|
|
16
17
|
<div class="content" v-else-if="output.type == 'object'">
|
|
17
18
|
<json-viewer :value="output.value" copyable sort />
|
|
18
19
|
</div>
|
|
19
|
-
<div class="content" v-if="output.type == 'code'">
|
|
20
|
+
<div class="content" v-else-if="output.type == 'code'">
|
|
20
21
|
<pre>{{ output.value }}</pre>
|
|
21
22
|
</div>
|
|
23
|
+
<div class="content" v-else-if="output.type == 'function'">
|
|
24
|
+
<div ref="customContainer"></div>
|
|
25
|
+
</div>
|
|
22
26
|
<div class="content" v-else>
|
|
23
27
|
<pre>{{ output.value }}</pre>
|
|
24
28
|
</div>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { saveAs } from 'file-saver'
|
|
2
|
+
import domtoimage from 'dom-to-image'
|
|
2
3
|
|
|
3
4
|
const Blob = window['Blob']
|
|
4
5
|
|
|
@@ -11,27 +12,82 @@ function stringify (v) {
|
|
|
11
12
|
const component = {
|
|
12
13
|
props: ['output'],
|
|
13
14
|
emits: ['notification'],
|
|
15
|
+
mounted() {
|
|
16
|
+
this.executeRenderFunction()
|
|
17
|
+
},
|
|
18
|
+
updated() {
|
|
19
|
+
this.executeRenderFunction()
|
|
20
|
+
},
|
|
21
|
+
computed: {
|
|
22
|
+
isRenderFunction() {
|
|
23
|
+
return typeof this.output.value === 'function'
|
|
24
|
+
}
|
|
25
|
+
},
|
|
14
26
|
methods: {
|
|
15
27
|
save () {
|
|
16
28
|
// Prepare filename
|
|
17
29
|
let filename
|
|
30
|
+
let extension
|
|
18
31
|
if (this.output.filename) {
|
|
19
32
|
filename = this.output.filename
|
|
20
33
|
} else {
|
|
21
34
|
let name = this.output.name ? this.output.name : 'output'
|
|
22
|
-
|
|
35
|
+
switch (this.output.type) {
|
|
36
|
+
case 'function':
|
|
37
|
+
extension = 'png'
|
|
38
|
+
break
|
|
39
|
+
case 'svg':
|
|
40
|
+
extension = 'svg'
|
|
41
|
+
break
|
|
42
|
+
default:
|
|
43
|
+
extension = 'txt'
|
|
44
|
+
}
|
|
23
45
|
filename = name + '.' + extension
|
|
24
46
|
}
|
|
25
47
|
|
|
26
48
|
// Prepare blob
|
|
49
|
+
if (this.output.type === 'function') {
|
|
50
|
+
domtoimage.toBlob(this.$refs.customContainer)
|
|
51
|
+
.then(blob => {
|
|
52
|
+
saveAs(blob, filename)
|
|
53
|
+
})
|
|
54
|
+
}
|
|
27
55
|
let value = stringify(this.output.value)
|
|
28
56
|
let blob = new Blob([value], {type: 'text/plain;charset=utf-8'})
|
|
29
57
|
saveAs(blob, filename)
|
|
30
58
|
},
|
|
31
59
|
copy () {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
60
|
+
if (this.output.type === 'function') {
|
|
61
|
+
// Copy the image to the clipboard
|
|
62
|
+
domtoimage.toBlob(this.$refs.customContainer)
|
|
63
|
+
.then(blob => {
|
|
64
|
+
const item = new ClipboardItem({ [blob.type]: blob });
|
|
65
|
+
navigator.clipboard.write([item])
|
|
66
|
+
.then(() => {
|
|
67
|
+
this.$emit('notification', 'Image copied to clipboard');
|
|
68
|
+
})
|
|
69
|
+
.catch(err => {
|
|
70
|
+
console.error('Failed to copy image: ', err);
|
|
71
|
+
this.$emit('notification', 'Failed to copy image');
|
|
72
|
+
});
|
|
73
|
+
})
|
|
74
|
+
.catch(err => {
|
|
75
|
+
console.error('Failed to generate image blob: ', err);
|
|
76
|
+
this.$emit('notification', 'Failed to generate image');
|
|
77
|
+
});
|
|
78
|
+
} else {
|
|
79
|
+
let value = stringify(this.output.value)
|
|
80
|
+
navigator.clipboard.writeText(value)
|
|
81
|
+
this.$emit('notification', 'Copied')
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
executeRenderFunction() {
|
|
85
|
+
if (this.isRenderFunction && this.$refs.customContainer) {
|
|
86
|
+
// Clear previous content
|
|
87
|
+
this.$refs.customContainer.innerHTML = ''
|
|
88
|
+
// Execute the render function with the container
|
|
89
|
+
this.output.value(this.$refs.customContainer)
|
|
90
|
+
}
|
|
35
91
|
}
|
|
36
92
|
},
|
|
37
93
|
}
|
package/CHANGELOG.md
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## 0.3.1
|
|
4
|
-
- [x] Add `download` method to jsee object
|
|
5
|
-
- [x] Add `bin` folder with `cmd.js` for easier project building
|
|
6
|
-
|
|
7
|
-
## 0.2.9
|
|
8
|
-
- [x] Add examples
|
|
9
|
-
- [x] Add imports
|
|
10
|
-
- [x] Add `caller` field to the model input (can be: `run`, `autorun` or a button name)
|
|
11
|
-
- [x] Add `title` field (for buttons rn)
|
|
12
|
-
- [x] If `display` field is `false` the input is not shown
|
|
13
|
-
- [x] If `autorun` is true, then actually autorun the model initially
|
|
14
|
-
|
|
15
|
-
## 0.2.8
|
|
16
|
-
- [x] Fix no input case
|
|
17
|
-
|
|
18
|
-
## 0.2.7
|
|
19
|
-
- [x] Show output when result is `0`
|
|
20
|
-
- [x] Updated style for buttons and inputs
|
|
21
|
-
|
|
22
|
-
## 0.2.6
|
|
23
|
-
- [x] Tests
|
|
24
|
-
- [x] Load schema from query (loader)
|
|
25
|
-
- [x] Reset button appears only after data change
|
|
26
|
-
- [x] Default input type (`string`)
|
|
27
|
-
- [x] Directly load code when running in a window (not code to text)
|
|
28
|
-
- [x] Passing code directly
|
|
29
|
-
|
|
30
|
-
|