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 setCacheAvailableIndicator(tabId, expectedUrl) {
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: CACHE_AVAILABLE_BADGE_COLOR });
998
- await chrome.action?.setBadgeText?.({ tabId, text: CACHE_AVAILABLE_BADGE_TEXT });
999
- await chrome.action?.setTitle?.({ tabId, title: "Tab Control - page cache available" });
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) || !available) {
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 clearCacheAvailableIndicator(tab.id);
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) => {
@@ -22,5 +22,5 @@
22
22
  "action": {
23
23
  "default_title": "Tab Control"
24
24
  },
25
- "version_name": "0.6.0-rc.10"
25
+ "version_name": "0.6.0-rc.11"
26
26
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tabctl",
3
- "version": "0.6.0-rc.10",
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.10"
56
+ "tabctl-win32-x64": "0.6.0-rc.11"
57
57
  },
58
58
  "dependencies": {
59
59
  "normalize-url": "^8.1.1"