@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jseeio/jsee",
3
- "version": "0.3.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": "^2.1.0",
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
- // Relies on input check
351
- // Set default input type
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
  }
@@ -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
- <ul>
145
- <li v-for="(input, index) in $parent.inputs">
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>
151
- </li>
152
- </ul>
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>&nbsp;&nbsp;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 v-for="(output, index) in $parent.outputs">
192
- <vue-output v-bind:output="output" v-on:notification="$parent.notify($event)"></vue-output>
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
- let extension = this.output.type === 'svg' ? 'svg': 'txt'
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
- let value = stringify(this.output.value)
33
- navigator.clipboard.writeText(value)
34
- this.$emit('notification', 'Copied')
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
-