tabminal 3.0.4 → 3.0.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.
- package/package.json +1 -1
- package/public/app.js +80 -10
- package/public/index.html +119 -18
- package/public/styles.css +1 -1
package/package.json
CHANGED
package/public/app.js
CHANGED
|
@@ -170,6 +170,14 @@ function isCompactWorkspaceMode() {
|
|
|
170
170
|
return !!window.__tabminalCompactWorkspaceMode;
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
+
function isCompactTerminalTabsMode() {
|
|
174
|
+
return !!window.__tabminalCompactTerminalTabsMode;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function isForcedTerminalWorkspaceMode() {
|
|
178
|
+
return isCompactWorkspaceMode() || isCompactTerminalTabsMode();
|
|
179
|
+
}
|
|
180
|
+
|
|
173
181
|
function getTerminalFontSize() {
|
|
174
182
|
return IS_MOBILE ? 14 : 12;
|
|
175
183
|
}
|
|
@@ -286,6 +294,13 @@ if (sidebarToggle && sidebar && sidebarOverlay) {
|
|
|
286
294
|
sidebarOverlay.classList.remove('open');
|
|
287
295
|
};
|
|
288
296
|
|
|
297
|
+
sidebarToggle.addEventListener('pointerdown', (event) => {
|
|
298
|
+
if (!isCompactWorkspaceMode()) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
event.preventDefault();
|
|
302
|
+
});
|
|
303
|
+
|
|
289
304
|
sidebarToggle.addEventListener('click', () => {
|
|
290
305
|
sidebar.classList.toggle('open');
|
|
291
306
|
sidebarOverlay.classList.toggle('open');
|
|
@@ -711,13 +726,13 @@ class EditorManager {
|
|
|
711
726
|
}
|
|
712
727
|
|
|
713
728
|
canToggleTerminalWorkspaceMode(session = this.currentSession) {
|
|
714
|
-
return !!session && !
|
|
729
|
+
return !!session && !isForcedTerminalWorkspaceMode();
|
|
715
730
|
}
|
|
716
731
|
|
|
717
732
|
hasCompactWorkspaceTabs(session = this.currentSession) {
|
|
718
733
|
return !!session
|
|
719
734
|
&& (
|
|
720
|
-
|
|
735
|
+
isForcedTerminalWorkspaceMode()
|
|
721
736
|
|| this.isTerminalTabPinned(session)
|
|
722
737
|
);
|
|
723
738
|
}
|
|
@@ -784,7 +799,7 @@ class EditorManager {
|
|
|
784
799
|
&& this.terminalWrapper.contains(activeElement)
|
|
785
800
|
);
|
|
786
801
|
|
|
787
|
-
if (
|
|
802
|
+
if (isForcedTerminalWorkspaceMode()) {
|
|
788
803
|
session.sharedWorkspaceState.terminalDisplayMode = 'auto';
|
|
789
804
|
this.updateTerminalLayoutButton();
|
|
790
805
|
return;
|
|
@@ -10614,12 +10629,65 @@ function renderServerControls() {
|
|
|
10614
10629
|
|
|
10615
10630
|
// #region Notification Manager
|
|
10616
10631
|
const notificationManager = new NotificationManager();
|
|
10632
|
+
const APP_NOTIFICATION_QUIET_MS = 30_000;
|
|
10633
|
+
const APP_NOTIFICATION_IDLE_MS = 3 * 60 * 1000;
|
|
10634
|
+
let appNotificationQuietUntil = Date.now() + APP_NOTIFICATION_QUIET_MS;
|
|
10635
|
+
let lastAppInteractionAt = Date.now();
|
|
10636
|
+
|
|
10637
|
+
function noteAppInteraction() {
|
|
10638
|
+
lastAppInteractionAt = Date.now();
|
|
10639
|
+
}
|
|
10640
|
+
|
|
10641
|
+
function enterAppNotificationQuietPeriod(duration = APP_NOTIFICATION_QUIET_MS) {
|
|
10642
|
+
appNotificationQuietUntil = Math.max(
|
|
10643
|
+
appNotificationQuietUntil,
|
|
10644
|
+
Date.now() + duration
|
|
10645
|
+
);
|
|
10646
|
+
}
|
|
10647
|
+
|
|
10648
|
+
function shouldNotifyConnectionStatus() {
|
|
10649
|
+
if (Date.now() < appNotificationQuietUntil) {
|
|
10650
|
+
return false;
|
|
10651
|
+
}
|
|
10652
|
+
if (document.visibilityState !== 'visible') {
|
|
10653
|
+
return false;
|
|
10654
|
+
}
|
|
10655
|
+
if (typeof document.hasFocus === 'function' && !document.hasFocus()) {
|
|
10656
|
+
return false;
|
|
10657
|
+
}
|
|
10658
|
+
if ((Date.now() - lastAppInteractionAt) > APP_NOTIFICATION_IDLE_MS) {
|
|
10659
|
+
return false;
|
|
10660
|
+
}
|
|
10661
|
+
return true;
|
|
10662
|
+
}
|
|
10663
|
+
|
|
10664
|
+
document.addEventListener('pointerdown', noteAppInteraction, {
|
|
10665
|
+
capture: true,
|
|
10666
|
+
passive: true
|
|
10667
|
+
});
|
|
10668
|
+
document.addEventListener('touchstart', noteAppInteraction, {
|
|
10669
|
+
capture: true,
|
|
10670
|
+
passive: true
|
|
10671
|
+
});
|
|
10672
|
+
document.addEventListener('keydown', noteAppInteraction, {
|
|
10673
|
+
capture: true
|
|
10674
|
+
});
|
|
10675
|
+
window.addEventListener('focus', () => {
|
|
10676
|
+
noteAppInteraction();
|
|
10677
|
+
enterAppNotificationQuietPeriod();
|
|
10678
|
+
});
|
|
10679
|
+
window.addEventListener('pageshow', () => {
|
|
10680
|
+
noteAppInteraction();
|
|
10681
|
+
enterAppNotificationQuietPeriod();
|
|
10682
|
+
});
|
|
10617
10683
|
|
|
10618
10684
|
document.addEventListener('click', () => {
|
|
10619
10685
|
notificationManager.requestPermission();
|
|
10620
10686
|
}, { once: true, capture: true });
|
|
10621
10687
|
document.addEventListener('visibilitychange', () => {
|
|
10622
10688
|
if (document.visibilityState === 'visible') {
|
|
10689
|
+
noteAppInteraction();
|
|
10690
|
+
enterAppNotificationQuietPeriod();
|
|
10623
10691
|
clearVisibleAttentionState();
|
|
10624
10692
|
}
|
|
10625
10693
|
});
|
|
@@ -10661,18 +10729,20 @@ function setStatus(server, status) {
|
|
|
10661
10729
|
statusMemory.set(server.id, status);
|
|
10662
10730
|
server.connectionStatus = status;
|
|
10663
10731
|
renderServerControls();
|
|
10664
|
-
|
|
10665
|
-
const activeServer = getActiveServer();
|
|
10666
|
-
if (!activeServer || activeServer.id !== server.id) return;
|
|
10667
10732
|
const hostName = getDisplayHost(server);
|
|
10668
10733
|
const target = hostName || 'host';
|
|
10734
|
+
const shouldNotify = shouldNotifyConnectionStatus();
|
|
10669
10735
|
|
|
10670
|
-
if (status === 'reconnecting') {
|
|
10736
|
+
if (status === 'reconnecting' && shouldNotify) {
|
|
10671
10737
|
alert(`Lost connection to ${target}. Reconnecting...`, {
|
|
10672
10738
|
type: 'warning',
|
|
10673
10739
|
title: 'Connection'
|
|
10674
10740
|
});
|
|
10675
|
-
} else if (
|
|
10741
|
+
} else if (
|
|
10742
|
+
status === 'connected'
|
|
10743
|
+
&& prevStatus === 'reconnecting'
|
|
10744
|
+
&& shouldNotify
|
|
10745
|
+
) {
|
|
10676
10746
|
alert(`Connection to ${target} restored.`, {
|
|
10677
10747
|
type: 'success',
|
|
10678
10748
|
title: 'Connection'
|
|
@@ -10682,7 +10752,7 @@ function setStatus(server, status) {
|
|
|
10682
10752
|
type: 'error',
|
|
10683
10753
|
title: 'Connection'
|
|
10684
10754
|
});
|
|
10685
|
-
} else if (status === 'connected' && !prevStatus) {
|
|
10755
|
+
} else if (status === 'connected' && !prevStatus && shouldNotify) {
|
|
10686
10756
|
alert(`Connected to ${target}.`, {
|
|
10687
10757
|
type: 'success',
|
|
10688
10758
|
title: 'Connection'
|
|
@@ -10809,7 +10879,7 @@ window.addEventListener('tabminal:layout-modechange', () => {
|
|
|
10809
10879
|
&& terminalEl.contains(activeElement)
|
|
10810
10880
|
);
|
|
10811
10881
|
|
|
10812
|
-
if (
|
|
10882
|
+
if (isForcedTerminalWorkspaceMode()) {
|
|
10813
10883
|
if (terminalHasFocus) {
|
|
10814
10884
|
session.workspaceState.activeTabKey = TERMINAL_WORKSPACE_TAB_KEY;
|
|
10815
10885
|
session.saveState();
|
package/public/index.html
CHANGED
|
@@ -14,26 +14,37 @@
|
|
|
14
14
|
<link rel="preconnect" href="https://cdn.jsdelivr.net">
|
|
15
15
|
<script>
|
|
16
16
|
const COMPACT_WORKSPACE_MAX_WIDTH = 767;
|
|
17
|
-
const
|
|
18
|
-
|
|
17
|
+
const COMPACT_TERMINAL_TAB_MAX_WIDTH = 1408;
|
|
18
|
+
// Temporarily disabled short-landscape fallback. Pixel Fold and
|
|
19
|
+
// similar devices can oscillate around the keyboard-driven viewport
|
|
20
|
+
// boundary, which makes compact mode flap during sidebar animation.
|
|
21
|
+
// const COMPACT_WORKSPACE_MAX_LANDSCAPE_WIDTH = 950;
|
|
22
|
+
// const COMPACT_WORKSPACE_ENTER_MAX_SHORT_HEIGHT = 500;
|
|
23
|
+
// const COMPACT_WORKSPACE_EXIT_MAX_SHORT_HEIGHT = 540;
|
|
24
|
+
const SIDEBAR_LAYOUT_PAUSE_MS = 360;
|
|
25
|
+
let layoutLoopPausedUntil = 0;
|
|
26
|
+
let frozenLayoutViewport = null;
|
|
27
|
+
let sidebarLayoutThawTimer = 0;
|
|
19
28
|
|
|
20
|
-
function shouldUseCompactWorkspaceMode(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
function shouldUseCompactWorkspaceMode(
|
|
30
|
+
width,
|
|
31
|
+
height,
|
|
32
|
+
previousCompactWorkspaceMode = false
|
|
33
|
+
) {
|
|
34
|
+
// Keep the extra args to preserve the old signature for possible
|
|
35
|
+
// re-enable of the short-landscape fallback above.
|
|
36
|
+
void height;
|
|
37
|
+
void previousCompactWorkspaceMode;
|
|
38
|
+
return width <= COMPACT_WORKSPACE_MAX_WIDTH;
|
|
26
39
|
}
|
|
27
40
|
|
|
28
|
-
|
|
29
|
-
function updateLayout() {
|
|
41
|
+
function readLayoutViewport() {
|
|
30
42
|
const vv = window.visualViewport;
|
|
31
43
|
let width = window.innerWidth;
|
|
32
44
|
let height = window.innerHeight;
|
|
33
45
|
let offsetTop = 0;
|
|
34
46
|
let offsetLeft = 0;
|
|
35
47
|
|
|
36
|
-
// Sanity check for iPadOS PWA bug where vv.height can be negative with physical keyboard
|
|
37
48
|
if (vv && vv.height > 100) {
|
|
38
49
|
width = vv.width;
|
|
39
50
|
height = vv.height;
|
|
@@ -41,18 +52,43 @@
|
|
|
41
52
|
offsetLeft = vv.offsetLeft;
|
|
42
53
|
}
|
|
43
54
|
|
|
55
|
+
return {
|
|
56
|
+
width,
|
|
57
|
+
height,
|
|
58
|
+
offsetTop,
|
|
59
|
+
offsetLeft
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Mobile Layout Manager
|
|
64
|
+
function updateLayout() {
|
|
65
|
+
const layoutViewport = frozenLayoutViewport || readLayoutViewport();
|
|
66
|
+
const {
|
|
67
|
+
width,
|
|
68
|
+
height,
|
|
69
|
+
offsetTop,
|
|
70
|
+
offsetLeft
|
|
71
|
+
} = layoutViewport;
|
|
72
|
+
|
|
44
73
|
document.documentElement.style.setProperty('--app-width', `${width}px`);
|
|
45
74
|
document.documentElement.style.setProperty('--app-height', `${height}px`);
|
|
46
75
|
document.documentElement.style.setProperty('--app-top', `${offsetTop}px`);
|
|
47
76
|
document.documentElement.style.setProperty('--app-left', `${offsetLeft}px`);
|
|
48
77
|
|
|
78
|
+
const previousCompactWorkspaceMode =
|
|
79
|
+
window.__tabminalCompactWorkspaceMode;
|
|
80
|
+
const previousCompactTerminalTabsMode =
|
|
81
|
+
window.__tabminalCompactTerminalTabsMode;
|
|
49
82
|
const compactWorkspaceMode = shouldUseCompactWorkspaceMode(
|
|
50
83
|
width,
|
|
51
|
-
height
|
|
84
|
+
height,
|
|
85
|
+
previousCompactWorkspaceMode
|
|
52
86
|
);
|
|
53
|
-
const
|
|
54
|
-
|
|
87
|
+
const compactTerminalTabsMode =
|
|
88
|
+
width <= COMPACT_TERMINAL_TAB_MAX_WIDTH;
|
|
55
89
|
window.__tabminalCompactWorkspaceMode = compactWorkspaceMode;
|
|
90
|
+
window.__tabminalCompactTerminalTabsMode =
|
|
91
|
+
compactTerminalTabsMode;
|
|
56
92
|
|
|
57
93
|
if (document.body) {
|
|
58
94
|
window.scrollTo(0, 0);
|
|
@@ -83,17 +119,56 @@
|
|
|
83
119
|
}
|
|
84
120
|
|
|
85
121
|
if (
|
|
86
|
-
|
|
87
|
-
|
|
122
|
+
(
|
|
123
|
+
previousCompactWorkspaceMode !== undefined
|
|
124
|
+
&& previousCompactWorkspaceMode !== compactWorkspaceMode
|
|
125
|
+
)
|
|
126
|
+
|| (
|
|
127
|
+
previousCompactTerminalTabsMode !== undefined
|
|
128
|
+
&& previousCompactTerminalTabsMode
|
|
129
|
+
!== compactTerminalTabsMode
|
|
130
|
+
)
|
|
88
131
|
) {
|
|
89
132
|
window.dispatchEvent(
|
|
90
133
|
new CustomEvent('tabminal:layout-modechange', {
|
|
91
|
-
detail: {
|
|
134
|
+
detail: {
|
|
135
|
+
compactWorkspaceMode,
|
|
136
|
+
compactTerminalTabsMode
|
|
137
|
+
}
|
|
92
138
|
})
|
|
93
139
|
);
|
|
94
140
|
}
|
|
95
141
|
}
|
|
96
142
|
|
|
143
|
+
function pauseLayoutLoop(duration = SIDEBAR_LAYOUT_PAUSE_MS) {
|
|
144
|
+
const now = performance.now();
|
|
145
|
+
layoutLoopPausedUntil = Math.max(
|
|
146
|
+
layoutLoopPausedUntil,
|
|
147
|
+
now + duration
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function freezeLayoutViewport() {
|
|
152
|
+
frozenLayoutViewport = readLayoutViewport();
|
|
153
|
+
if (sidebarLayoutThawTimer) {
|
|
154
|
+
clearTimeout(sidebarLayoutThawTimer);
|
|
155
|
+
sidebarLayoutThawTimer = 0;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function thawLayoutViewport() {
|
|
160
|
+
if (sidebarLayoutThawTimer) {
|
|
161
|
+
clearTimeout(sidebarLayoutThawTimer);
|
|
162
|
+
}
|
|
163
|
+
sidebarLayoutThawTimer = window.setTimeout(() => {
|
|
164
|
+
frozenLayoutViewport = null;
|
|
165
|
+
sidebarLayoutThawTimer = 0;
|
|
166
|
+
updateLayout();
|
|
167
|
+
}, SIDEBAR_LAYOUT_PAUSE_MS);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
window.__tabminalPauseLayoutLoop = pauseLayoutLoop;
|
|
171
|
+
|
|
97
172
|
if (window.visualViewport) {
|
|
98
173
|
window.visualViewport.addEventListener('resize', updateLayout);
|
|
99
174
|
window.visualViewport.addEventListener('scroll', updateLayout);
|
|
@@ -103,13 +178,39 @@
|
|
|
103
178
|
|
|
104
179
|
// High-performance Layout Loop (60fps)
|
|
105
180
|
function layoutLoop() {
|
|
106
|
-
|
|
181
|
+
if (performance.now() >= layoutLoopPausedUntil) {
|
|
182
|
+
updateLayout();
|
|
183
|
+
}
|
|
107
184
|
requestAnimationFrame(layoutLoop);
|
|
108
185
|
}
|
|
109
186
|
layoutLoop();
|
|
110
187
|
|
|
111
188
|
// Initial triggers
|
|
112
189
|
document.addEventListener('DOMContentLoaded', updateLayout);
|
|
190
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
191
|
+
const sidebar = document.getElementById('sidebar');
|
|
192
|
+
if (!sidebar) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
let sidebarOpen = sidebar.classList.contains('open');
|
|
196
|
+
const observer = new MutationObserver(() => {
|
|
197
|
+
const isOpen = sidebar.classList.contains('open');
|
|
198
|
+
if (isOpen === sidebarOpen) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
sidebarOpen = isOpen;
|
|
202
|
+
pauseLayoutLoop();
|
|
203
|
+
if (isOpen) {
|
|
204
|
+
freezeLayoutViewport();
|
|
205
|
+
} else {
|
|
206
|
+
thawLayoutViewport();
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
observer.observe(sidebar, {
|
|
210
|
+
attributes: true,
|
|
211
|
+
attributeFilter: ['class']
|
|
212
|
+
});
|
|
213
|
+
});
|
|
113
214
|
window.addEventListener('load', updateLayout);
|
|
114
215
|
|
|
115
216
|
// Enhanced Touch Interceptor with Boundary Check
|