pinokiod 3.270.0 → 3.272.0

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 (56) hide show
  1. package/kernel/ansi_stream_tracker.js +115 -0
  2. package/kernel/api/app/index.js +422 -0
  3. package/kernel/api/htmlmodal/index.js +94 -0
  4. package/kernel/app_launcher/index.js +115 -0
  5. package/kernel/app_launcher/platform/base.js +276 -0
  6. package/kernel/app_launcher/platform/linux.js +229 -0
  7. package/kernel/app_launcher/platform/macos.js +163 -0
  8. package/kernel/app_launcher/platform/unsupported.js +34 -0
  9. package/kernel/app_launcher/platform/windows.js +247 -0
  10. package/kernel/bin/conda-meta.js +93 -0
  11. package/kernel/bin/conda.js +2 -4
  12. package/kernel/bin/index.js +2 -4
  13. package/kernel/index.js +9 -2
  14. package/kernel/peer.js +186 -19
  15. package/kernel/shell.js +212 -1
  16. package/package.json +1 -1
  17. package/server/index.js +491 -6
  18. package/server/public/common.js +224 -741
  19. package/server/public/create-launcher.js +754 -0
  20. package/server/public/htmlmodal.js +292 -0
  21. package/server/public/logs.js +715 -0
  22. package/server/public/resizeSync.js +117 -0
  23. package/server/public/style.css +651 -6
  24. package/server/public/tab-idle-notifier.js +34 -59
  25. package/server/public/tab-link-popover.js +7 -10
  26. package/server/public/terminal-settings.js +723 -9
  27. package/server/public/terminal_input_utils.js +72 -0
  28. package/server/public/terminal_key_caption.js +187 -0
  29. package/server/public/urldropdown.css +120 -3
  30. package/server/public/xterm-inline-bridge.js +116 -0
  31. package/server/socket.js +29 -0
  32. package/server/views/agents.ejs +1 -2
  33. package/server/views/app.ejs +55 -28
  34. package/server/views/bookmarklet.ejs +1 -1
  35. package/server/views/bootstrap.ejs +1 -0
  36. package/server/views/connect.ejs +1 -2
  37. package/server/views/create.ejs +63 -0
  38. package/server/views/editor.ejs +36 -4
  39. package/server/views/index.ejs +1 -2
  40. package/server/views/index2.ejs +1 -2
  41. package/server/views/init/index.ejs +36 -28
  42. package/server/views/install.ejs +20 -22
  43. package/server/views/layout.ejs +2 -8
  44. package/server/views/logs.ejs +155 -0
  45. package/server/views/mini.ejs +0 -18
  46. package/server/views/net.ejs +2 -2
  47. package/server/views/network.ejs +1 -2
  48. package/server/views/network2.ejs +1 -2
  49. package/server/views/old_network.ejs +1 -2
  50. package/server/views/pro.ejs +26 -23
  51. package/server/views/prototype/index.ejs +30 -34
  52. package/server/views/screenshots.ejs +1 -2
  53. package/server/views/settings.ejs +1 -20
  54. package/server/views/shell.ejs +59 -66
  55. package/server/views/terminal.ejs +118 -73
  56. package/server/views/tools.ejs +1 -2
package/kernel/peer.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const dgram = require('dgram');
2
2
  const axios = require('axios');
3
3
  const os = require('os')
4
+ const systeminformation = require('systeminformation')
4
5
  const Environment = require("./environment")
