pinokiod 3.8.1 → 3.8.6

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