git-watchtower 1.10.2 → 1.10.3
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 +8 -7
- package/src/server/web-ui/js.js +56 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "git-watchtower",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.3",
|
|
4
4
|
"description": "Terminal-based Git branch monitor with activity sparklines and optional dev server with live reload",
|
|
5
5
|
"main": "bin/git-watchtower.js",
|
|
6
6
|
"bin": {
|
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"start": "node bin/git-watchtower.js",
|
|
11
|
-
"test": "node --require ./tests/setup.js --test tests/unit/**/*.test.js tests/integration/**/*.test.js",
|
|
12
|
-
"test:unit": "node --require ./tests/setup.js --test tests/unit/**/*.test.js",
|
|
11
|
+
"test": "node --require ./tests/setup.js --test tests/unit/**/*.test.js tests/unit/**/**/*.test.js tests/integration/**/*.test.js",
|
|
12
|
+
"test:unit": "node --require ./tests/setup.js --test tests/unit/**/*.test.js tests/unit/**/**/*.test.js",
|
|
13
13
|
"test:integration": "node --require ./tests/setup.js --test tests/integration/**/*.test.js",
|
|
14
|
-
"test:watch": "node --require ./tests/setup.js --test --watch tests/unit/**/*.test.js",
|
|
15
|
-
"test:coverage": "c8 --reporter=text --reporter=html --reporter=lcov node --require ./tests/setup.js --test tests/unit/**/*.test.js tests/integration/**/*.test.js",
|
|
16
|
-
"test:coverage:text": "c8 --reporter=text node --require ./tests/setup.js --test tests/unit/**/*.test.js tests/integration/**/*.test.js",
|
|
17
|
-
"test:coverage:html": "c8 --reporter=html node --require ./tests/setup.js --test tests/unit/**/*.test.js tests/integration/**/*.test.js && echo 'Coverage report: coverage/index.html'",
|
|
14
|
+
"test:watch": "node --require ./tests/setup.js --test --watch tests/unit/**/*.test.js tests/unit/**/**/*.test.js",
|
|
15
|
+
"test:coverage": "c8 --reporter=text --reporter=html --reporter=lcov node --require ./tests/setup.js --test tests/unit/**/*.test.js tests/unit/**/**/*.test.js tests/integration/**/*.test.js",
|
|
16
|
+
"test:coverage:text": "c8 --reporter=text node --require ./tests/setup.js --test tests/unit/**/*.test.js tests/unit/**/**/*.test.js tests/integration/**/*.test.js",
|
|
17
|
+
"test:coverage:html": "c8 --reporter=html node --require ./tests/setup.js --test tests/unit/**/*.test.js tests/unit/**/**/*.test.js tests/integration/**/*.test.js && echo 'Coverage report: coverage/index.html'",
|
|
18
18
|
"typecheck": "tsc --noEmit"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"@semantic-release/git": "^10.0.1",
|
|
23
23
|
"@types/node": "^22.0.0",
|
|
24
24
|
"c8": "^10.1.2",
|
|
25
|
+
"jsdom": "^24.1.3",
|
|
25
26
|
"semantic-release": "^25.0.3",
|
|
26
27
|
"typescript": "^5.7.0"
|
|
27
28
|
},
|
package/src/server/web-ui/js.js
CHANGED
|
@@ -34,6 +34,7 @@ function getDashboardJs() {
|
|
|
34
34
|
var stashMode = false;
|
|
35
35
|
var pendingStashBranch = null;
|
|
36
36
|
var updateNotificationShown = false;
|
|
37
|
+
var remoteTabPollTimer = null;
|
|
37
38
|
|
|
38
39
|
// ── Persistent Preferences (localStorage) ─────────────────────
|
|
39
40
|
var PREFS_KEY = 'git-watchtower-prefs';
|
|
@@ -187,14 +188,33 @@ function getDashboardJs() {
|
|
|
187
188
|
evtSource.addEventListener('state', function(e) {
|
|
188
189
|
try {
|
|
189
190
|
var newState = JSON.parse(e.data);
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
diffBranchesForNotifications(state.branches, newState.branches || []);
|
|
191
|
+
if (!activeTabId && newState.activeProjectId) {
|
|
192
|
+
activeTabId = newState.activeProjectId;
|
|
193
193
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
194
|
+
// SSE always pushes the local project's state. When the user
|
|
195
|
+
// is viewing a different tab we must NOT overwrite the per-project
|
|
196
|
+
// data (branches, PRs, activity, etc.) — only update global
|
|
197
|
+
// metadata so the tab bar, connection status, and version info
|
|
198
|
+
// stay current.
|
|
199
|
+
var viewingLocalProject = !activeTabId || activeTabId === newState.activeProjectId;
|
|
200
|
+
if (viewingLocalProject) {
|
|
201
|
+
// Diff branches for desktop notifications
|
|
202
|
+
if (state && state.branches) {
|
|
203
|
+
diffBranchesForNotifications(state.branches, newState.branches || []);
|
|
204
|
+
}
|
|
205
|
+
prevBranches = state ? state.branches : null;
|
|
206
|
+
state = newState;
|
|
207
|
+
} else {
|
|
208
|
+
// Viewing a remote tab — preserve per-project fields, update globals only
|
|
209
|
+
if (state) {
|
|
210
|
+
state.projects = newState.projects;
|
|
211
|
+
state.version = newState.version;
|
|
212
|
+
state.updateAvailable = newState.updateAvailable;
|
|
213
|
+
state.updateInProgress = newState.updateInProgress;
|
|
214
|
+
state.clientCount = newState.clientCount;
|
|
215
|
+
} else {
|
|
216
|
+
state = newState;
|
|
217
|
+
}
|
|
198
218
|
}
|
|
199
219
|
renderTabs();
|
|
200
220
|
render();
|
|
@@ -331,23 +351,17 @@ function getDashboardJs() {
|
|
|
331
351
|
tabBar.innerHTML = html;
|
|
332
352
|
}
|
|
333
353
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
searchMode = false;
|
|
340
|
-
document.getElementById('search-bar').className = 'search-bar';
|
|
341
|
-
document.getElementById('search-input').value = '';
|
|
342
|
-
renderTabs();
|
|
343
|
-
// Fetch the project's state
|
|
354
|
+
/**
|
|
355
|
+
* Fetch a project's state from the server and merge it into the
|
|
356
|
+
* current client-side state for rendering.
|
|
357
|
+
*/
|
|
358
|
+
function fetchAndApplyProjectState(projectId) {
|
|
344
359
|
var xhr = new XMLHttpRequest();
|
|
345
360
|
xhr.open('GET', '/api/projects/' + projectId + '/state');
|
|
346
361
|
xhr.onload = function() {
|
|
347
|
-
if (xhr.status === 200) {
|
|
362
|
+
if (xhr.status === 200 && activeTabId === projectId) {
|
|
348
363
|
try {
|
|
349
364
|
var pState = JSON.parse(xhr.responseText);
|
|
350
|
-
// Merge into current state for rendering
|
|
351
365
|
state.branches = pState.branches || [];
|
|
352
366
|
state.currentBranch = pState.currentBranch;
|
|
353
367
|
state.activityLog = pState.activityLog || [];
|
|
@@ -359,6 +373,7 @@ function getDashboardJs() {
|
|
|
359
373
|
state.pollingStatus = pState.pollingStatus || 'idle';
|
|
360
374
|
state.isOffline = pState.isOffline || false;
|
|
361
375
|
state.serverMode = pState.serverMode || 'none';
|
|
376
|
+
state.repoWebUrl = pState.repoWebUrl || null;
|
|
362
377
|
render();
|
|
363
378
|
} catch (err) { /* ignore */ }
|
|
364
379
|
}
|
|
@@ -366,6 +381,28 @@ function getDashboardJs() {
|
|
|
366
381
|
xhr.send();
|
|
367
382
|
}
|
|
368
383
|
|
|
384
|
+
function switchTab(projectId) {
|
|
385
|
+
if (projectId === activeTabId) return;
|
|
386
|
+
activeTabId = projectId;
|
|
387
|
+
selectedIndex = 0;
|
|
388
|
+
searchQuery = '';
|
|
389
|
+
searchMode = false;
|
|
390
|
+
document.getElementById('search-bar').className = 'search-bar';
|
|
391
|
+
document.getElementById('search-input').value = '';
|
|
392
|
+
renderTabs();
|
|
393
|
+
fetchAndApplyProjectState(projectId);
|
|
394
|
+
|
|
395
|
+
// For non-local tabs the SSE stream won't push per-project updates,
|
|
396
|
+
// so poll the server periodically to keep the view fresh.
|
|
397
|
+
clearInterval(remoteTabPollTimer);
|
|
398
|
+
remoteTabPollTimer = null;
|
|
399
|
+
if (state && projectId !== state.activeProjectId) {
|
|
400
|
+
remoteTabPollTimer = setInterval(function() {
|
|
401
|
+
fetchAndApplyProjectState(projectId);
|
|
402
|
+
}, 2000);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
369
406
|
// ── Time Formatting ────────────────────────────────────────────
|
|
370
407
|
function timeAgo(dateStr) {
|
|
371
408
|
if (!dateStr) return '';
|