tabctl 0.6.0-rc.10 → 0.6.0-rc.11
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.
|
@@ -795,6 +795,10 @@
|
|
|
795
795
|
var ACTIVE_PAGE_CACHE_STATUS_TIMEOUT_MS = 3e4;
|
|
796
796
|
var CACHE_AVAILABLE_BADGE_TEXT = "C";
|
|
797
797
|
var CACHE_AVAILABLE_BADGE_COLOR = "#2da44e";
|
|
798
|
+
var CACHE_WAITING_BADGE_TEXT = "W";
|
|
799
|
+
var CACHE_WAITING_BADGE_COLOR = "#bf8700";
|
|
800
|
+
var CACHE_ERROR_BADGE_TEXT = "E";
|
|
801
|
+
var CACHE_ERROR_BADGE_COLOR = "#cf222e";
|
|
798
802
|
var RECONNECT_INITIAL_DELAY_MS = 250;
|
|
799
803
|
var RECONNECT_MAX_DELAY_MS = 3e4;
|
|
800
804
|
var RECONNECT_ALARM_MIN_DELAY_MS = 3e4;
|
|
@@ -832,7 +836,8 @@
|
|
|
832
836
|
lastCapturedKey: null,
|
|
833
837
|
lastQuiescentCapturedKey: null,
|
|
834
838
|
lastQuiescentCapturedAt: 0,
|
|
835
|
-
statusRequests: /* @__PURE__ */ new Map()
|
|
839
|
+
statusRequests: /* @__PURE__ */ new Map(),
|
|
840
|
+
diagnostics: /* @__PURE__ */ new Map()
|
|
836
841
|
};
|
|
837
842
|
function log(...args) {
|
|
838
843
|
console.log("[tabctl]", ...args);
|
|
@@ -969,6 +974,9 @@
|
|
|
969
974
|
function activePageCacheKey(tab, url) {
|
|
970
975
|
return `${tab.id}:${url}`;
|
|
971
976
|
}
|
|
977
|
+
function activePageCacheKeyForTabId(tabId, url) {
|
|
978
|
+
return `${tabId}:${url}`;
|
|
979
|
+
}
|
|
972
980
|
function activePageCacheUrl(tab) {
|
|
973
981
|
return tab.url || tab.pendingUrl || "";
|
|
974
982
|
}
|
|
@@ -987,18 +995,63 @@
|
|
|
987
995
|
log("clear cache indicator failed", { tabId, error });
|
|
988
996
|
}
|
|
989
997
|
}
|
|
990
|
-
async function
|
|
998
|
+
async function setCacheBadgeIndicator(tabId, expectedUrl, text, color, title) {
|
|
991
999
|
try {
|
|
992
1000
|
const tab = await chrome.tabs.get(tabId);
|
|
993
1001
|
if (!isEligibleActivePageCacheTab(tab) || activePageCacheUrl(tab) !== expectedUrl) {
|
|
994
1002
|
await clearCacheAvailableIndicator(tabId);
|
|
995
1003
|
return;
|
|
996
1004
|
}
|
|
997
|
-
await chrome.action?.setBadgeBackgroundColor?.({ tabId, color
|
|
998
|
-
await chrome.action?.setBadgeText?.({ tabId, text
|
|
999
|
-
await chrome.action?.setTitle?.({ tabId, title
|
|
1005
|
+
await chrome.action?.setBadgeBackgroundColor?.({ tabId, color });
|
|
1006
|
+
await chrome.action?.setBadgeText?.({ tabId, text });
|
|
1007
|
+
await chrome.action?.setTitle?.({ tabId, title });
|
|
1000
1008
|
} catch (error) {
|
|
1001
|
-
log("set cache indicator failed", { tabId, error });
|
|
1009
|
+
log("set cache indicator failed", { tabId, text, error });
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
async function setCacheAvailableIndicator(tabId, expectedUrl) {
|
|
1013
|
+
await setCacheBadgeIndicator(
|
|
1014
|
+
tabId,
|
|
1015
|
+
expectedUrl,
|
|
1016
|
+
CACHE_AVAILABLE_BADGE_TEXT,
|
|
1017
|
+
CACHE_AVAILABLE_BADGE_COLOR,
|
|
1018
|
+
"Tab Control - page cache available"
|
|
1019
|
+
);
|
|
1020
|
+
}
|
|
1021
|
+
async function setCacheWaitingIndicator(tabId, expectedUrl, detail) {
|
|
1022
|
+
await setCacheBadgeIndicator(
|
|
1023
|
+
tabId,
|
|
1024
|
+
expectedUrl,
|
|
1025
|
+
CACHE_WAITING_BADGE_TEXT,
|
|
1026
|
+
CACHE_WAITING_BADGE_COLOR,
|
|
1027
|
+
`Tab Control - waiting for page cache (${detail})`
|
|
1028
|
+
);
|
|
1029
|
+
}
|
|
1030
|
+
async function setCacheErrorIndicator(tabId, expectedUrl, detail) {
|
|
1031
|
+
await setCacheBadgeIndicator(
|
|
1032
|
+
tabId,
|
|
1033
|
+
expectedUrl,
|
|
1034
|
+
CACHE_ERROR_BADGE_TEXT,
|
|
1035
|
+
CACHE_ERROR_BADGE_COLOR,
|
|
1036
|
+
`Tab Control - page cache error (${detail})`
|
|
1037
|
+
);
|
|
1038
|
+
}
|
|
1039
|
+
function hasPendingActivePageCacheWork(tabId, url) {
|
|
1040
|
+
const key = activePageCacheKeyForTabId(tabId, url);
|
|
1041
|
+
return activePageCache.inFlightKeys.has(key) || activePageCache.pending?.key === key || activePageCache.quiescentPending?.key === key;
|
|
1042
|
+
}
|
|
1043
|
+
function setActivePageCacheDiagnostic(tabId, url, kind, detail) {
|
|
1044
|
+
activePageCache.diagnostics.set(activePageCacheKeyForTabId(tabId, url), { kind, detail });
|
|
1045
|
+
}
|
|
1046
|
+
function clearActivePageCacheDiagnostic(tabId, url) {
|
|
1047
|
+
activePageCache.diagnostics.delete(activePageCacheKeyForTabId(tabId, url));
|
|
1048
|
+
}
|
|
1049
|
+
function clearActivePageCacheDiagnosticsForTab(tabId) {
|
|
1050
|
+
const prefix = `${tabId}:`;
|
|
1051
|
+
for (const key of activePageCache.diagnostics.keys()) {
|
|
1052
|
+
if (key.startsWith(prefix)) {
|
|
1053
|
+
activePageCache.diagnostics.delete(key);
|
|
1054
|
+
}
|
|
1002
1055
|
}
|
|
1003
1056
|
}
|
|
1004
1057
|
async function applyPageCacheStatus(tabId, expectedUrl, available) {
|
|
@@ -1007,10 +1060,28 @@
|
|
|
1007
1060
|
if (activePageCacheUrl(tab) !== expectedUrl) {
|
|
1008
1061
|
return;
|
|
1009
1062
|
}
|
|
1010
|
-
if (!isEligibleActivePageCacheTab(tab)
|
|
1063
|
+
if (!isEligibleActivePageCacheTab(tab)) {
|
|
1011
1064
|
await clearCacheAvailableIndicator(tabId);
|
|
1012
1065
|
return;
|
|
1013
1066
|
}
|
|
1067
|
+
if (!available) {
|
|
1068
|
+
const diagnostic = activePageCache.diagnostics.get(activePageCacheKeyForTabId(tabId, expectedUrl));
|
|
1069
|
+
if (diagnostic?.kind === "waiting") {
|
|
1070
|
+
await setCacheWaitingIndicator(tabId, expectedUrl, diagnostic.detail);
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
if (diagnostic?.kind === "error") {
|
|
1074
|
+
await setCacheErrorIndicator(tabId, expectedUrl, diagnostic.detail);
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
if (hasPendingActivePageCacheWork(tabId, expectedUrl)) {
|
|
1078
|
+
await setCacheWaitingIndicator(tabId, expectedUrl, "capture pending");
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
await clearCacheAvailableIndicator(tabId);
|
|
1082
|
+
return;
|
|
1083
|
+
}
|
|
1084
|
+
clearActivePageCacheDiagnostic(tabId, expectedUrl);
|
|
1014
1085
|
await setCacheAvailableIndicator(tabId, expectedUrl);
|
|
1015
1086
|
} catch {
|
|
1016
1087
|
await clearCacheAvailableIndicator(tabId);
|
|
@@ -1044,7 +1115,7 @@
|
|
|
1044
1115
|
const port = state.port;
|
|
1045
1116
|
if (!port) {
|
|
1046
1117
|
connectNative();
|
|
1047
|
-
void
|
|
1118
|
+
void setCacheWaitingIndicator(tab.id, url, "native host reconnecting");
|
|
1048
1119
|
return;
|
|
1049
1120
|
}
|
|
1050
1121
|
const id = nextActivePageCacheStatusId();
|
|
@@ -1144,9 +1215,11 @@
|
|
|
1144
1215
|
clearPendingQuiescentActivePageCacheCapture();
|
|
1145
1216
|
return;
|
|
1146
1217
|
}
|
|
1147
|
-
requestPageCacheStatus(tab, reason);
|
|
1148
1218
|
const url = activePageCacheUrl(tab);
|
|
1149
1219
|
const key = activePageCacheKey(tab, url);
|
|
1220
|
+
clearActivePageCacheDiagnostic(tab.id, url);
|
|
1221
|
+
void setCacheWaitingIndicator(tab.id, url, "checking status");
|
|
1222
|
+
requestPageCacheStatus(tab, reason);
|
|
1150
1223
|
scheduleQuiescentActivePageCacheCapture(tab, reason, key);
|
|
1151
1224
|
if (activePageCache.inFlightKeys.has(key) || activePageCache.lastCapturedKey === key) {
|
|
1152
1225
|
return;
|
|
@@ -1252,6 +1325,27 @@
|
|
|
1252
1325
|
}
|
|
1253
1326
|
const extraction = await content.extractPageHtml(captureTab.id, ACTIVE_PAGE_CACHE_TIMEOUT_MS, ACTIVE_PAGE_CACHE_MAX_HTML_CHARS);
|
|
1254
1327
|
if (extraction.status !== "READ" || typeof extraction.html !== "string" || extraction.html.length === 0) {
|
|
1328
|
+
log("active page cache extraction not readable", {
|
|
1329
|
+
tabId: captureTab.id,
|
|
1330
|
+
reason,
|
|
1331
|
+
key,
|
|
1332
|
+
status: extraction.status,
|
|
1333
|
+
error: extraction.error,
|
|
1334
|
+
sourceHtmlChars: extraction.sourceHtmlChars,
|
|
1335
|
+
sourceTextChars: extraction.sourceTextChars,
|
|
1336
|
+
documentReadyState: extraction.documentReadyState,
|
|
1337
|
+
truncatedHtml: extraction.truncatedHtml
|
|
1338
|
+
});
|
|
1339
|
+
if (extraction.status === "NOT_LOADED") {
|
|
1340
|
+
const url = activePageCacheUrl(captureTab);
|
|
1341
|
+
setActivePageCacheDiagnostic(captureTab.id, url, "waiting", "page still loading");
|
|
1342
|
+
void setCacheWaitingIndicator(captureTab.id, url, "page still loading");
|
|
1343
|
+
} else {
|
|
1344
|
+
const detail = typeof extraction.status === "string" ? extraction.status.toLowerCase().replace(/_/g, " ") : "capture failed";
|
|
1345
|
+
const url = activePageCacheUrl(captureTab);
|
|
1346
|
+
setActivePageCacheDiagnostic(captureTab.id, url, "error", detail);
|
|
1347
|
+
void setCacheErrorIndicator(captureTab.id, url, detail);
|
|
1348
|
+
}
|
|
1255
1349
|
void requestPageCacheStatusForTab(captureTab.id, `${reason}:capture-failed`);
|
|
1256
1350
|
return false;
|
|
1257
1351
|
}
|
|
@@ -1294,6 +1388,9 @@
|
|
|
1294
1388
|
return true;
|
|
1295
1389
|
} catch (error) {
|
|
1296
1390
|
log("active page cache capture failed", { tabId: tab.id, reason, error });
|
|
1391
|
+
const url = activePageCacheUrl(tab);
|
|
1392
|
+
setActivePageCacheDiagnostic(tab.id, url, "error", "capture exception");
|
|
1393
|
+
void setCacheErrorIndicator(tab.id, url, "capture exception");
|
|
1297
1394
|
void requestPageCacheStatusForTab(tab.id, `${reason}:capture-error`);
|
|
1298
1395
|
return false;
|
|
1299
1396
|
} finally {
|
|
@@ -1417,6 +1514,7 @@
|
|
|
1417
1514
|
changeInfo
|
|
1418
1515
|
});
|
|
1419
1516
|
if ("url" in changeInfo || "status" in changeInfo || "discarded" in changeInfo) {
|
|
1517
|
+
clearActivePageCacheDiagnosticsForTab(tabId);
|
|
1420
1518
|
void clearCacheAvailableIndicator(tabId);
|
|
1421
1519
|
}
|
|
1422
1520
|
if (tab.active && ("url" in changeInfo || "status" in changeInfo || "discarded" in changeInfo)) {
|
|
@@ -1456,6 +1554,7 @@
|
|
|
1456
1554
|
activePageCache.statusRequests.delete(requestId);
|
|
1457
1555
|
}
|
|
1458
1556
|
});
|
|
1557
|
+
clearActivePageCacheDiagnosticsForTab(tabId);
|
|
1459
1558
|
void clearCacheAvailableIndicator(tabId);
|
|
1460
1559
|
});
|
|
1461
1560
|
chrome.tabs?.onActivated?.addListener((activeInfo) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tabctl",
|
|
3
|
-
"version": "0.6.0-rc.
|
|
3
|
+
"version": "0.6.0-rc.11",
|
|
4
4
|
"description": "CLI tool to manage and analyze browser tabs",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"typescript": "^5.4.5"
|
|
54
54
|
},
|
|
55
55
|
"optionalDependencies": {
|
|
56
|
-
"tabctl-win32-x64": "0.6.0-rc.
|
|
56
|
+
"tabctl-win32-x64": "0.6.0-rc.11"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
59
|
"normalize-url": "^8.1.1"
|