pinokiod 3.8.0 → 3.8.5

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.
Files changed (51) hide show
  1. package/kernel/api/fs/index.js +6 -4
  2. package/kernel/api/index.js +102 -31
  3. package/kernel/bin/cuda.js +7 -0
  4. package/kernel/bin/index.js +143 -4
  5. package/kernel/bin/node.js +9 -0
  6. package/kernel/index.js +32 -1
  7. package/kernel/info.js +1 -0
  8. package/kernel/prototype.js +47 -0
  9. package/kernel/shell.js +110 -54
  10. package/kernel/shells.js +16 -7
  11. package/kernel/store.js +58 -0
  12. package/kernel/util.js +106 -35
  13. package/package.json +1 -1
  14. package/script/index.js +12 -0
  15. package/server/index.js +466 -250
  16. package/server/public/SpaceMono-Bold.ttf +0 -0
  17. package/server/public/SpaceMono-BoldItalic.ttf +0 -0
  18. package/server/public/SpaceMono-Italic.ttf +0 -0
  19. package/server/public/SpaceMono-Regular.ttf +0 -0
  20. package/server/public/filepond-plugin-file-validate-type.min.js +9 -0
  21. package/server/public/filepond-plugin-image-crop.min.js +9 -0
  22. package/server/public/filepond-plugin-image-edit.min.css +8 -0
  23. package/server/public/filepond-plugin-image-edit.min.js +9 -0
  24. package/server/public/filepond-plugin-image-exif-orientation.min.js +9 -0
  25. package/server/public/filepond-plugin-image-preview.min.css +8 -0
  26. package/server/public/filepond-plugin-image-preview.min.js +9 -0
  27. package/server/public/filepond-plugin-image-resize.min.js +9 -0
  28. package/server/public/filepond-plugin-image-transform.min.js +9 -0
  29. package/server/public/filepond.min.css +8 -0
  30. package/server/public/filepond.min.js +9 -0
  31. package/server/public/fscreator.js +160 -0
  32. package/server/public/fseditor.js +189 -0
  33. package/server/public/github.png +0 -0
  34. package/server/public/opener.js +3 -1
  35. package/server/public/pinokio-black.png +0 -0
  36. package/server/public/pinokio-black2.png +0 -0
  37. package/server/public/style.css +131 -9
  38. package/server/socket.js +123 -37
  39. package/server/views/app.ejs +184 -11
  40. package/server/views/editor.ejs +12 -2
  41. package/server/views/explore.ejs +4 -3
  42. package/server/views/help.ejs +4 -2
  43. package/server/views/index.ejs +218 -80
  44. package/server/views/install.ejs +1 -1
  45. package/server/views/local_editor.ejs +282 -0
  46. package/server/views/minterm.ejs +687 -0
  47. package/server/views/partials/menu.ejs +7 -1
  48. package/server/views/prototypes.ejs +463 -0
  49. package/server/views/proxy.ejs +10 -4
  50. package/server/views/settings.ejs +4 -3
  51. package/server/views/terminal.ejs +99 -15
@@ -0,0 +1,687 @@
1
+ <html>
2
+ <head>
3
+ <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
4
+ <script src="/hotkeys.min.js"></script>
5
+ <script src="/ace/ace.js"></script>
6
+ <script src="/noty.js"></script>
7
+ <script src="/notyq.js"></script>
8
+ <script src="/xterm.js"></script>
9
+ <script src="/xterm-addon-fit.js"></script>
10
+ <script src="/xterm-addon-web-links.js"></script>
11
+ <script src="/xterm-theme.js"></script>
12
+ <script src="/xterm-addon-search.js"></script>
13
+ <script src="/xterm-addon-search-bar.js"></script>
14
+ <script src="/sweetalert2.js"></script>
15
+ <script src="/Socket.js"></script>
16
+ <script src="/common.js"></script>
17
+ <script src="/he.js"></script>
18
+ <script src="/opener.js"></script>
19
+ <!--
20
+ <script src="/nav.js"></script>
21
+ -->
22
+ <script src="/autoexpand.js"></script>
23
+ <script src="/modalinput.js"></script>
24
+ <script src="/simplemodal.js"></script>
25
+ <script src="/waitmodal.js"></script>
26
+ <script src="/notifyinput.js"></script>
27
+ <script src="/report.js"></script>
28
+ <script src="/mark.min.js"></script>
29
+ <link href="/xterm.min.css" rel="stylesheet" />
30
+ <link href="/css/fontawesome.min.css" rel="stylesheet">
31
+ <link href="/css/solid.min.css" rel="stylesheet">
32
+ <link href="/css/regular.min.css" rel="stylesheet">
33
+ <link href="/css/brands.min.css" rel="stylesheet">
34
+ <link href="/style.css" rel="stylesheet"/>
35
+ <link href="/noty.css" rel="stylesheet"/>
36
+ <!--
37
+ <link rel="preconnect" href="https://fonts.googleapis.com">
38
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
39
+ <link href="https://fonts.googleapis.com/css2?family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet">
40
+ <link rel="preconnect" href="https://fonts.googleapis.com">
41
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
42
+ <link href="https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet">
43
+ -->
44
+
45
+
46
+ <% if (agent === "electron") { %>
47
+ <link href="/electron.css" rel="stylesheet"/>
48
+ <% } %>
49
+ <style>
50
+ .stopped-btn {
51
+ background: silver;
52
+ }
53
+ html {
54
+ scroll-behavior: smooth;
55
+ height: 100%;
56
+ overflow: hidden;
57
+ }
58
+ body {
59
+ height: 100%;
60
+ overflow: hidden;
61
+ display: flex;
62
+ flex-direction: column;
63
+ }
64
+ #table > table {
65
+ border-left: 2px solid black;
66
+ padding-left: 5px;
67
+ margin: 10px 0;
68
+ }
69
+ .highlight {
70
+ background: yellow;
71
+ width: 100px;
72
+ }
73
+ th {
74
+ background: rgba(0,0,0,0.1);
75
+ padding: 10px;
76
+ font-size: 12px;
77
+ }
78
+ td {
79
+ font-size: 12px;
80
+ padding: 10px;
81
+ }
82
+ td.val {
83
+ background: rgba(0,0,0,0.06);
84
+ }
85
+ td.key {
86
+ background: rgba(0,0,0,0.1);
87
+ }
88
+ .button {
89
+ cursor: pointer;
90
+ background: rgba(0,0,0,0.8);
91
+ color: white;
92
+ padding: 5px 10px;
93
+ border-radius: 4px;
94
+ }
95
+ header {
96
+ position: relative;
97
+ padding: 10px 0;
98
+ }
99
+ .button:hover {
100
+
101
+ }
102
+ .terminal {
103
+ box-sizing: border-box;
104
+ }
105
+ .xterm .xterm-viewport {
106
+ width: initial !important;
107
+ }
108
+ /*
109
+ .navheader {
110
+ background: var(--dark-bg);
111
+ }
112
+ */
113
+ /*
114
+ .btn {
115
+ background: royalblue;
116
+ }
117
+ */
118
+ #del-bin {
119
+ color: royalblue;
120
+ cursor: pointer;
121
+ font-weight: bold;
122
+ padding: 0 5px;
123
+ text-decoration: underline;
124
+ }
125
+ body.frozen {
126
+ overflow: auto !important;
127
+ }
128
+ </style>
129
+ <link href="/terminal.css" rel="stylesheet"/>
130
+ <script>
131
+ let shell_id
132
+ document.addEventListener("DOMContentLoaded", async () => {
133
+ const n = new N()
134
+ class RPC {
135
+ constructor() {
136
+ this.socket = new Socket()
137
+ this.buffer = []
138
+ }
139
+ write(text) {
140
+ if (text !== "\u0007") {
141
+ this.term.write(text)
142
+ }
143
+ this.dirty = true
144
+ }
145
+ finished() {
146
+ /*
147
+ n.Noty({
148
+ text: `[Success] All steps complete`,
149
+ })
150
+ */
151
+ }
152
+ stop() {
153
+ this.socket.run({
154
+ method: "kernel.bin.shell_kill",
155
+ params: {
156
+ id: shell_id
157
+ }
158
+ }, (stream) => {
159
+ })
160
+ }
161
+ save() {
162
+ return new Promise((resolve, reject) => {
163
+ let cwd = "~" + location.pathname
164
+
165
+ this.socket.close()
166
+ this.socket.run({
167
+ method: "fs.write",
168
+ params: {
169
+ path: cwd,
170
+ text: str,
171
+ // string: str
172
+ // json: instructions
173
+ }
174
+ }, (packet) => {
175
+ if (packet.type === "result" && packet.id === "fs.write") {
176
+ dirty = false
177
+ document.querySelector("#save").classList.add("disabled")
178
+ resolve()
179
+ }
180
+ })
181
+ })
182
+ }
183
+ start(mode) {
184
+ return new Promise(async (resolve, reject) => {
185
+ // await this.save()
186
+ await this.socket.close()
187
+
188
+ let query = Object.fromEntries(new URLSearchParams(location.search))
189
+ console.log(">>>>>>>> 1")
190
+
191
+ this.socket.run({
192
+ method: "kernel.bin.shell_start",
193
+ input: true,
194
+ id: "<%=id%>",
195
+ // params: {
196
+ path: "<%=cwd%>",
197
+ // },
198
+ client: {
199
+ cols: this.term.cols,
200
+ rows: this.term.rows,
201
+ }
202
+ }, async (packet) => {
203
+ console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ", { packet })
204
+ if (packet.type === 'start') {
205
+ refreshParent(packet)
206
+ if (packet.data && packet.data.description) {
207
+ if ('current' in packet.data) {
208
+ document.querySelector("footer").innerHTML = `<b>
209
+ <i class="fa-solid fa-circle-notch fa-spin"></i>(${packet.data.current+1}/${packet.data.total}) ${packet.data.title ? packet.data.title : ''}
210
+ </b>
211
+ <div class='flexible content'>${packet.data.description}</div>`
212
+ // <div class='toggle-expand'>
213
+ // <i class="fa-solid fa-circle-chevron-up"></i>
214
+ // </div>`
215
+ } else {
216
+ document.querySelector("footer").innerHTML = `<b>
217
+ <i class="fa-solid fa-circle-notch fa-spin"></i> ${packet.data.title ? packet.data.title : ''}
218
+ </b>
219
+ <div class='flexible content'></div>`
220
+ // <div class='toggle-expand'>
221
+ // <i class="fa-solid fa-circle-chevron-up"></i>
222
+ // </div>`
223
+ }
224
+ } else if (packet.data && packet.data.method) {
225
+ document.querySelector("footer").innerHTML = `<b>
226
+ <i class="fa-solid fa-circle-notch fa-spin"></i> (${packet.data.current+1}/${packet.data.total}) ${packet.data.method}
227
+ </b>
228
+ <div class='flexible content'>${packet.data.params ? JSON.stringify(packet.data.params) : ''}</div>`
229
+ // <div class='toggle-expand'>
230
+ // <i class="fa-solid fa-circle-chevron-up"></i>
231
+ // </div>`
232
+ }
233
+ } else if (packet.type === "stream") {
234
+ console.log(">>>>>>>> 3")
235
+ // set the current shell id
236
+ if (packet.data.id) {
237
+ shell_id = packet.data.id
238
+ }
239
+ if (packet.data.raw) {
240
+ // console.log({ packet })
241
+ this.write(packet.data.raw)
242
+ } else if (packet.data.json) {
243
+ this.write(JSON.stringify(packet.data.json).replace(/\n/g, "\r\n"))
244
+ this.write("\r\n")
245
+ } else if (packet.data.json2) {
246
+ this.write(JSON.stringify(packet.data.json2, null, 2).replace(/\n/g, "\r\n"))
247
+ this.write("\r\n")
248
+ }
249
+ } else if (packet.type === 'disconnect') {
250
+ refreshParent(packet)
251
+ this.term.write("\r\nDisconnected...\r\n")
252
+ document.querySelector("footer").innerHTML = "<b>Ready</b>"
253
+ this.socket.close()
254
+ } else if (packet.type === 'connect') {
255
+ if (packet.data) {
256
+ if (packet.data.shell) {
257
+ shell_id = packet.data.shell
258
+ }
259
+ if (packet.data.state) {
260
+ this.write(packet.data.state)
261
+ }
262
+ }
263
+ /*
264
+ for(let i=0; i<message.length; i++) {
265
+ await new Promise((r, reject) => {
266
+ setTimeout(()=> {
267
+ this.term.write(message[i])
268
+ r()
269
+ }, 20)
270
+ })
271
+ }
272
+ */
273
+ console.log(">>>>>>>> 2")
274
+ // } else if (packet.type === "key.set") {
275
+ // let keys = await fetch("/pinokio/keys").then((res) => {
276
+ // return res.json()
277
+ // })
278
+ // let input = await ModalInput({
279
+ // title: "Key",
280
+ // description: "select a key",
281
+ // form: [{
282
+ // key: "key",
283
+ // type: "select",
284
+ // items: keys,
285
+ // }, {
286
+ // key: "password",
287
+ // type: "password",
288
+ // }]
289
+ // })
290
+ // if (input) {
291
+ // this.socket.respond({
292
+ // response: input,
293
+ // uri: "~" + location.pathname,
294
+ // })
295
+ // }
296
+ } else if (packet.type === 'wait.end') {
297
+ try {
298
+ Swal.close()
299
+ } catch (e) {
300
+ }
301
+ this.socket.respond({
302
+ response: {},
303
+ // uri: "~" + location.pathname,
304
+ uri: packet.id
305
+ })
306
+ } else if (packet.type === 'wait') {
307
+ await WaitModal(packet.data)
308
+ } else if (packet.type === "modal") {
309
+ await SimpleModal(packet.data)
310
+ this.socket.respond({
311
+ response: {},
312
+ //uri: "~" + location.pathname,
313
+ uri: packet.id
314
+ })
315
+ } else if (packet.type === "input") {
316
+ let params = packet.data
317
+ let type = (params.type ? params.type : "modal")
318
+ let input
319
+ if (type === "modal") {
320
+ input = await ModalInput(packet.data)
321
+ } else if (type === "notify") {
322
+ input = await NotifyInput(packet.data, n)
323
+ }
324
+ if (input) {
325
+ this.socket.respond({
326
+ response: input,
327
+ uri: packet.id
328
+ // uri: "~" + location.pathname,
329
+ // uri: (packet.caller ? packet.caller : packet.id)
330
+ })
331
+ }
332
+ /*
333
+ params := {
334
+ title,
335
+ description,
336
+ form: [{
337
+ title,
338
+ description
339
+ }]
340
+ }
341
+ */
342
+ } else if (packet.type === "tab.open") {
343
+ refreshParent(packet)
344
+ } else if (packet.type === "browser.open") {
345
+ /*
346
+ {
347
+ "method": "browser.open",
348
+ "params": {
349
+ "uri",
350
+ "target": "_self (default)"|"_blank"|"_parent"|"_top",
351
+ "features"
352
+ }
353
+ }
354
+ */
355
+
356
+ ///// DEPRECATE browser.open : Pinokio IS the browser. Everything opens in Pinokio. May bring back when necessary
357
+ // refreshParent()
358
+ // let params = packet.data
359
+ // window.open(
360
+ // params.uri,
361
+ // (params.target || "_self"),
362
+ // params.features
363
+ // )
364
+ } else if (packet.type === "browser.close") {
365
+ let params = packet.data
366
+ if (params && params.target) {
367
+ let targetWindow = window.open('', params.target)
368
+ if (targetWindow) {
369
+ targetWindow.close();
370
+ }
371
+ } else {
372
+ window.close()
373
+ }
374
+ } else if (packet.type === "browser") {
375
+ /*
376
+ {
377
+ "method": "browser.close"
378
+ }
379
+ */
380
+ let { method, params } = packet.data
381
+ let resolved = window
382
+ let tokens = method.split(".")
383
+ for(let token of tokens) {
384
+ resolved = resolved[token]
385
+ }
386
+ let result = resolved(...params)
387
+ } else if (packet.type === "notify") {
388
+ /*
389
+ {
390
+ html: <notification html>,
391
+ href: <link location to open>,
392
+ target: <target for window.open()>,
393
+ features: <windowFeatures>, ("self" => opens in pinokio)
394
+ }
395
+ */
396
+ if (packet.data) {
397
+ let payload = {}
398
+ if (packet.data.html) {
399
+ payload.text = packet.data.html
400
+ }
401
+ if (packet.data.type) {
402
+ payload.type = packet.data.type
403
+ }
404
+ payload.silent = packet.data.silent
405
+ payload.callbacks = {
406
+ onClick: () => {
407
+ let params = packet.data
408
+ if (params.href || params.uri) {
409
+ window.open(
410
+ params.uri || params.href,
411
+ (params.target || "_self"),
412
+ params.features
413
+ )
414
+ } else if (params.action === "close") {
415
+ if (params && params.target) {
416
+ let targetWindow = window.open('', params.target)
417
+ if (targetWindow) {
418
+ targetWindow.close();
419
+ }
420
+ } else {
421
+ window.close()
422
+ }
423
+ }
424
+ }
425
+ }
426
+ n.Noty(payload)
427
+ }
428
+ } else if (packet.type === "result") {
429
+ refreshParent(packet)
430
+ } else if (packet.type === "info") {
431
+ n.Noty({
432
+ text: `${packet.data}`,
433
+ })
434
+ } else if (packet.type === "error") {
435
+ // n.Noty({
436
+ // text: `${packet.data}`,
437
+ // type: "error"
438
+ // })
439
+
440
+ // this.socket.close()
441
+ // refreshParent(packet)
442
+
443
+
444
+
445
+ //document.querySelector("footer .content").innerHTML = document.querySelector("footer .content").innerHTML + `<pre class='error'>${packet.data}</pre>`
446
+ document.querySelector("footer .content").innerHTML = document.querySelector("footer .content").innerHTML + `<pre class='error'>ERROR</pre>`
447
+ // document.querySelector("footer .toggle-expand i").className = "fa-solid fa-circle-chevron-down"
448
+ // document.querySelector("footer").classList.add("expanded")
449
+ document.querySelector("footer i").className = "fa-solid fa-triangle-exclamation"
450
+
451
+ document.querySelector("#error-screen").classList.remove("hidden")
452
+ document.querySelector("#error-screen pre").textContent = packet.data
453
+ let instance = new Mark(document.querySelector("#error-screen pre"), {
454
+ separateWordSearch: false,
455
+ accuracy: "exactly"
456
+ })
457
+ /*
458
+ document.querySelector("#error-screen").addEventListener("click", (e) => {
459
+ document.querySelector("#error-screen").classList.add("hidden")
460
+ })
461
+ */
462
+ instance.mark(packet.event)
463
+ let element = document.querySelector("mark")
464
+ element.scrollIntoView({ behavior: "smooth", block: "center" });
465
+
466
+
467
+
468
+
469
+ } else if (packet.type === "event") {
470
+ if (packet.data === "stop") {
471
+ document.querySelector("footer").innerHTML = "<b>Ready</b>"
472
+ setTimeout(() => {
473
+
474
+ let params = new URLSearchParams(location.search)
475
+ let entries = [...params.entries()]
476
+ let options;
477
+ if (entries.length > 0) {
478
+ options = {}
479
+ for(const [key, value] of entries) {
480
+ options[key] = value;
481
+ }
482
+ }
483
+ if (options && options.html) {
484
+ n.Noty({
485
+ text: options.html,
486
+ timeout: 3000,
487
+ callbacks: {
488
+ onClose: () => {
489
+ let uri = options.uri || options.href
490
+ if (uri) {
491
+ let target = options.target || "_self"
492
+ let features = options.features
493
+ window.open(uri, target, features)
494
+ } else if (options.action === "close") {
495
+ window.close()
496
+ }
497
+ }
498
+ }
499
+ })
500
+ }
501
+ /*
502
+ n.Noty({
503
+ text: '[Notice] finished',
504
+ type: 'success'
505
+ })
506
+ */
507
+ }, 0)
508
+ //this.socket.close()
509
+ }
510
+ }
511
+
512
+ // Get the available space for the terminal
513
+ // let terminalContainer = document.querySelector("#terminal")
514
+ // const availableHeight = terminalContainer.clientHeight;
515
+ //
516
+ // // Calculate the number of columns and rows the terminal should have
517
+ // console.log(this.term)
518
+ // //const rows = Math.floor(availableHeight / this.term._core.viewport._charSizeService.height);
519
+ // const rows = Math.floor(this.term._core.viewport._lastRecordedViewportHeight / this.term._core.viewport._charSizeService.height);
520
+ //
521
+ //
522
+ // // Resize the terminal
523
+ // this.term.resize(this.term.cols, rows);
524
+ // this.term.scrollToLine(this.term.rows-1);
525
+ // this.fit.fit()
526
+ // let pageBottom = document.querySelector("#end")
527
+ // pageBottom.scrollIntoView()
528
+ })
529
+ })
530
+ }
531
+ async run (mode) {
532
+ this.mode = (mode ? mode : "run")
533
+
534
+ // if (dirty) {
535
+ // await this.save()
536
+ // n.Noty({
537
+ // text: `script updated`,
538
+ // })
539
+ // }
540
+
541
+ await this.createTerm()
542
+ await this.start(mode)
543
+ }
544
+ async createTerm () {
545
+ console.log(xtermTheme)
546
+ if (!this.term) {
547
+ const theme = Object.assign({ }, xtermTheme.Terminal_Basic, {
548
+ selectionBackground: "red",
549
+ selectionForeground: "white"
550
+ })
551
+
552
+ let config = {
553
+ scrollback: 9999999,
554
+ //fontSize: 12,
555
+ fontSize: 14,
556
+ theme,
557
+ //theme: xtermTheme.FrontEndDelight
558
+ //theme: xtermTheme.Afterglow
559
+
560
+ //theme: xtermTheme.PencilLight
561
+
562
+ //theme: xtermTheme.Flatland
563
+ //theme: xtermTheme.OneHalfLight
564
+ //theme: xtermTheme.Piatto_Light
565
+ //theme: xtermTheme.Spring
566
+ //theme: xtermTheme.Terminal_Basic
567
+ //theme: xtermTheme.Tomorrow
568
+ }
569
+ let res = await fetch("/xterm_config").then((res) => {
570
+ return res.json()
571
+ })
572
+ if (res && res.config) {
573
+ config = res.config
574
+ }
575
+ const term = new Terminal(config)
576
+ term.open(document.querySelector("#terminal"))
577
+ term.attachCustomKeyEventHandler(event => {
578
+ console.log({ event })
579
+ if ((event.ctrlKey || event.metaKey) && event.key === 'c') {
580
+ const selection = term.getSelection();
581
+ if (selection) {
582
+ navigator.clipboard.writeText(selection);
583
+ return false;
584
+ }
585
+ }
586
+ if ((event.ctrlKey || event.metaKey) && event.key === 'v') {
587
+ navigator.clipboard.readText().then((text) => {
588
+ term.write(text);
589
+ })
590
+ return false
591
+ }
592
+ return true;
593
+ });
594
+ //const rows = Math.floor(document.querySelector("#terminal").clientHeight / term._core.viewport._charSizeService.height);
595
+ //const cols = Math.floor(document.querySelector("#terminal").clientHeight / term._core.viewport._charSizeService.width);
596
+ //console.log(cols, term.cols)
597
+ // // Resize the terminal
598
+ ////term.resize(cols, rows-5);
599
+ //term.resize(cols, rows);
600
+
601
+ term.onKey(({ key }) => {
602
+ console.log({ key, shell_id })
603
+ if (this.socket) {
604
+ if (shell_id) {
605
+ this.socket.run({
606
+ key,
607
+ id: shell_id
608
+ })
609
+ }
610
+ }
611
+ });
612
+
613
+ const fitAddon = new FitAddon.FitAddon();
614
+ // const searchAddon = new SearchAddon.SearchAddon();
615
+ // const searchAddonBar = new SearchBarAddon.SearchBarAddon({searchAddon});
616
+ term.loadAddon(fitAddon);
617
+ <% if (agent === "electron") { %>
618
+ term.loadAddon(new WebLinksAddon.WebLinksAddon((event, uri) => {
619
+ window.open(uri, "_blank")
620
+ }))
621
+ <% } else { %>
622
+ term.loadAddon(new WebLinksAddon.WebLinksAddon());
623
+ <% } %>
624
+ // term.loadAddon(searchAddon)
625
+ // term.loadAddon(searchAddonBar);
626
+ // searchAddonBar.show();
627
+ fitAddon.fit();
628
+ this.term = term
629
+ this.fit = fitAddon
630
+
631
+ }
632
+ this.term.focus()
633
+ }
634
+ }
635
+ const rpc = new RPC()
636
+ document.querySelector(".stop").addEventListener("click", async (e) => {
637
+ await rpc.stop()
638
+ // socket.run({
639
+ // id: location.pathname,
640
+ // method: "kernel.api.stop"
641
+ // }, (stream) => {
642
+ // console.log("#", stream)
643
+ // })
644
+ /*
645
+ document.querySelector(".run .play").classList.remove("hidden")
646
+ document.querySelector(".run .starting").classList.add("hidden")
647
+ */
648
+ document.querySelector(".stop-btn").classList.add("hidden")
649
+ document.querySelector(".stopped-btn").classList.remove("hidden")
650
+ })
651
+
652
+ window.addEventListener('message', function(event) {
653
+ console.log("Message received from the parent: ", event.data); // Message received from parent
654
+ // "foreground" message adds a newline to the terminal
655
+ // Needed to fix the issue where the terminal won't write anything when in background
656
+ // so when coming back from background, need to refresh by just adding a newline.
657
+ if (event.data && event.data.action === "foreground") {
658
+ // if dirty, update
659
+ // if not dirty, don't update
660
+ const delimiter = "\r\n"
661
+ if (rpc.dirty) {
662
+ rpc.term.write(delimiter)
663
+ rpc.dirty = false
664
+ } else {
665
+ }
666
+ }
667
+ });
668
+ rpc.run()
669
+ })
670
+
671
+ </script>
672
+ </head>
673
+ <body class='<%=theme%>'>
674
+ <header class='navheader2'>
675
+ <div class='btn run stop-btn'>
676
+ <span class='stop'><i class="fa-solid fa-stop"></i> Stop</span>
677
+ </div>
678
+ <div class='btn stopped-btn hidden'>
679
+ <span class='stopped'><i class="fa-solid fa-hand"></i> Stopped</span>
680
+ </div>
681
+ </header>
682
+ <div class='terminal-container'>
683
+ <div id='terminal'></div>
684
+ </div>
685
+ <footer><b>Ready</b></footer>
686
+ </body>
687
+ </html>