tabminal 3.0.4 → 3.0.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.
- package/package.json +1 -1
- package/public/app.js +61 -6
- package/public/index.html +61 -11
- package/public/styles.css +1 -1
package/package.json
CHANGED
package/public/app.js
CHANGED
|
@@ -10614,12 +10614,65 @@ function renderServerControls() {
|
|
|
10614
10614
|
|
|
10615
10615
|
// #region Notification Manager
|
|
10616
10616
|
const notificationManager = new NotificationManager();
|
|
10617
|
+
const APP_NOTIFICATION_QUIET_MS = 30_000;
|
|
10618
|
+
const APP_NOTIFICATION_IDLE_MS = 3 * 60 * 1000;
|
|
10619
|
+
let appNotificationQuietUntil = Date.now() + APP_NOTIFICATION_QUIET_MS;
|
|
10620
|
+
let lastAppInteractionAt = Date.now();
|
|
10621
|
+
|
|
10622
|
+
function noteAppInteraction() {
|
|
10623
|
+
lastAppInteractionAt = Date.now();
|
|
10624
|
+
}
|
|
10625
|
+
|
|
10626
|
+
function enterAppNotificationQuietPeriod(duration = APP_NOTIFICATION_QUIET_MS) {
|
|
10627
|
+
appNotificationQuietUntil = Math.max(
|
|
10628
|
+
appNotificationQuietUntil,
|
|
10629
|
+
Date.now() + duration
|
|
10630
|
+
);
|
|
10631
|
+
}
|
|
10632
|
+
|
|
10633
|
+
function shouldNotifyConnectionStatus() {
|
|
10634
|
+
if (Date.now() < appNotificationQuietUntil) {
|
|
10635
|
+
return false;
|
|
10636
|
+
}
|
|
10637
|
+
if (document.visibilityState !== 'visible') {
|
|
10638
|
+
return false;
|
|
10639
|
+
}
|
|
10640
|
+
if (typeof document.hasFocus === 'function' && !document.hasFocus()) {
|
|
10641
|
+
return false;
|
|
10642
|
+
}
|
|
10643
|
+
if ((Date.now() - lastAppInteractionAt) > APP_NOTIFICATION_IDLE_MS) {
|
|
10644
|
+
return false;
|
|
10645
|
+
}
|
|
10646
|
+
return true;
|
|
10647
|
+
}
|
|
10648
|
+
|
|
10649
|
+
document.addEventListener('pointerdown', noteAppInteraction, {
|
|
10650
|
+
capture: true,
|
|
10651
|
+
passive: true
|
|
10652
|
+
});
|
|
10653
|
+
document.addEventListener('touchstart', noteAppInteraction, {
|
|
10654
|
+
capture: true,
|
|
10655
|
+
passive: true
|
|
10656
|
+
});
|
|
10657
|
+
document.addEventListener('keydown', noteAppInteraction, {
|
|
10658
|
+
capture: true
|
|
10659
|
+
});
|
|
10660
|
+
window.addEventListener('focus', () => {
|
|
10661
|
+
noteAppInteraction();
|
|
10662
|
+
enterAppNotificationQuietPeriod();
|
|
10663
|
+
});
|
|
10664
|
+
window.addEventListener('pageshow', () => {
|
|
10665
|
+
noteAppInteraction();
|
|
10666
|
+
enterAppNotificationQuietPeriod();
|
|
10667
|
+
});
|
|
10617
10668
|
|
|
10618
10669
|
document.addEventListener('click', () => {
|
|
10619
10670
|
notificationManager.requestPermission();
|
|
10620
10671
|
}, { once: true, capture: true });
|
|
10621
10672
|
document.addEventListener('visibilitychange', () => {
|
|
10622
10673
|
if (document.visibilityState === 'visible') {
|
|
10674
|
+
noteAppInteraction();
|
|
10675
|
+
enterAppNotificationQuietPeriod();
|
|
10623
10676
|
clearVisibleAttentionState();
|
|
10624
10677
|
}
|
|
10625
10678
|
});
|
|
@@ -10661,18 +10714,20 @@ function setStatus(server, status) {
|
|
|
10661
10714
|
statusMemory.set(server.id, status);
|
|
10662
10715
|
server.connectionStatus = status;
|
|
10663
10716
|
renderServerControls();
|
|
10664
|
-
|
|
10665
|
-
const activeServer = getActiveServer();
|
|
10666
|
-
if (!activeServer || activeServer.id !== server.id) return;
|
|
10667
10717
|
const hostName = getDisplayHost(server);
|
|
10668
10718
|
const target = hostName || 'host';
|
|
10719
|
+
const shouldNotify = shouldNotifyConnectionStatus();
|
|
10669
10720
|
|
|
10670
|
-
if (status === 'reconnecting') {
|
|
10721
|
+
if (status === 'reconnecting' && shouldNotify) {
|
|
10671
10722
|
alert(`Lost connection to ${target}. Reconnecting...`, {
|
|
10672
10723
|
type: 'warning',
|
|
10673
10724
|
title: 'Connection'
|
|
10674
10725
|
});
|
|
10675
|
-
} else if (
|
|
10726
|
+
} else if (
|
|
10727
|
+
status === 'connected'
|
|
10728
|
+
&& prevStatus === 'reconnecting'
|
|
10729
|
+
&& shouldNotify
|
|
10730
|
+
) {
|
|
10676
10731
|
alert(`Connection to ${target} restored.`, {
|
|
10677
10732
|
type: 'success',
|
|
10678
10733
|
title: 'Connection'
|
|
@@ -10682,7 +10737,7 @@ function setStatus(server, status) {
|
|
|
10682
10737
|
type: 'error',
|
|
10683
10738
|
title: 'Connection'
|
|
10684
10739
|
});
|
|
10685
|
-
} else if (status === 'connected' && !prevStatus) {
|
|
10740
|
+
} else if (status === 'connected' && !prevStatus && shouldNotify) {
|
|
10686
10741
|
alert(`Connected to ${target}.`, {
|
|
10687
10742
|
type: 'success',
|
|
10688
10743
|
title: 'Connection'
|
package/public/index.html
CHANGED
|
@@ -14,15 +14,33 @@
|
|
|
14
14
|
<link rel="preconnect" href="https://cdn.jsdelivr.net">
|
|
15
15
|
<script>
|
|
16
16
|
const COMPACT_WORKSPACE_MAX_WIDTH = 767;
|
|
17
|
-
const COMPACT_WORKSPACE_MAX_SHORT_HEIGHT = 500;
|
|
18
17
|
const COMPACT_WORKSPACE_MAX_LANDSCAPE_WIDTH = 950;
|
|
18
|
+
const COMPACT_WORKSPACE_ENTER_MAX_SHORT_HEIGHT = 500;
|
|
19
|
+
const COMPACT_WORKSPACE_EXIT_MAX_SHORT_HEIGHT = 540;
|
|
20
|
+
const SIDEBAR_LAYOUT_PAUSE_MS = 360;
|
|
21
|
+
let layoutLoopPausedUntil = 0;
|
|
19
22
|
|
|
20
|
-
function shouldUseCompactWorkspaceMode(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
function shouldUseCompactWorkspaceMode(
|
|
24
|
+
width,
|
|
25
|
+
height,
|
|
26
|
+
previousCompactWorkspaceMode = false
|
|
27
|
+
) {
|
|
28
|
+
if (width <= COMPACT_WORKSPACE_MAX_WIDTH) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const isShortLandscapeCandidate = (
|
|
33
|
+
width > height
|
|
34
|
+
&& width <= COMPACT_WORKSPACE_MAX_LANDSCAPE_WIDTH
|
|
35
|
+
);
|
|
36
|
+
if (!isShortLandscapeCandidate) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const shortHeightThreshold = previousCompactWorkspaceMode
|
|
41
|
+
? COMPACT_WORKSPACE_EXIT_MAX_SHORT_HEIGHT
|
|
42
|
+
: COMPACT_WORKSPACE_ENTER_MAX_SHORT_HEIGHT;
|
|
43
|
+
return height <= shortHeightThreshold;
|
|
26
44
|
}
|
|
27
45
|
|
|
28
46
|
// Mobile Layout Manager
|
|
@@ -46,12 +64,13 @@
|
|
|
46
64
|
document.documentElement.style.setProperty('--app-top', `${offsetTop}px`);
|
|
47
65
|
document.documentElement.style.setProperty('--app-left', `${offsetLeft}px`);
|
|
48
66
|
|
|
67
|
+
const previousCompactWorkspaceMode =
|
|
68
|
+
window.__tabminalCompactWorkspaceMode;
|
|
49
69
|
const compactWorkspaceMode = shouldUseCompactWorkspaceMode(
|
|
50
70
|
width,
|
|
51
|
-
height
|
|
71
|
+
height,
|
|
72
|
+
previousCompactWorkspaceMode
|
|
52
73
|
);
|
|
53
|
-
const previousCompactWorkspaceMode =
|
|
54
|
-
window.__tabminalCompactWorkspaceMode;
|
|
55
74
|
window.__tabminalCompactWorkspaceMode = compactWorkspaceMode;
|
|
56
75
|
|
|
57
76
|
if (document.body) {
|
|
@@ -94,6 +113,16 @@
|
|
|
94
113
|
}
|
|
95
114
|
}
|
|
96
115
|
|
|
116
|
+
function pauseLayoutLoop(duration = SIDEBAR_LAYOUT_PAUSE_MS) {
|
|
117
|
+
const now = performance.now();
|
|
118
|
+
layoutLoopPausedUntil = Math.max(
|
|
119
|
+
layoutLoopPausedUntil,
|
|
120
|
+
now + duration
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
window.__tabminalPauseLayoutLoop = pauseLayoutLoop;
|
|
125
|
+
|
|
97
126
|
if (window.visualViewport) {
|
|
98
127
|
window.visualViewport.addEventListener('resize', updateLayout);
|
|
99
128
|
window.visualViewport.addEventListener('scroll', updateLayout);
|
|
@@ -103,13 +132,34 @@
|
|
|
103
132
|
|
|
104
133
|
// High-performance Layout Loop (60fps)
|
|
105
134
|
function layoutLoop() {
|
|
106
|
-
|
|
135
|
+
if (performance.now() >= layoutLoopPausedUntil) {
|
|
136
|
+
updateLayout();
|
|
137
|
+
}
|
|
107
138
|
requestAnimationFrame(layoutLoop);
|
|
108
139
|
}
|
|
109
140
|
layoutLoop();
|
|
110
141
|
|
|
111
142
|
// Initial triggers
|
|
112
143
|
document.addEventListener('DOMContentLoaded', updateLayout);
|
|
144
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
145
|
+
const sidebar = document.getElementById('sidebar');
|
|
146
|
+
if (!sidebar) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
let sidebarOpen = sidebar.classList.contains('open');
|
|
150
|
+
const observer = new MutationObserver(() => {
|
|
151
|
+
const isOpen = sidebar.classList.contains('open');
|
|
152
|
+
if (isOpen === sidebarOpen) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
sidebarOpen = isOpen;
|
|
156
|
+
pauseLayoutLoop();
|
|
157
|
+
});
|
|
158
|
+
observer.observe(sidebar, {
|
|
159
|
+
attributes: true,
|
|
160
|
+
attributeFilter: ['class']
|
|
161
|
+
});
|
|
162
|
+
});
|
|
113
163
|
window.addEventListener('load', updateLayout);
|
|
114
164
|
|
|
115
165
|
// Enhanced Touch Interceptor with Boundary Check
|