shell-mirror 1.5.139 → 1.5.140
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 +1 -1
- package/public/app/terminal.html +77 -57
- package/public/app/terminal.js +43 -3
package/package.json
CHANGED
package/public/app/terminal.html
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<html>
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, interactive-widget=resizes-content">
|
|
6
6
|
<link rel="icon" type="image/png" href="/images/favicon.png">
|
|
7
7
|
<title>Terminal Mirror</title>
|
|
8
8
|
|
|
@@ -56,25 +56,33 @@
|
|
|
56
56
|
<script src="https://cdn.jsdelivr.net/npm/xterm@4.15.0/lib/xterm.js"></script>
|
|
57
57
|
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.5.0/lib/xterm-addon-fit.js"></script>
|
|
58
58
|
<style>
|
|
59
|
-
body, html {
|
|
60
|
-
margin: 0;
|
|
61
|
-
padding: 0;
|
|
62
|
-
height: 100%;
|
|
63
|
-
overflow: hidden;
|
|
64
|
-
background-color: #1e1e1e;
|
|
65
|
-
color: #ccc;
|
|
59
|
+
body, html {
|
|
60
|
+
margin: 0;
|
|
61
|
+
padding: 0;
|
|
62
|
+
height: 100%;
|
|
63
|
+
overflow: hidden;
|
|
64
|
+
background-color: #1e1e1e;
|
|
65
|
+
color: #ccc;
|
|
66
66
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
box-sizing: border-box;
|
|
68
|
+
/* JS sets --keyboard-inset on <html> via visualViewport to shrink
|
|
69
|
+
the body when the on-screen keyboard opens. The flex column inside
|
|
70
|
+
shrinks with it so the FAB rides above the keyboard. */
|
|
71
|
+
padding-bottom: var(--keyboard-inset, 0px);
|
|
72
|
+
transition: padding-bottom 0.15s ease-out;
|
|
73
|
+
}
|
|
74
|
+
#terminal-container {
|
|
75
|
+
display: none;
|
|
76
|
+
height: 100%;
|
|
77
|
+
width: 100%;
|
|
72
78
|
background-color: #000000;
|
|
73
79
|
}
|
|
74
|
-
|
|
80
|
+
|
|
75
81
|
#terminal-container.show {
|
|
76
82
|
display: flex;
|
|
77
83
|
flex-direction: column;
|
|
84
|
+
min-height: 0;
|
|
85
|
+
overflow: hidden;
|
|
78
86
|
}
|
|
79
87
|
|
|
80
88
|
/* Session Header - Unified Design */
|
|
@@ -281,9 +289,11 @@
|
|
|
281
289
|
#terminal {
|
|
282
290
|
padding: 8px; /* Mac Terminal.app padding */
|
|
283
291
|
background-color: #000000;
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
292
|
+
flex: 1 1 auto;
|
|
293
|
+
min-height: 0; /* critical: lets xterm shrink as the column shortens */
|
|
294
|
+
width: 100%;
|
|
295
|
+
box-sizing: border-box;
|
|
296
|
+
overflow: hidden;
|
|
287
297
|
}
|
|
288
298
|
#connect-container { padding: 2em; text-align: center; }
|
|
289
299
|
#agent-id-input { font-size: 1.2em; padding: 8px; width: 400px; margin-bottom: 1em; }
|
|
@@ -376,14 +386,18 @@
|
|
|
376
386
|
Floating Buttons - Mobile CLI Shortcuts
|
|
377
387
|
======================================== */
|
|
378
388
|
.floating-buttons {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
389
|
+
/* In-flow flex child of #terminal-container. Layout (not the
|
|
390
|
+
compositor) decides where the strip sits, which avoids iOS Safari's
|
|
391
|
+
position:fixed + backdrop-filter freeze during keyboard transitions. */
|
|
392
|
+
position: relative;
|
|
393
|
+
align-self: center;
|
|
394
|
+
margin: 0 auto 8px;
|
|
395
|
+
padding-bottom: env(safe-area-inset-bottom, 0);
|
|
383
396
|
z-index: 1000;
|
|
384
397
|
display: flex;
|
|
385
398
|
align-items: center;
|
|
386
399
|
gap: 0;
|
|
400
|
+
flex-shrink: 0;
|
|
387
401
|
}
|
|
388
402
|
|
|
389
403
|
/* Toggle button (collapse/expand) */
|
|
@@ -400,6 +414,8 @@
|
|
|
400
414
|
cursor: pointer;
|
|
401
415
|
flex-shrink: 0;
|
|
402
416
|
margin-right: 4px;
|
|
417
|
+
touch-action: manipulation;
|
|
418
|
+
-webkit-tap-highlight-color: transparent;
|
|
403
419
|
}
|
|
404
420
|
|
|
405
421
|
.fab-toggle:active {
|
|
@@ -422,6 +438,8 @@
|
|
|
422
438
|
display: flex;
|
|
423
439
|
align-items: center;
|
|
424
440
|
justify-content: center;
|
|
441
|
+
touch-action: manipulation;
|
|
442
|
+
-webkit-tap-highlight-color: transparent;
|
|
425
443
|
}
|
|
426
444
|
|
|
427
445
|
.fab-scroll:active { opacity: 1; background: #22232a; }
|
|
@@ -486,6 +504,8 @@
|
|
|
486
504
|
white-space: nowrap;
|
|
487
505
|
cursor: pointer;
|
|
488
506
|
transition: background 0.1s;
|
|
507
|
+
touch-action: manipulation;
|
|
508
|
+
-webkit-tap-highlight-color: transparent;
|
|
489
509
|
}
|
|
490
510
|
|
|
491
511
|
.fab-btn:active { background: #22232a; }
|
|
@@ -545,43 +565,43 @@
|
|
|
545
565
|
</div>
|
|
546
566
|
</div>
|
|
547
567
|
<div id="terminal"></div>
|
|
548
|
-
</div>
|
|
549
568
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
569
|
+
<!-- Floating Buttons - Mobile CLI Shortcuts (flex child so it reserves layout space) -->
|
|
570
|
+
<div id="floating-buttons" class="floating-buttons">
|
|
571
|
+
<!-- Toggle button (collapse/expand) -->
|
|
572
|
+
<button class="fab-toggle" id="fabToggle" aria-label="Toggle keyboard shortcuts" tabindex="-1">
|
|
573
|
+
<span class="fab-toggle-icon">⌨</span>
|
|
574
|
+
</button>
|
|
575
|
+
|
|
576
|
+
<!-- Left scroll indicator -->
|
|
577
|
+
<button class="fab-scroll fab-scroll-left hidden" id="fabScrollLeft" aria-label="Scroll left" tabindex="-1">
|
|
578
|
+
<span>‹</span>
|
|
579
|
+
</button>
|
|
580
|
+
|
|
581
|
+
<!-- Scrollable button strip -->
|
|
582
|
+
<div class="fab-strip" id="fabStrip">
|
|
583
|
+
<!-- Control group (scroll left to reveal) -->
|
|
584
|
+
<button class="fab-btn" data-keys="ctrl+l" title="Clear screen" tabindex="-1">^L</button>
|
|
585
|
+
<button class="fab-btn" data-keys="ctrl+d" title="EOF/Exit" tabindex="-1">^D</button>
|
|
586
|
+
<button class="fab-btn" data-keys="ctrl+c" title="Interrupt" tabindex="-1">^C</button>
|
|
587
|
+
|
|
588
|
+
<!-- Primary group (default visible - AI CLI essentials) -->
|
|
589
|
+
<button class="fab-btn fab-primary" data-keys="shift+tab" title="Mode: Normal → Auto → Plan" tabindex="-1">⇧Tab</button>
|
|
590
|
+
<button class="fab-btn" data-keys="tab" title="Autocomplete / Thinking" tabindex="-1">Tab</button>
|
|
591
|
+
<button class="fab-btn" data-keys="up" data-repeat="true" title="History up" tabindex="-1">↑</button>
|
|
592
|
+
<button class="fab-btn" data-keys="down" data-repeat="true" title="History down" tabindex="-1">↓</button>
|
|
593
|
+
<button class="fab-btn" data-keys="escape" title="Cancel / Rewind (2x)" tabindex="-1">Esc</button>
|
|
594
|
+
|
|
595
|
+
<!-- Navigation group (scroll right to reveal) -->
|
|
596
|
+
<button class="fab-btn" data-keys="left" title="Cursor left" tabindex="-1">←</button>
|
|
597
|
+
<button class="fab-btn" data-keys="right" title="Cursor right" tabindex="-1">→</button>
|
|
598
|
+
</div>
|
|
599
|
+
|
|
600
|
+
<!-- Right scroll indicator -->
|
|
601
|
+
<button class="fab-scroll fab-scroll-right" id="fabScrollRight" aria-label="Scroll right" tabindex="-1">
|
|
602
|
+
<span>›</span>
|
|
603
|
+
</button>
|
|
604
|
+
</div>
|
|
585
605
|
</div>
|
|
586
606
|
|
|
587
607
|
<!-- Help Modal (Dark Kraken Style) -->
|
package/public/app/terminal.js
CHANGED
|
@@ -996,9 +996,7 @@ function setupDataChannel() {
|
|
|
996
996
|
}
|
|
997
997
|
});
|
|
998
998
|
|
|
999
|
-
window.addEventListener('resize',
|
|
1000
|
-
fitAddon.fit();
|
|
1001
|
-
});
|
|
999
|
+
window.addEventListener('resize', scheduleFit);
|
|
1002
1000
|
|
|
1003
1001
|
term.onResize(({ cols, rows }) => {
|
|
1004
1002
|
if (dataChannel && dataChannel.readyState === 'open') {
|
|
@@ -1543,6 +1541,11 @@ function initFloatingButtons() {
|
|
|
1543
1541
|
// Long-press repeat for arrow keys
|
|
1544
1542
|
initFabLongPress();
|
|
1545
1543
|
|
|
1544
|
+
// Drive body padding-bottom from the visualViewport keyboard inset so the
|
|
1545
|
+
// flex column shrinks when the iOS keyboard opens. The FAB is a flex child
|
|
1546
|
+
// and rides up with the column — no fixed-positioning, no transform.
|
|
1547
|
+
initKeyboardInsetTracking();
|
|
1548
|
+
|
|
1546
1549
|
// Scroll to show primary buttons (⇧Tab visible on left)
|
|
1547
1550
|
setTimeout(() => {
|
|
1548
1551
|
const modeBtn = strip.querySelector('[data-keys="shift+tab"]');
|
|
@@ -1555,6 +1558,43 @@ function initFloatingButtons() {
|
|
|
1555
1558
|
console.log('[CLIENT] ⌨️ Floating buttons initialized');
|
|
1556
1559
|
}
|
|
1557
1560
|
|
|
1561
|
+
// rAF-throttled fit so window.resize, visualViewport, and orientationchange
|
|
1562
|
+
// don't trigger duplicate measurement passes within the same frame.
|
|
1563
|
+
let fitPending = false;
|
|
1564
|
+
function scheduleFit() {
|
|
1565
|
+
if (fitPending) return;
|
|
1566
|
+
fitPending = true;
|
|
1567
|
+
requestAnimationFrame(() => {
|
|
1568
|
+
fitPending = false;
|
|
1569
|
+
try { fitAddon.fit(); } catch (_) { /* container may be 0×0 mid-bootstrap */ }
|
|
1570
|
+
});
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
// Track on-screen keyboard size via visualViewport, expose it as
|
|
1574
|
+
// --keyboard-inset on <html> so body { padding-bottom } shrinks the flex column.
|
|
1575
|
+
function initKeyboardInsetTracking() {
|
|
1576
|
+
const vv = window.visualViewport;
|
|
1577
|
+
if (!vv) return; // older browsers — body stays full-height (acceptable on desktop)
|
|
1578
|
+
|
|
1579
|
+
let rafPending = false;
|
|
1580
|
+
const update = () => {
|
|
1581
|
+
rafPending = false;
|
|
1582
|
+
const inset = Math.max(0, window.innerHeight - (vv.height + vv.offsetTop));
|
|
1583
|
+
document.documentElement.style.setProperty('--keyboard-inset', `${inset}px`);
|
|
1584
|
+
scheduleFit();
|
|
1585
|
+
};
|
|
1586
|
+
const schedule = () => {
|
|
1587
|
+
if (rafPending) return;
|
|
1588
|
+
rafPending = true;
|
|
1589
|
+
requestAnimationFrame(update);
|
|
1590
|
+
};
|
|
1591
|
+
|
|
1592
|
+
vv.addEventListener('resize', schedule);
|
|
1593
|
+
vv.addEventListener('scroll', schedule); // QuickType bar slide-in fires scroll on some iOS builds
|
|
1594
|
+
window.addEventListener('orientationchange', schedule);
|
|
1595
|
+
update(); // initial state
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1558
1598
|
// Send key sequence through the appropriate connection
|
|
1559
1599
|
function sendFabKey(keySequence) {
|
|
1560
1600
|
// Use the same logic as term.onData - send through WebRTC or direct WS
|