5
6
  class PeerDiscovery {
6
7
  constructor(kernel, port = 41234, message = 'ping', interval = 1000) {
@@ -12,9 +13,8 @@ class PeerDiscovery {
12
13
  this.peers = new Set();
13
14
  this.interface_addresses = []
14
15
  this.host_candidates = []
15
- this.host = this._getLocalIPAddress()
16
+ this.host = null
16
17
  this.default_port = 42000
17
- this.peers.add(this.host)
18
18
  this.router_info_cache = {}
19
19
  // this.start();
20
20
  }
@@ -85,6 +85,13 @@ class PeerDiscovery {
85
85
  this.active = false
86
86
  }
87
87
  }
88
+ // Prepare host/peer state before the rest of the kernel bootstraps
89
+ async initialize(kernel) {
90
+ await this.refreshLocalAddress()
91
+ if (kernel) {
92
+ await this.check(kernel)
93
+ }
94
+ }
88
95
  async start(kernel) {
89
96
  let env = await Environment.get(kernel.homedir, kernel)
90
97
 
@@ -576,23 +583,70 @@ class PeerDiscovery {
576
583
  _isLocalLAN(ip) {
577
584
  return this.isRFC1918(ip)
578
585
  }
579
- _getLocalIPAddress() {
580
- this.interface_addresses = this._collectInterfaceAddresses()
581
- const shareable = this.interface_addresses.filter((entry) => entry.shareable)
582
- this.host_candidates = shareable
583
- const lanCandidate = shareable.find((entry) => entry.scope === 'lan')
584
- if (lanCandidate) {
585
- return lanCandidate.address
586
+ // Refresh LAN/IP selection; keeps peers set in sync with the active address
587
+ async refreshLocalAddress() {
588
+ try {
589
+ const { host, host_candidates, interface_addresses } = await this._getLocalIPAddress()
590
+ this.interface_addresses = interface_addresses
591
+ this.host_candidates = host_candidates
592
+ if (host && this.host !== host) {
593
+ if (this.host) {
594
+ this.peers.delete(this.host)
595
+ }
596
+ this.host = host
597
+ } else if (!this.host) {
598
+ this.host = host
599
+ }
600
+ if (this.host) {
601
+ this.peers.add(this.host)
602
+ }
603
+ return this.host
604
+ } catch (err) {
605
+ console.error('peer refreshLocalAddress error', err)
606
+ if (!this.host) {
607
+ this.host = null
608
+ }
609
+ return this.host
610
+ }
611
+ }
612
+ async _getLocalIPAddress() {
613
+ const interface_addresses = await this._collectInterfaceAddresses()
614
+ const shareable = interface_addresses.filter((entry) => entry.shareable)
615
+ const host_candidates = shareable.map((entry) => ({
616
+ address: entry.address,
617
+ netmask: entry.netmask,
618
+ interface: entry.interface,
619
+ scope: entry.scope,
620
+ shareable: entry.shareable,
621
+ type: entry.type || null,
622
+ operstate: entry.operstate || null,
623
+ virtual: entry.virtual || false,
624
+ default: entry.default || false,
625
+ prefixLength: entry.prefixLength,
626
+ mac: entry.mac || null,
627
+ score: this._scoreCandidate(entry)
628
+ }))
629
+ let selectedHost = null
630
+ let bestScore = -Infinity
631
+ host_candidates.forEach((candidate, index) => {
632
+ const score = typeof candidate.score === 'number' ? candidate.score : -Infinity
633
+ if (score > bestScore) {
634
+ bestScore = score
635
+ selectedHost = candidate.address
636
+ } else if (score === bestScore && selectedHost === null) {
637
+ selectedHost = candidate.address
638
+ }
639
+ })
640
+ if (!selectedHost && shareable.length > 0) {
641
+ selectedHost = shareable[0].address
586
642
  }
587
- const cgnatCandidate = shareable.find((entry) => entry.scope === 'cgnat')
588
- if (cgnatCandidate) {
589
- return cgnatCandidate.address
643
+ if (!selectedHost && interface_addresses.length > 0) {
644
+ selectedHost = interface_addresses[0].address
590
645
  }
591
- const publicCandidate = shareable.find((entry) => entry.scope === 'public')
592
- if (publicCandidate) {
593
- return publicCandidate.address
646
+ if (!selectedHost) {
647
+ selectedHost = '127.0.0.1'
594
648
  }
595
- return shareable.length > 0 ? shareable[0].address : null
649
+ return { host: selectedHost, host_candidates, interface_addresses }
596
650
  }
597
651
  isPrivateOrCGNAT(ip) {
598
652
  return this.isRFC1918(ip) || this.isCGNAT(ip)
@@ -626,7 +680,7 @@ class PeerDiscovery {
626
680
  }
627
681
  return secondOctet >= 16 && secondOctet <= 31
628
682
  }
629
- _collectInterfaceAddresses() {
683
+ _collectInterfaceAddressesSync() {
630
684
  const interfaces = os.networkInterfaces()
631
685
  const results = []
632
686
  const seen = new Set()
@@ -653,12 +707,51 @@ class PeerDiscovery {
653
707
  interface: ifaceName,
654
708
  internal: Boolean(iface.internal),
655
709
  scope: classification.scope,
656
- shareable: classification.shareable
710
+ shareable: classification.shareable,
711
+ mac: typeof iface.mac === 'string' ? iface.mac : null
657
712
  })
658
713
  }
659
714
  }
660
715
  return results
661
716
  }
717
+ async _collectInterfaceAddresses() {
718
+ const baseEntries = this._collectInterfaceAddressesSync()
719
+ let metadata = []
720
+ try {
721
+ metadata = await systeminformation.networkInterfaces()
722
+ } catch (err) {
723
+ metadata = []
724
+ }
725
+ const metadataMap = new Map()
726
+ if (Array.isArray(metadata)) {
727
+ metadata.forEach((entry) => {
728
+ if (entry && typeof entry.iface === 'string') {
729
+ metadataMap.set(this._normalizeInterfaceName(entry.iface), entry)
730
+ }
731
+ })
732
+ }
733
+ return baseEntries.map((entry) => {
734
+ const key = this._normalizeInterfaceName(entry.interface)
735
+ const meta = key ? metadataMap.get(key) : null
736
+ const prefixLength = this._prefixLengthFromNetmask(entry.netmask)
737
+ return {
738
+ ...entry,
739
+ prefixLength,
740
+ type: meta && meta.type ? meta.type : null,
741
+ operstate: meta && meta.operstate ? meta.operstate : null,
742
+ speed: typeof meta?.speed === 'number' ? meta.speed : null,
743
+ virtual: Boolean(meta && meta.virtual),
744
+ default: Boolean(meta && meta.default),
745
+ mac: entry.mac || (meta && meta.mac) || null
746
+ }
747
+ })
748
+ }
749
+ _normalizeInterfaceName(name) {
750
+ if (!name || typeof name !== 'string') {
751
+ return ''
752
+ }
753
+ return name.trim().toLowerCase()
754
+ }
662
755
  classifyAddress(address, isInternal = false) {
663
756
  if (!address || typeof address !== 'string') {
664
757
  return { scope: 'unknown', shareable: false }
@@ -684,6 +777,80 @@ class PeerDiscovery {
684
777
  }
685
778
  return { scope: 'public', shareable: true }
686
779
  }
780
+ _prefixLengthFromNetmask(netmask) {
781
+ if (!netmask || typeof netmask !== 'string') {
782
+ return null
783
+ }
784
+ const octets = this._parseIPv4(netmask)
785
+ if (!octets) {
786
+ return null
787
+ }
788
+ let bits = 0
789
+ for (const octet of octets) {
790
+ bits += this._countBits(octet)
791
+ }
792
+ return bits
793
+ }
794
+ _countBits(value) {
795
+ let count = 0
796
+ let v = value & 255
797
+ while (v) {
798
+ count += v & 1
799
+ v >>= 1
800
+ }
801
+ return count
802
+ }
803
+ // Heuristically rank interface candidates so physical LAN adapters win over VPN/tunnels
804
+ _scoreCandidate(entry) {
805
+ if (!entry || !entry.shareable) {
806
+ return -Infinity
807
+ }
808
+ let score = 0
809
+ switch (entry.scope) {
810
+ case 'lan':
811
+ score += 100
812
+ break
813
+ case 'cgnat':
814
+ score += 60
815
+ break
816
+ case 'public':
817
+ score += 40
818
+ break
819
+ default:
820
+ score -= 50
821
+ break
822
+ }
823
+ if (entry.default) {
824
+ score += 20
825
+ }
826
+ const type = entry.type ? entry.type.toLowerCase() : ''
827
+ if (type === 'wired') {
828
+ score += 25
829
+ } else if (type === 'wireless') {
830
+ score += 18
831
+ } else if (type === 'vpn') {
832
+ score -= 40
833
+ } else if (type === 'cellular') {
834
+ score += 5
835
+ }
836
+ if (entry.virtual) {
837
+ score -= 25
838
+ }
839
+ if (entry.operstate && entry.operstate.toLowerCase() === 'up') {
840
+ score += 5
841
+ } else if (entry.operstate) {
842
+ score -= 10
843
+ }
844
+ if (typeof entry.prefixLength === 'number') {
845
+ if (entry.prefixLength <= 24) {
846
+ score += 5
847
+ }
848
+ if (entry.prefixLength >= 30) {
849
+ score -= 20
850
+ }
851
+ }
852
+ return score
853
+ }
687
854
  _buildExternalHostEntries(externalPort) {
688
855
  if (!externalPort && externalPort !== 0) {
689
856
  return []
@@ -741,7 +908,7 @@ class PeerDiscovery {
741
908
  return entries
742
909
  }
743
910
  _broadcastTargets() {
744
- const addresses = this._collectInterfaceAddresses()
911
+ const addresses = this._collectInterfaceAddressesSync()
745
912
  this.interface_addresses = addresses
746
913
  const targets = new Set()
747
914
  for (const entry of addresses) {
package/kernel/shell.js CHANGED
@@ -17,6 +17,7 @@ const unparse = require('yargs-unparser-custom-flag');
17
17
  const Util = require('./util')
18
18
  const Environment = require('./environment')
19
19
  const ShellParser = require('./shell_parser')
20
+ const AnsiStreamTracker = require('./ansi_stream_tracker')
20
21
  const home = os.homedir()
21
22
 
22
23
  // xterm.js currently ignores DECSYNCTERM (CSI ? 2026 h/l) and renders it as text on Windows.
@@ -48,6 +49,19 @@ class Shell {
48
49
  }
49
50
  }
50
51
  this.decsyncBuffer = ''
52
+ this.nudgeRestoreTimer = null
53
+ this.nudging = false
54
+ this.nudgeReleaseTimer = null
55
+ this.lastInputAt = 0
56
+ this.canNudge = true
57
+ this.awaitingIdleNudge = false
58
+ this.idleNudgeTimer = null
59
+ this.idleNudgeDelay = 100
60
+ this.ignoreNudgeOutput = false
61
+ this.userActive = false
62
+ this.userActiveTimer = null
63
+ this.userActiveTimeout = 1000
64
+ this.ansiTracker = new AnsiStreamTracker()
51
65
 
52
66
  // Windows: /D => ignore AutoRun Registry Key
53
67
  // Others: --noprofile => ignore .bash_profile, --norc => ignore .bashrc
@@ -229,6 +243,25 @@ class Shell {
229
243
  }
230
244
  async start(params, ondata) {
231
245
  this.ondata = ondata
246
+ if (this.nudgeRestoreTimer) {
247
+ clearTimeout(this.nudgeRestoreTimer)
248
+ this.nudgeRestoreTimer = null
249
+ }
250
+ if (this.nudgeReleaseTimer) {
251
+ clearTimeout(this.nudgeReleaseTimer)
252
+ this.nudgeReleaseTimer = null
253
+ }
254
+ this.cancelIdleNudge()
255
+ this.nudging = false
256
+ this.lastInputAt = 0
257
+ this.canNudge = true
258
+ this.ignoreNudgeOutput = false
259
+ if (this.userActiveTimer) {
260
+ clearTimeout(this.userActiveTimer)
261
+ this.userActiveTimer = null
262
+ }
263
+ this.userActive = false
264
+ this.decsyncBuffer = ''
232
265
 
233
266
  /*
234
267
  params := {
@@ -383,6 +416,9 @@ class Shell {
383
416
  // return this.id
384
417
  }
385
418
  resize({ cols, rows }) {
419
+ // console.log("RESIZE", { cols, rows })
420
+ this.cols = cols
421
+ this.rows = rows
386
422
  this.ptyProcess.resize(cols, rows)
387
423
  this.vt.resize(cols, rows)
388
424
  }
@@ -400,6 +436,8 @@ class Shell {
400
436
  chunk_size = 1024
401
437
  }
402
438
  // console.log({ interactive: this.params.interactive, chunk_size })
439
+ this.canNudge = true
440
+ this.cancelIdleNudge()
403
441
  for(let i=0; i<message.length; i+=chunk_size) {
404
442
  let chunk = message.slice(i, i+chunk_size)
405
443
  // console.log("write chunk", { i, chunk })
@@ -417,9 +455,15 @@ class Shell {
417
455
  if (this.input) {
418
456
  if (this.ptyProcess) {
419
457
  if (message.length > 1024) {
458
+ this.lastInputAt = Date.now()
459
+ this.canNudge = true
460
+ this.cancelIdleNudge()
420
461
  this.emit2(message)
421
462
  } else {
422
463
  this.ptyProcess.write(message)
464
+ this.lastInputAt = Date.now()
465
+ this.canNudge = true
466
+ this.cancelIdleNudge()
423
467
  }
424
468
  }
425
469
  }
@@ -433,15 +477,27 @@ class Shell {
433
477
  for(let m of message) {
434
478
  this.cmd = this.build({ message: m })
435
479
  this.ptyProcess.write(this.cmd)
480
+ this.lastInputAt = Date.now()
481
+ this.canNudge = true
482
+ this.cancelIdleNudge()
436
483
  if (newline) {
437
484
  this.ptyProcess.write(this.EOL)
485
+ this.lastInputAt = Date.now()
486
+ this.canNudge = true
487
+ this.cancelIdleNudge()
438
488
  }
439
489
  }
440
490
  } else {
441
491
  this.cmd = this.build({ message })
442
492
  this.ptyProcess.write(this.cmd)
493
+ this.lastInputAt = Date.now()
494
+ this.canNudge = true
495
+ this.cancelIdleNudge()
443
496
  if (newline) {
444
497
  this.ptyProcess.write(this.EOL)
498
+ this.lastInputAt = Date.now()
499
+ this.canNudge = true
500
+ this.cancelIdleNudge()
445
501
  }
446
502
  }
447
503
  })
@@ -457,11 +513,17 @@ class Shell {
457
513
  this.cmd = this.build({ message: m })
458
514
  this.ptyProcess.write(this.cmd)
459
515
  this.ptyProcess.write(this.EOL)
516
+ this.lastInputAt = Date.now()
517
+ this.canNudge = true
518
+ this.cancelIdleNudge()
460
519
  }
461
520
  } else {
462
521
  this.cmd = this.build({ message })
463
522
  this.ptyProcess.write(this.cmd)
464
523
  this.ptyProcess.write(this.EOL)
524
+ this.lastInputAt = Date.now()
525
+ this.canNudge = true
526
+ this.cancelIdleNudge()
465
527
  }
466
528
  })
467
529
  }
@@ -473,6 +535,9 @@ class Shell {
473
535
  this.resolve = resolve
474
536
  this.cmd = this.build({ message })
475
537
  this.ptyProcess.write(this.cmd)
538
+ this.lastInputAt = Date.now()
539
+ this.canNudge = true
540
+ this.cancelIdleNudge()
476
541
  })
477
542
  }
478
543
  }
@@ -1113,7 +1178,12 @@ class Shell {
1113
1178
  data = data.replace(/\x1b\[6n/g, ''); // remove the code
1114
1179
  }
1115
1180
 
1116
- this.queue.push(this.filterDecsync(data))
1181
+ const filtered = this.filterDecsync(data)
1182
+ if (this.awaitingIdleNudge) {
1183
+ this.scheduleIdleNudge()
1184
+ }
1185
+ this.maybeNudgeForSequences(filtered)
1186
+ this.queue.push(filtered)
1117
1187
  }
1118
1188
  });
1119
1189
  }
@@ -1143,6 +1213,24 @@ class Shell {
1143
1213
 
1144
1214
  this.done = true
1145
1215
  this.ready = false
1216
+ if (this.nudgeRestoreTimer) {
1217
+ clearTimeout(this.nudgeRestoreTimer)
1218
+ this.nudgeRestoreTimer = null
1219
+ }
1220
+ if (this.nudgeReleaseTimer) {
1221
+ clearTimeout(this.nudgeReleaseTimer)
1222
+ this.nudgeReleaseTimer = null
1223
+ }
1224
+ this.cancelIdleNudge()
1225
+ this.nudging = false
1226
+ this.lastInputAt = 0
1227
+ this.canNudge = true
1228
+ this.ignoreNudgeOutput = false
1229
+ if (this.userActiveTimer) {
1230
+ clearTimeout(this.userActiveTimer)
1231
+ this.userActiveTimer = null
1232
+ }
1233
+ this.userActive = false
1146
1234
 
1147
1235
  let buf = this.vts.serialize()
1148
1236
  let cleaned = this.stripAnsi(buf)
@@ -1260,6 +1348,129 @@ class Shell {
1260
1348
 
1261
1349
  return result
1262
1350
  }
1351
+ setUserActive(active, ttl) {
1352
+ const clearTimer = () => {
1353
+ if (this.userActiveTimer) {
1354
+ clearTimeout(this.userActiveTimer)
1355
+ this.userActiveTimer = null
1356
+ }
1357
+ }
1358
+ if (active) {
1359
+ this.userActive = true
1360
+ this.lastInputAt = Date.now()
1361
+ const parsedTtl = Number(ttl)
1362
+ const timeout = Number.isFinite(parsedTtl) ? parsedTtl : this.userActiveTimeout
1363
+ clearTimer()
1364
+ if (timeout > 0) {
1365
+ this.userActiveTimer = setTimeout(() => {
1366
+ this.userActive = false
1367
+ this.userActiveTimer = null
1368
+ }, timeout)
1369
+ }
1370
+ this.cancelIdleNudge()
1371
+ } else {
1372
+ clearTimer()
1373
+ this.userActive = false
1374
+ }
1375
+ }
1376
+ maybeNudgeForSequences(chunk = '') {
1377
+ if (!chunk || typeof chunk !== 'string') {
1378
+ return
1379
+ }
1380
+ const detection = this.ansiTracker.push(chunk)
1381
+ if (!detection) {
1382
+ return
1383
+ }
1384
+ if (this.ignoreNudgeOutput || this.userActive) {
1385
+ return
1386
+ }
1387
+ if (this.nudging || !this.canNudge) {
1388
+ // console.log('[nudge] guard: nudging/canNudge', { nudging: this.nudging, canNudge: this.canNudge, reason: detection.reason })
1389
+ return
1390
+ }
1391
+ const sinceInput = Date.now() - this.lastInputAt
1392
+ if (sinceInput < 200) {
1393
+ // console.log('[nudge] guard: recent input', { sinceInput, reason: detection.reason })
1394
+ return
1395
+ }
1396
+ // console.log('[nudge] scheduling idle nudge', {
1397
+ // reason: detection.reason,
1398
+ // sinceInput,
1399
+ // preview: chunk.slice(0, 160)
1400
+ // })
1401
+ this.requestIdleNudge()
1402
+ }
1403
+ cancelIdleNudge() {
1404
+ if (this.idleNudgeTimer) {
1405
+ clearTimeout(this.idleNudgeTimer)
1406
+ this.idleNudgeTimer = null
1407
+ }
1408
+ this.awaitingIdleNudge = false
1409
+ }
1410
+ requestIdleNudge() {
1411
+ if (this.awaitingIdleNudge || this.nudging || !this.canNudge) {
1412
+ return
1413
+ }
1414
+ this.awaitingIdleNudge = true
1415
+ this.scheduleIdleNudge()
1416
+ }
1417
+ scheduleIdleNudge() {
1418
+ if (!this.awaitingIdleNudge) {
1419
+ return
1420
+ }
1421
+ const delay = this.idleNudgeDelay || 500
1422
+ if (this.idleNudgeTimer) {
1423
+ clearTimeout(this.idleNudgeTimer)
1424
+ }
1425
+ this.idleNudgeTimer = setTimeout(() => {
1426
+ if (this.nudging || !this.canNudge) {
1427
+ this.cancelIdleNudge()
1428
+ return
1429
+ }
1430
+ this.idleNudgeTimer = null
1431
+ this.awaitingIdleNudge = false
1432
+ this.canNudge = false
1433
+ // console.log('[nudge] idle window elapsed')
1434
+ this.forceTerminalNudge()
1435
+ }, delay)
1436
+ }
1437
+ forceTerminalNudge() {
1438
+ if (!this.ptyProcess || this.nudging) {
1439
+ // console.log('[nudge] force skipped', { hasPty: !!this.ptyProcess, nudging: this.nudging })
1440
+ return
1441
+ }
1442
+ this.cancelIdleNudge()
1443
+ const baseCols = Number.isFinite(this.cols) ? this.cols : (this.vt && Number.isFinite(this.vt.cols) ? this.vt.cols : 80)
1444
+ const baseRows = Number.isFinite(this.rows) ? this.rows : (this.vt && Number.isFinite(this.vt.rows) ? this.vt.rows : 24)
1445
+ const cols = Math.max(2, Math.floor(baseCols))
1446
+ const rows = Math.max(2, Math.floor(baseRows))
1447
+ if (cols <= 2) {
1448
+ return
1449
+ }
1450
+ this.ignoreNudgeOutput = true
1451
+ this.nudging = true
1452
+ // console.log('[nudge] shrink start', { cols: cols - 1, rows })
1453
+ this.resize({ cols: cols - 1, rows })
1454
+ if (this.nudgeRestoreTimer) {
1455
+ clearTimeout(this.nudgeRestoreTimer)
1456
+ }
1457
+ this.nudgeRestoreTimer = setTimeout(() => {
1458
+ // console.log('[nudge] restore', { cols, rows })
1459
+ // console.log('[nudge] restore start', { cols, rows })
1460
+ this.resize({ cols, rows })
1461
+ if (this.nudgeReleaseTimer) {
1462
+ clearTimeout(this.nudgeReleaseTimer)
1463
+ }
1464
+ this.nudgeReleaseTimer = setTimeout(() => {
1465
+ this.nudging = false
1466
+ this.canNudge = true
1467
+ this.ignoreNudgeOutput = false
1468
+ // console.log('[nudge] complete')
1469
+ this.nudgeReleaseTimer = null
1470
+ }, 100)
1471
+ this.nudgeRestoreTimer = null
1472
+ }, 100)
1473
+ }
1263
1474
  _log(buf, cleaned) {
1264
1475
 
1265
1476
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "3.270.0",
3
+ "version": "3.272.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {