tabctl 0.6.0-alpha.7 → 0.6.0-alpha.8
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/README.md +2 -2
- package/dist/extension/background.js +170 -63
- package/dist/extension/lib/archive.js +31 -6
- package/dist/extension/lib/groups.js +33 -4
- package/dist/extension/lib/inspect.js +30 -28
- package/dist/extension/lib/move.js +38 -4
- package/dist/extension/lib/screenshot.js +2 -11
- package/dist/extension/lib/tabs.js +15 -10
- package/dist/extension/manifest.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -264,7 +264,7 @@ Relevant knobs: `TABCTL_SOCKET`, `TABCTL_TCP_PORT`, `TABCTL_PROFILE`, `TABCTL_DA
|
|
|
264
264
|
- Runtime command runs can auto-sync extension files when host/extension versions drift; rerun `tabctl reload` if the browser does not pick up changes immediately.
|
|
265
265
|
- For local release-like testing while developing, force runtime sync behavior with `TABCTL_AUTO_SYNC_MODE=release-like`.
|
|
266
266
|
- Disable runtime sync entirely with `TABCTL_AUTO_SYNC_MODE=off`.
|
|
267
|
-
- `tabctl ping --json` is the canonical runtime version check (`
|
|
267
|
+
- `tabctl ping --json` is the canonical runtime version check (`versionsInSync`, `hostBaseVersion`, `baseVersion`).
|
|
268
268
|
- Version metadata is intentionally health-only: regular command payloads (`open`, `list`, etc.) do not include version fields.
|
|
269
269
|
- `tabctl ping` returns connect errors (`ENOENT`, `ECONNREFUSED`, timeout): ensure extension is loaded and active, rerun `tabctl setup`, and in WSL verify `TABCTL_TCP_PORT` or `<dataDir>/tcp-port` matches a listening localhost port.
|
|
270
270
|
- `tabctl doctor --fix --json` includes per-profile connectivity diagnostics in `data.profiles[].connectivity`; if ping remains unhealthy after local repairs, follow `manualSteps`.
|
|
@@ -409,5 +409,5 @@ Notes:
|
|
|
409
409
|
- Selector `attr` supports `href-url`/`src-url` to return absolute http(s) URLs.
|
|
410
410
|
- `screenshot --out` writes per-tab folders into the target directory.
|
|
411
411
|
- `tabctl undo` accepts a positional txid, `--txid`, or `--latest`.
|
|
412
|
-
- `tabctl history --json` returns a JSON array
|
|
412
|
+
- `tabctl history --json` returns a top-level JSON array.
|
|
413
413
|
- `--format` is only supported by `report` (use `--json` elsewhere).
|
|
@@ -318,7 +318,6 @@
|
|
|
318
318
|
const tabs2 = selection.tabs;
|
|
319
319
|
const entries = [];
|
|
320
320
|
let totalTiles = 0;
|
|
321
|
-
const startedAt = Date.now();
|
|
322
321
|
for (let index = 0; index < tabs2.length; index += 1) {
|
|
323
322
|
const tab = tabs2[index];
|
|
324
323
|
const tabId = tab.tabId;
|
|
@@ -374,19 +373,11 @@
|
|
|
374
373
|
deps2.sendProgress(requestId, { phase: "screenshot", processed: index + 1, total: tabs2.length, tabId });
|
|
375
374
|
}
|
|
376
375
|
}
|
|
377
|
-
|
|
378
|
-
generatedAt: Date.now(),
|
|
376
|
+
const response = {
|
|
379
377
|
totals: { tabs: tabs2.length, tiles: totalTiles },
|
|
380
|
-
meta: {
|
|
381
|
-
durationMs: Date.now() - startedAt,
|
|
382
|
-
mode,
|
|
383
|
-
format,
|
|
384
|
-
quality: format === "jpeg" ? quality : null,
|
|
385
|
-
tileMaxDim: adjustedTileMaxDim,
|
|
386
|
-
maxBytes: adjustedMaxBytes
|
|
387
|
-
},
|
|
388
378
|
entries
|
|
389
379
|
};
|
|
380
|
+
return response;
|
|
390
381
|
}
|
|
391
382
|
}
|
|
392
383
|
});
|
|
@@ -876,7 +867,7 @@
|
|
|
876
867
|
throw new Error("Missing group update fields");
|
|
877
868
|
}
|
|
878
869
|
const updated = await chrome.tabGroups.update(match.group.groupId, update);
|
|
879
|
-
|
|
870
|
+
const fullResult = {
|
|
880
871
|
groupId: updated.id,
|
|
881
872
|
windowId: updated.windowId,
|
|
882
873
|
title: updated.title,
|
|
@@ -894,6 +885,13 @@
|
|
|
894
885
|
},
|
|
895
886
|
txid: params.txid || null
|
|
896
887
|
};
|
|
888
|
+
return {
|
|
889
|
+
groupId: updated.id,
|
|
890
|
+
windowId: updated.windowId,
|
|
891
|
+
summary: { updatedGroups: 1 },
|
|
892
|
+
undo: fullResult.undo,
|
|
893
|
+
txid: fullResult.txid
|
|
894
|
+
};
|
|
897
895
|
}
|
|
898
896
|
async function groupUngroup2(params, deps2) {
|
|
899
897
|
const groupId = Number.isFinite(params.groupId) ? Number(params.groupId) : null;
|
|
@@ -937,7 +935,7 @@
|
|
|
937
935
|
if (tabIds.length) {
|
|
938
936
|
await chrome.tabs.ungroup(tabIds);
|
|
939
937
|
}
|
|
940
|
-
|
|
938
|
+
const fullResult = {
|
|
941
939
|
groupId: match.group.groupId,
|
|
942
940
|
groupTitle: match.group.title || null,
|
|
943
941
|
windowId: match.windowId,
|
|
@@ -955,6 +953,13 @@
|
|
|
955
953
|
},
|
|
956
954
|
txid: params.txid || null
|
|
957
955
|
};
|
|
956
|
+
return {
|
|
957
|
+
groupId: match.group.groupId,
|
|
958
|
+
windowId: match.windowId,
|
|
959
|
+
summary: fullResult.summary,
|
|
960
|
+
undo: fullResult.undo,
|
|
961
|
+
txid: fullResult.txid
|
|
962
|
+
};
|
|
958
963
|
}
|
|
959
964
|
async function groupAssign2(params, deps2) {
|
|
960
965
|
const rawTabIds = Array.isArray(params.tabIds) ? params.tabIds.map(Number) : [];
|
|
@@ -1078,7 +1083,7 @@
|
|
|
1078
1083
|
}
|
|
1079
1084
|
created = true;
|
|
1080
1085
|
}
|
|
1081
|
-
|
|
1086
|
+
const fullResult = {
|
|
1082
1087
|
groupId: assignedGroupId,
|
|
1083
1088
|
groupTitle: targetTitle || groupTitle || null,
|
|
1084
1089
|
windowId: targetWindowId,
|
|
@@ -1100,6 +1105,15 @@
|
|
|
1100
1105
|
},
|
|
1101
1106
|
txid: params.txid || null
|
|
1102
1107
|
};
|
|
1108
|
+
return {
|
|
1109
|
+
groupId: assignedGroupId,
|
|
1110
|
+
windowId: targetWindowId,
|
|
1111
|
+
created,
|
|
1112
|
+
summary: fullResult.summary,
|
|
1113
|
+
skipped: fullResult.skipped,
|
|
1114
|
+
undo: fullResult.undo,
|
|
1115
|
+
txid: fullResult.txid
|
|
1116
|
+
};
|
|
1103
1117
|
}
|
|
1104
1118
|
async function groupGather2(params, deps2) {
|
|
1105
1119
|
const snapshot = await deps2.getTabSnapshot();
|
|
@@ -1167,7 +1181,7 @@
|
|
|
1167
1181
|
});
|
|
1168
1182
|
}
|
|
1169
1183
|
}
|
|
1170
|
-
|
|
1184
|
+
const fullResult = {
|
|
1171
1185
|
merged,
|
|
1172
1186
|
summary: {
|
|
1173
1187
|
mergedGroups: merged.reduce((sum, m) => sum + m.mergedGroupCount, 0),
|
|
@@ -1179,6 +1193,12 @@
|
|
|
1179
1193
|
},
|
|
1180
1194
|
txid: params.txid || null
|
|
1181
1195
|
};
|
|
1196
|
+
return {
|
|
1197
|
+
merged,
|
|
1198
|
+
summary: fullResult.summary,
|
|
1199
|
+
undo: fullResult.undo,
|
|
1200
|
+
txid: fullResult.txid
|
|
1201
|
+
};
|
|
1182
1202
|
}
|
|
1183
1203
|
}
|
|
1184
1204
|
});
|
|
@@ -1460,6 +1480,15 @@
|
|
|
1460
1480
|
const index = Number(value);
|
|
1461
1481
|
return Number.isFinite(index) ? index : null;
|
|
1462
1482
|
}
|
|
1483
|
+
function shapeOpenResult(result) {
|
|
1484
|
+
return {
|
|
1485
|
+
windowId: result.windowId,
|
|
1486
|
+
groupId: result.groupId,
|
|
1487
|
+
createdTabIds: result.created.map((tab) => tab.tabId).filter((id) => typeof id === "number"),
|
|
1488
|
+
skipped: result.skipped,
|
|
1489
|
+
summary: result.summary
|
|
1490
|
+
};
|
|
1491
|
+
}
|
|
1463
1492
|
function matchIncludes(value, needle) {
|
|
1464
1493
|
if (!needle) {
|
|
1465
1494
|
return false;
|
|
@@ -1651,12 +1680,9 @@
|
|
|
1651
1680
|
groupId2 = null;
|
|
1652
1681
|
}
|
|
1653
1682
|
}
|
|
1654
|
-
return {
|
|
1683
|
+
return shapeOpenResult({
|
|
1655
1684
|
windowId: windowId2,
|
|
1656
1685
|
groupId: groupId2,
|
|
1657
|
-
groupTitle: groupTitle || null,
|
|
1658
|
-
afterGroupTitle: null,
|
|
1659
|
-
insertIndex: null,
|
|
1660
1686
|
created: created2,
|
|
1661
1687
|
skipped: skipped2,
|
|
1662
1688
|
summary: {
|
|
@@ -1664,7 +1690,7 @@
|
|
|
1664
1690
|
skippedUrls: skipped2.length,
|
|
1665
1691
|
grouped: Boolean(groupId2)
|
|
1666
1692
|
}
|
|
1667
|
-
};
|
|
1693
|
+
});
|
|
1668
1694
|
}
|
|
1669
1695
|
const snapshot = await deps2.getTabSnapshot();
|
|
1670
1696
|
let openParams = params;
|
|
@@ -1855,12 +1881,9 @@
|
|
|
1855
1881
|
deps2.log("Failed delayed grouping verification", error);
|
|
1856
1882
|
}
|
|
1857
1883
|
}
|
|
1858
|
-
return {
|
|
1884
|
+
return shapeOpenResult({
|
|
1859
1885
|
windowId,
|
|
1860
1886
|
groupId,
|
|
1861
|
-
groupTitle: groupTitle || null,
|
|
1862
|
-
afterGroupTitle: afterGroupTitle || null,
|
|
1863
|
-
insertIndex,
|
|
1864
1887
|
created,
|
|
1865
1888
|
skipped,
|
|
1866
1889
|
summary: {
|
|
@@ -1868,7 +1891,7 @@
|
|
|
1868
1891
|
skippedUrls: skipped.length,
|
|
1869
1892
|
grouped: Boolean(groupId)
|
|
1870
1893
|
}
|
|
1871
|
-
};
|
|
1894
|
+
});
|
|
1872
1895
|
}
|
|
1873
1896
|
}
|
|
1874
1897
|
});
|
|
@@ -1987,7 +2010,7 @@
|
|
|
1987
2010
|
targetIndex2 = 0;
|
|
1988
2011
|
}
|
|
1989
2012
|
}
|
|
1990
|
-
|
|
2013
|
+
const fullResult2 = {
|
|
1991
2014
|
tabId,
|
|
1992
2015
|
from: { windowId: sourceWindow.windowId, index: sourceTab.index },
|
|
1993
2016
|
to: { windowId: targetWindowId2, index: targetIndex2 },
|
|
@@ -2010,6 +2033,14 @@
|
|
|
2010
2033
|
},
|
|
2011
2034
|
txid: params.txid || null
|
|
2012
2035
|
};
|
|
2036
|
+
return {
|
|
2037
|
+
tabId,
|
|
2038
|
+
fromWindowId: sourceWindow.windowId,
|
|
2039
|
+
toWindowId: targetWindowId2,
|
|
2040
|
+
summary: fullResult2.summary,
|
|
2041
|
+
undo: fullResult2.undo,
|
|
2042
|
+
txid: fullResult2.txid
|
|
2043
|
+
};
|
|
2013
2044
|
}
|
|
2014
2045
|
let normalizedParams = params;
|
|
2015
2046
|
if (params.windowId != null) {
|
|
@@ -2027,7 +2058,7 @@
|
|
|
2027
2058
|
targetIndex -= 1;
|
|
2028
2059
|
}
|
|
2029
2060
|
const moved = await chrome.tabs.move(tabId, { windowId: targetWindowId, index: targetIndex });
|
|
2030
|
-
|
|
2061
|
+
const fullResult = {
|
|
2031
2062
|
tabId,
|
|
2032
2063
|
from: { windowId: sourceWindow.windowId, index: sourceTab.index },
|
|
2033
2064
|
to: { windowId: targetWindowId, index: moved.index },
|
|
@@ -2050,6 +2081,14 @@
|
|
|
2050
2081
|
},
|
|
2051
2082
|
txid: params.txid || null
|
|
2052
2083
|
};
|
|
2084
|
+
return {
|
|
2085
|
+
tabId,
|
|
2086
|
+
fromWindowId: sourceWindow.windowId,
|
|
2087
|
+
toWindowId: targetWindowId,
|
|
2088
|
+
summary: fullResult.summary,
|
|
2089
|
+
undo: fullResult.undo,
|
|
2090
|
+
txid: fullResult.txid
|
|
2091
|
+
};
|
|
2053
2092
|
}
|
|
2054
2093
|
async function moveGroup(params, deps2) {
|
|
2055
2094
|
const groupId = Number.isFinite(params.groupId) ? Number(params.groupId) : null;
|
|
@@ -2126,7 +2165,7 @@
|
|
|
2126
2165
|
groupColor: tab.groupColor,
|
|
2127
2166
|
groupCollapsed: source.group.collapsed ?? null
|
|
2128
2167
|
})).filter((tab) => typeof tab.tabId === "number");
|
|
2129
|
-
|
|
2168
|
+
const fullResult2 = {
|
|
2130
2169
|
groupId: source.group.groupId,
|
|
2131
2170
|
windowId: source.windowId,
|
|
2132
2171
|
movedToWindowId: targetWindowId2,
|
|
@@ -2144,6 +2183,15 @@
|
|
|
2144
2183
|
},
|
|
2145
2184
|
txid: params.txid || null
|
|
2146
2185
|
};
|
|
2186
|
+
return {
|
|
2187
|
+
groupId: source.group.groupId,
|
|
2188
|
+
windowId: source.windowId,
|
|
2189
|
+
movedToWindowId: targetWindowId2,
|
|
2190
|
+
newGroupId: newGroupId2,
|
|
2191
|
+
summary: fullResult2.summary,
|
|
2192
|
+
undo: fullResult2.undo,
|
|
2193
|
+
txid: fullResult2.txid
|
|
2194
|
+
};
|
|
2147
2195
|
}
|
|
2148
2196
|
const target = resolveMoveTarget(snapshot, params, deps2);
|
|
2149
2197
|
if (target.error) {
|
|
@@ -2198,7 +2246,7 @@
|
|
|
2198
2246
|
groupColor: tab.groupColor,
|
|
2199
2247
|
groupCollapsed: source.group.collapsed ?? null
|
|
2200
2248
|
})).filter((tab) => typeof tab.tabId === "number");
|
|
2201
|
-
|
|
2249
|
+
const fullResult = {
|
|
2202
2250
|
groupId: source.group.groupId,
|
|
2203
2251
|
windowId: source.windowId,
|
|
2204
2252
|
movedToWindowId: targetWindowId,
|
|
@@ -2216,6 +2264,15 @@
|
|
|
2216
2264
|
},
|
|
2217
2265
|
txid: params.txid || null
|
|
2218
2266
|
};
|
|
2267
|
+
return {
|
|
2268
|
+
groupId: source.group.groupId,
|
|
2269
|
+
windowId: source.windowId,
|
|
2270
|
+
movedToWindowId: targetWindowId,
|
|
2271
|
+
newGroupId,
|
|
2272
|
+
summary: fullResult.summary,
|
|
2273
|
+
undo: fullResult.undo,
|
|
2274
|
+
txid: fullResult.txid
|
|
2275
|
+
};
|
|
2219
2276
|
}
|
|
2220
2277
|
}
|
|
2221
2278
|
});
|
|
@@ -2246,7 +2303,6 @@
|
|
|
2246
2303
|
const selectedTabs = selection.tabs;
|
|
2247
2304
|
const scopeTabs = selectedTabs;
|
|
2248
2305
|
const now = Date.now();
|
|
2249
|
-
const startedAt = Date.now();
|
|
2250
2306
|
const normalizedMap = /* @__PURE__ */ new Map();
|
|
2251
2307
|
const duplicates = /* @__PURE__ */ new Map();
|
|
2252
2308
|
for (const tab of scopeTabs) {
|
|
@@ -2299,19 +2355,16 @@
|
|
|
2299
2355
|
severity
|
|
2300
2356
|
};
|
|
2301
2357
|
});
|
|
2302
|
-
|
|
2303
|
-
generatedAt: Date.now(),
|
|
2358
|
+
const response = {
|
|
2304
2359
|
staleDays,
|
|
2305
2360
|
totals: {
|
|
2306
2361
|
tabs: scopeTabs.length,
|
|
2307
2362
|
analyzed: selectedTabs.length,
|
|
2308
2363
|
candidates: candidates.length
|
|
2309
2364
|
},
|
|
2310
|
-
meta: {
|
|
2311
|
-
durationMs: Date.now() - startedAt
|
|
2312
|
-
},
|
|
2313
2365
|
candidates
|
|
2314
2366
|
};
|
|
2367
|
+
return response;
|
|
2315
2368
|
}
|
|
2316
2369
|
async function inspectTabs(params, requestId, deps2) {
|
|
2317
2370
|
const signalList = Array.isArray(params.signals) && params.signals.length > 0 ? params.signals.map(String) : ["page-meta"];
|
|
@@ -2333,7 +2386,6 @@
|
|
|
2333
2386
|
throw selection.error;
|
|
2334
2387
|
}
|
|
2335
2388
|
const tabs3 = selection.tabs;
|
|
2336
|
-
const startedAt = Date.now();
|
|
2337
2389
|
const selectorSpecs = [];
|
|
2338
2390
|
if (Array.isArray(params.selectorSpecs)) {
|
|
2339
2391
|
selectorSpecs.push(...params.selectorSpecs);
|
|
@@ -2351,17 +2403,24 @@
|
|
|
2351
2403
|
}
|
|
2352
2404
|
}
|
|
2353
2405
|
}
|
|
2354
|
-
const normalizedSelectors = selectorSpecs.filter((spec) => spec && typeof spec.selector === "string" && spec.selector.length > 0).map((spec) =>
|
|
2355
|
-
|
|
2406
|
+
const normalizedSelectors = selectorSpecs.filter((spec) => spec && typeof spec.selector === "string" && spec.selector.length > 0).map((spec) => {
|
|
2407
|
+
const selector = spec.selector;
|
|
2408
|
+
return {
|
|
2409
|
+
name: typeof spec.name === "string" ? spec.name : void 0,
|
|
2410
|
+
selector,
|
|
2411
|
+
attr: typeof spec.attr === "string" ? spec.attr : "text",
|
|
2412
|
+
all: Boolean(spec.all),
|
|
2413
|
+
text: typeof spec.text === "string" && spec.text.trim() ? spec.text.trim() : void 0,
|
|
2414
|
+
textMode: typeof spec.textMode === "string" ? spec.textMode.trim().toLowerCase() : void 0
|
|
2415
|
+
};
|
|
2416
|
+
});
|
|
2417
|
+
const selectorWarnings = normalizedSelectors.filter((spec) => spec.selector.includes(":contains(")).map((spec) => ({
|
|
2418
|
+
code: "unsupported_selector_syntax",
|
|
2419
|
+
signalId: "selector",
|
|
2356
2420
|
selector: spec.selector,
|
|
2357
|
-
attr: typeof spec.attr === "string" ? spec.attr : "text",
|
|
2358
|
-
all: Boolean(spec.all),
|
|
2359
|
-
text: typeof spec.text === "string" && spec.text.trim() ? spec.text.trim() : void 0,
|
|
2360
|
-
textMode: typeof spec.textMode === "string" ? spec.textMode.trim().toLowerCase() : void 0
|
|
2361
|
-
}));
|
|
2362
|
-
const selectorWarnings = normalizedSelectors.filter((spec) => typeof spec.selector === "string" && spec.selector.includes(":contains(")).map((spec) => ({
|
|
2363
2421
|
name: spec.name || spec.selector,
|
|
2364
|
-
|
|
2422
|
+
message: "Selector uses unsupported CSS :contains() syntax.",
|
|
2423
|
+
hint: "Use selector text filters (text/textMode) or a different selector."
|
|
2365
2424
|
}));
|
|
2366
2425
|
const signalDefs = [];
|
|
2367
2426
|
for (const signalId of signalList) {
|
|
@@ -2403,19 +2462,22 @@
|
|
|
2403
2462
|
const tabId = task.tab.tabId;
|
|
2404
2463
|
let result = null;
|
|
2405
2464
|
let error = null;
|
|
2406
|
-
const started = Date.now();
|
|
2407
2465
|
try {
|
|
2408
2466
|
await waitForTabReady2(tabId, params, signalTimeoutMs);
|
|
2409
2467
|
result = await task.signal.run(tabId);
|
|
2468
|
+
if (task.signal.id === "selector" && result && typeof result === "object") {
|
|
2469
|
+
const selectorErrors = Object.keys(result.errors || {});
|
|
2470
|
+
if (selectorErrors.length > 0) {
|
|
2471
|
+
error = `selector failures: ${selectorErrors.join(", ")}`;
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2410
2474
|
} catch (err) {
|
|
2411
2475
|
const message = err instanceof Error ? err.message : "signal_error";
|
|
2412
2476
|
error = message;
|
|
2413
2477
|
}
|
|
2414
|
-
const durationMs = Date.now() - started;
|
|
2415
2478
|
const entry = entryMap.get(tabId) || { tab: task.tab, signals: {} };
|
|
2416
2479
|
entry.signals[task.signal.id] = {
|
|
2417
2480
|
ok: error === null,
|
|
2418
|
-
durationMs,
|
|
2419
2481
|
data: result,
|
|
2420
2482
|
error
|
|
2421
2483
|
};
|
|
@@ -2441,21 +2503,18 @@
|
|
|
2441
2503
|
title: entry.tab.title,
|
|
2442
2504
|
signals: entry.signals
|
|
2443
2505
|
}));
|
|
2444
|
-
|
|
2445
|
-
generatedAt: Date.now(),
|
|
2506
|
+
const response = {
|
|
2446
2507
|
totals: {
|
|
2447
2508
|
tabs: tabs3.length,
|
|
2448
2509
|
signals: signalDefs.length,
|
|
2449
2510
|
tasks: totalTasks
|
|
2450
2511
|
},
|
|
2451
|
-
meta: {
|
|
2452
|
-
durationMs: Date.now() - startedAt,
|
|
2453
|
-
signalTimeoutMs,
|
|
2454
|
-
selectorCount: normalizedSelectors.length,
|
|
2455
|
-
selectorWarnings: selectorWarnings.length > 0 ? selectorWarnings : void 0
|
|
2456
|
-
},
|
|
2457
2512
|
entries
|
|
2458
2513
|
};
|
|
2514
|
+
if (selectorWarnings.length > 0) {
|
|
2515
|
+
response.warnings = selectorWarnings;
|
|
2516
|
+
}
|
|
2517
|
+
return response;
|
|
2459
2518
|
}
|
|
2460
2519
|
}
|
|
2461
2520
|
});
|
|
@@ -2935,13 +2994,14 @@
|
|
|
2935
2994
|
})).filter((win) => win.tabs.length > 0);
|
|
2936
2995
|
}
|
|
2937
2996
|
if (windowsToProcess.length === 0) {
|
|
2938
|
-
|
|
2997
|
+
const fullResult2 = {
|
|
2939
2998
|
txid: params.txid || null,
|
|
2940
2999
|
summary: { movedTabs: 0, movedGroups: 0, skippedTabs: 0 },
|
|
2941
3000
|
archiveWindowId: null,
|
|
2942
3001
|
skipped: [],
|
|
2943
3002
|
undo: { action: "archive", tabs: [] }
|
|
2944
3003
|
};
|
|
3004
|
+
return fullResult2;
|
|
2945
3005
|
}
|
|
2946
3006
|
const archiveWindowId = await ensureArchiveWindow(deps2);
|
|
2947
3007
|
const undoTabs = [];
|
|
@@ -3044,7 +3104,7 @@
|
|
|
3044
3104
|
}
|
|
3045
3105
|
}
|
|
3046
3106
|
}
|
|
3047
|
-
|
|
3107
|
+
const fullResult = {
|
|
3048
3108
|
txid: params.txid || null,
|
|
3049
3109
|
summary: {
|
|
3050
3110
|
movedTabs,
|
|
@@ -3058,6 +3118,13 @@
|
|
|
3058
3118
|
tabs: undoTabs
|
|
3059
3119
|
}
|
|
3060
3120
|
};
|
|
3121
|
+
return {
|
|
3122
|
+
txid: fullResult.txid,
|
|
3123
|
+
summary: fullResult.summary,
|
|
3124
|
+
archiveWindowId,
|
|
3125
|
+
skipped: fullResult.skipped,
|
|
3126
|
+
undo: fullResult.undo
|
|
3127
|
+
};
|
|
3061
3128
|
}
|
|
3062
3129
|
async function getTabsByIds(tabIds) {
|
|
3063
3130
|
const results = [];
|
|
@@ -3086,12 +3153,13 @@
|
|
|
3086
3153
|
tabIds = selection.tabs.map((tab) => tab.tabId);
|
|
3087
3154
|
}
|
|
3088
3155
|
if (!tabIds.length) {
|
|
3089
|
-
|
|
3156
|
+
const fullResult2 = {
|
|
3090
3157
|
txid: params.txid || null,
|
|
3091
3158
|
summary: { closedTabs: 0, skippedTabs: 0 },
|
|
3092
3159
|
skipped: [],
|
|
3093
3160
|
undo: { action: "close", tabs: [] }
|
|
3094
3161
|
};
|
|
3162
|
+
return fullResult2;
|
|
3095
3163
|
}
|
|
3096
3164
|
const expectedUrls = params.expectedUrls || {};
|
|
3097
3165
|
const tabInfos = await getTabsByIds(tabIds);
|
|
@@ -3131,7 +3199,7 @@
|
|
|
3131
3199
|
if (validTabs.length > 0) {
|
|
3132
3200
|
await chrome.tabs.remove(validTabs.map((tab) => tab.tabId));
|
|
3133
3201
|
}
|
|
3134
|
-
|
|
3202
|
+
const fullResult = {
|
|
3135
3203
|
txid: params.txid || null,
|
|
3136
3204
|
summary: {
|
|
3137
3205
|
closedTabs: validTabs.length,
|
|
@@ -3149,6 +3217,12 @@
|
|
|
3149
3217
|
}))
|
|
3150
3218
|
}
|
|
3151
3219
|
};
|
|
3220
|
+
return {
|
|
3221
|
+
txid: fullResult.txid,
|
|
3222
|
+
summary: fullResult.summary,
|
|
3223
|
+
skipped: fullResult.skipped,
|
|
3224
|
+
undo: fullResult.undo
|
|
3225
|
+
};
|
|
3152
3226
|
}
|
|
3153
3227
|
async function mergeWindow(params, deps2) {
|
|
3154
3228
|
const fromWindowId = Number.isFinite(params.fromWindowId) ? Number(params.fromWindowId) : Number(params.windowId);
|
|
@@ -3183,7 +3257,7 @@
|
|
|
3183
3257
|
selectedTabs = sourceWindow.tabs.filter((tab) => tabIdSet.has(tab.tabId));
|
|
3184
3258
|
}
|
|
3185
3259
|
if (selectedTabs.length === 0) {
|
|
3186
|
-
|
|
3260
|
+
const fullResult2 = {
|
|
3187
3261
|
fromWindowId,
|
|
3188
3262
|
toWindowId,
|
|
3189
3263
|
summary: { movedTabs: 0, movedGroups: 0, skippedTabs: skipped.length, closedSource: false },
|
|
@@ -3197,6 +3271,7 @@
|
|
|
3197
3271
|
tabs: []
|
|
3198
3272
|
}
|
|
3199
3273
|
};
|
|
3274
|
+
return fullResult2;
|
|
3200
3275
|
}
|
|
3201
3276
|
const orderedTabs = [...selectedTabs].sort((a, b) => {
|
|
3202
3277
|
const aIndex = Number(a.index);
|
|
@@ -3295,7 +3370,7 @@
|
|
|
3295
3370
|
deps2.log("Failed to close source window", error);
|
|
3296
3371
|
}
|
|
3297
3372
|
}
|
|
3298
|
-
|
|
3373
|
+
const fullResult = {
|
|
3299
3374
|
fromWindowId,
|
|
3300
3375
|
toWindowId,
|
|
3301
3376
|
summary: { movedTabs, movedGroups, skippedTabs: skipped.length, closedSource },
|
|
@@ -3310,6 +3385,15 @@
|
|
|
3310
3385
|
},
|
|
3311
3386
|
txid: params.txid || null
|
|
3312
3387
|
};
|
|
3388
|
+
return {
|
|
3389
|
+
fromWindowId,
|
|
3390
|
+
toWindowId,
|
|
3391
|
+
summary: fullResult.summary,
|
|
3392
|
+
skipped: fullResult.skipped,
|
|
3393
|
+
groups: fullResult.groups,
|
|
3394
|
+
undo: fullResult.undo,
|
|
3395
|
+
txid: fullResult.txid
|
|
3396
|
+
};
|
|
3313
3397
|
}
|
|
3314
3398
|
}
|
|
3315
3399
|
});
|
|
@@ -3489,7 +3573,7 @@
|
|
|
3489
3573
|
component: "extension"
|
|
3490
3574
|
};
|
|
3491
3575
|
case "list":
|
|
3492
|
-
return await getTabSnapshot();
|
|
3576
|
+
return shapeListSnapshot(await getTabSnapshot());
|
|
3493
3577
|
case "analyze":
|
|
3494
3578
|
return await inspect.analyzeTabs(params, requestId, deps);
|
|
3495
3579
|
case "inspect":
|
|
@@ -3533,6 +3617,29 @@
|
|
|
3533
3617
|
throw new Error(`Unknown action: ${action}`);
|
|
3534
3618
|
}
|
|
3535
3619
|
}
|
|
3620
|
+
function shapeListSnapshot(snapshot) {
|
|
3621
|
+
return {
|
|
3622
|
+
windows: snapshot.windows.map((win) => ({
|
|
3623
|
+
windowId: win.windowId,
|
|
3624
|
+
focused: win.focused,
|
|
3625
|
+
tabs: (win.tabs || []).map((tab) => ({
|
|
3626
|
+
tabId: tab.tabId,
|
|
3627
|
+
windowId: tab.windowId,
|
|
3628
|
+
url: tab.url,
|
|
3629
|
+
title: tab.title,
|
|
3630
|
+
active: tab.active,
|
|
3631
|
+
groupId: tab.groupId,
|
|
3632
|
+
groupTitle: tab.groupTitle
|
|
3633
|
+
})),
|
|
3634
|
+
groups: (win.groups || []).map((group) => ({
|
|
3635
|
+
groupId: group.groupId,
|
|
3636
|
+
title: group.title,
|
|
3637
|
+
color: group.color,
|
|
3638
|
+
collapsed: group.collapsed
|
|
3639
|
+
}))
|
|
3640
|
+
}))
|
|
3641
|
+
};
|
|
3642
|
+
}
|
|
3536
3643
|
function resolveWindowIdFromParams(snapshot, value) {
|
|
3537
3644
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
3538
3645
|
return value;
|
|
@@ -51,13 +51,14 @@ async function archiveTabs(params, deps) {
|
|
|
51
51
|
.filter((win) => win.tabs.length > 0);
|
|
52
52
|
}
|
|
53
53
|
if (windowsToProcess.length === 0) {
|
|
54
|
-
|
|
54
|
+
const fullResult = {
|
|
55
55
|
txid: params.txid || null,
|
|
56
56
|
summary: { movedTabs: 0, movedGroups: 0, skippedTabs: 0 },
|
|
57
57
|
archiveWindowId: null,
|
|
58
58
|
skipped: [],
|
|
59
59
|
undo: { action: "archive", tabs: [] },
|
|
60
60
|
};
|
|
61
|
+
return fullResult;
|
|
61
62
|
}
|
|
62
63
|
const archiveWindowId = await ensureArchiveWindow(deps);
|
|
63
64
|
const undoTabs = [];
|
|
@@ -169,7 +170,7 @@ async function archiveTabs(params, deps) {
|
|
|
169
170
|
}
|
|
170
171
|
}
|
|
171
172
|
}
|
|
172
|
-
|
|
173
|
+
const fullResult = {
|
|
173
174
|
txid: params.txid || null,
|
|
174
175
|
summary: {
|
|
175
176
|
movedTabs,
|
|
@@ -183,6 +184,13 @@ async function archiveTabs(params, deps) {
|
|
|
183
184
|
tabs: undoTabs,
|
|
184
185
|
},
|
|
185
186
|
};
|
|
187
|
+
return {
|
|
188
|
+
txid: fullResult.txid,
|
|
189
|
+
summary: fullResult.summary,
|
|
190
|
+
archiveWindowId,
|
|
191
|
+
skipped: fullResult.skipped,
|
|
192
|
+
undo: fullResult.undo,
|
|
193
|
+
};
|
|
186
194
|
}
|
|
187
195
|
async function getTabsByIds(tabIds) {
|
|
188
196
|
const results = [];
|
|
@@ -212,12 +220,13 @@ async function closeTabs(params, deps) {
|
|
|
212
220
|
tabIds = selection.tabs.map((tab) => tab.tabId);
|
|
213
221
|
}
|
|
214
222
|
if (!tabIds.length) {
|
|
215
|
-
|
|
223
|
+
const fullResult = {
|
|
216
224
|
txid: params.txid || null,
|
|
217
225
|
summary: { closedTabs: 0, skippedTabs: 0 },
|
|
218
226
|
skipped: [],
|
|
219
227
|
undo: { action: "close", tabs: [] },
|
|
220
228
|
};
|
|
229
|
+
return fullResult;
|
|
221
230
|
}
|
|
222
231
|
const expectedUrls = params.expectedUrls || {};
|
|
223
232
|
const tabInfos = await getTabsByIds(tabIds);
|
|
@@ -257,7 +266,7 @@ async function closeTabs(params, deps) {
|
|
|
257
266
|
if (validTabs.length > 0) {
|
|
258
267
|
await chrome.tabs.remove(validTabs.map((tab) => tab.tabId));
|
|
259
268
|
}
|
|
260
|
-
|
|
269
|
+
const fullResult = {
|
|
261
270
|
txid: params.txid || null,
|
|
262
271
|
summary: {
|
|
263
272
|
closedTabs: validTabs.length,
|
|
@@ -275,6 +284,12 @@ async function closeTabs(params, deps) {
|
|
|
275
284
|
})),
|
|
276
285
|
},
|
|
277
286
|
};
|
|
287
|
+
return {
|
|
288
|
+
txid: fullResult.txid,
|
|
289
|
+
summary: fullResult.summary,
|
|
290
|
+
skipped: fullResult.skipped,
|
|
291
|
+
undo: fullResult.undo,
|
|
292
|
+
};
|
|
278
293
|
}
|
|
279
294
|
async function mergeWindow(params, deps) {
|
|
280
295
|
const fromWindowId = Number.isFinite(params.fromWindowId)
|
|
@@ -311,7 +326,7 @@ async function mergeWindow(params, deps) {
|
|
|
311
326
|
selectedTabs = sourceWindow.tabs.filter((tab) => tabIdSet.has(tab.tabId));
|
|
312
327
|
}
|
|
313
328
|
if (selectedTabs.length === 0) {
|
|
314
|
-
|
|
329
|
+
const fullResult = {
|
|
315
330
|
fromWindowId,
|
|
316
331
|
toWindowId,
|
|
317
332
|
summary: { movedTabs: 0, movedGroups: 0, skippedTabs: skipped.length, closedSource: false },
|
|
@@ -325,6 +340,7 @@ async function mergeWindow(params, deps) {
|
|
|
325
340
|
tabs: [],
|
|
326
341
|
},
|
|
327
342
|
};
|
|
343
|
+
return fullResult;
|
|
328
344
|
}
|
|
329
345
|
const orderedTabs = [...selectedTabs].sort((a, b) => {
|
|
330
346
|
const aIndex = Number(a.index);
|
|
@@ -426,7 +442,7 @@ async function mergeWindow(params, deps) {
|
|
|
426
442
|
deps.log("Failed to close source window", error);
|
|
427
443
|
}
|
|
428
444
|
}
|
|
429
|
-
|
|
445
|
+
const fullResult = {
|
|
430
446
|
fromWindowId,
|
|
431
447
|
toWindowId,
|
|
432
448
|
summary: { movedTabs, movedGroups, skippedTabs: skipped.length, closedSource },
|
|
@@ -441,4 +457,13 @@ async function mergeWindow(params, deps) {
|
|
|
441
457
|
},
|
|
442
458
|
txid: params.txid || null,
|
|
443
459
|
};
|
|
460
|
+
return {
|
|
461
|
+
fromWindowId,
|
|
462
|
+
toWindowId,
|
|
463
|
+
summary: fullResult.summary,
|
|
464
|
+
skipped: fullResult.skipped,
|
|
465
|
+
groups: fullResult.groups,
|
|
466
|
+
undo: fullResult.undo,
|
|
467
|
+
txid: fullResult.txid,
|
|
468
|
+
};
|
|
444
469
|
}
|
|
@@ -211,7 +211,7 @@ async function groupUpdate(params, deps) {
|
|
|
211
211
|
throw new Error("Missing group update fields");
|
|
212
212
|
}
|
|
213
213
|
const updated = await chrome.tabGroups.update(match.group.groupId, update);
|
|
214
|
-
|
|
214
|
+
const fullResult = {
|
|
215
215
|
groupId: updated.id,
|
|
216
216
|
windowId: updated.windowId,
|
|
217
217
|
title: updated.title,
|
|
@@ -229,6 +229,13 @@ async function groupUpdate(params, deps) {
|
|
|
229
229
|
},
|
|
230
230
|
txid: params.txid || null,
|
|
231
231
|
};
|
|
232
|
+
return {
|
|
233
|
+
groupId: updated.id,
|
|
234
|
+
windowId: updated.windowId,
|
|
235
|
+
summary: { updatedGroups: 1 },
|
|
236
|
+
undo: fullResult.undo,
|
|
237
|
+
txid: fullResult.txid,
|
|
238
|
+
};
|
|
232
239
|
}
|
|
233
240
|
async function groupUngroup(params, deps) {
|
|
234
241
|
const groupId = Number.isFinite(params.groupId) ? Number(params.groupId) : null;
|
|
@@ -277,7 +284,7 @@ async function groupUngroup(params, deps) {
|
|
|
277
284
|
if (tabIds.length) {
|
|
278
285
|
await chrome.tabs.ungroup(tabIds);
|
|
279
286
|
}
|
|
280
|
-
|
|
287
|
+
const fullResult = {
|
|
281
288
|
groupId: match.group.groupId,
|
|
282
289
|
groupTitle: match.group.title || null,
|
|
283
290
|
windowId: match.windowId,
|
|
@@ -295,6 +302,13 @@ async function groupUngroup(params, deps) {
|
|
|
295
302
|
},
|
|
296
303
|
txid: params.txid || null,
|
|
297
304
|
};
|
|
305
|
+
return {
|
|
306
|
+
groupId: match.group.groupId,
|
|
307
|
+
windowId: match.windowId,
|
|
308
|
+
summary: fullResult.summary,
|
|
309
|
+
undo: fullResult.undo,
|
|
310
|
+
txid: fullResult.txid,
|
|
311
|
+
};
|
|
298
312
|
}
|
|
299
313
|
async function groupAssign(params, deps) {
|
|
300
314
|
const rawTabIds = Array.isArray(params.tabIds) ? params.tabIds.map(Number) : [];
|
|
@@ -423,7 +437,7 @@ async function groupAssign(params, deps) {
|
|
|
423
437
|
}
|
|
424
438
|
created = true;
|
|
425
439
|
}
|
|
426
|
-
|
|
440
|
+
const fullResult = {
|
|
427
441
|
groupId: assignedGroupId,
|
|
428
442
|
groupTitle: targetTitle || groupTitle || null,
|
|
429
443
|
windowId: targetWindowId,
|
|
@@ -445,6 +459,15 @@ async function groupAssign(params, deps) {
|
|
|
445
459
|
},
|
|
446
460
|
txid: params.txid || null,
|
|
447
461
|
};
|
|
462
|
+
return {
|
|
463
|
+
groupId: assignedGroupId,
|
|
464
|
+
windowId: targetWindowId,
|
|
465
|
+
created,
|
|
466
|
+
summary: fullResult.summary,
|
|
467
|
+
skipped: fullResult.skipped,
|
|
468
|
+
undo: fullResult.undo,
|
|
469
|
+
txid: fullResult.txid,
|
|
470
|
+
};
|
|
448
471
|
}
|
|
449
472
|
async function groupGather(params, deps) {
|
|
450
473
|
const snapshot = await deps.getTabSnapshot();
|
|
@@ -514,7 +537,7 @@ async function groupGather(params, deps) {
|
|
|
514
537
|
});
|
|
515
538
|
}
|
|
516
539
|
}
|
|
517
|
-
|
|
540
|
+
const fullResult = {
|
|
518
541
|
merged,
|
|
519
542
|
summary: {
|
|
520
543
|
mergedGroups: merged.reduce((sum, m) => sum + m.mergedGroupCount, 0),
|
|
@@ -526,4 +549,10 @@ async function groupGather(params, deps) {
|
|
|
526
549
|
},
|
|
527
550
|
txid: params.txid || null,
|
|
528
551
|
};
|
|
552
|
+
return {
|
|
553
|
+
merged,
|
|
554
|
+
summary: fullResult.summary,
|
|
555
|
+
undo: fullResult.undo,
|
|
556
|
+
txid: fullResult.txid,
|
|
557
|
+
};
|
|
529
558
|
}
|
|
@@ -22,7 +22,6 @@ async function analyzeTabs(params, requestId, deps) {
|
|
|
22
22
|
const selectedTabs = selection.tabs;
|
|
23
23
|
const scopeTabs = selectedTabs;
|
|
24
24
|
const now = Date.now();
|
|
25
|
-
const startedAt = Date.now();
|
|
26
25
|
const normalizedMap = new Map();
|
|
27
26
|
const duplicates = new Map();
|
|
28
27
|
for (const tab of scopeTabs) {
|
|
@@ -78,19 +77,16 @@ async function analyzeTabs(params, requestId, deps) {
|
|
|
78
77
|
severity,
|
|
79
78
|
};
|
|
80
79
|
});
|
|
81
|
-
|
|
82
|
-
generatedAt: Date.now(),
|
|
80
|
+
const response = {
|
|
83
81
|
staleDays,
|
|
84
82
|
totals: {
|
|
85
83
|
tabs: scopeTabs.length,
|
|
86
84
|
analyzed: selectedTabs.length,
|
|
87
85
|
candidates: candidates.length,
|
|
88
86
|
},
|
|
89
|
-
meta: {
|
|
90
|
-
durationMs: Date.now() - startedAt,
|
|
91
|
-
},
|
|
92
87
|
candidates,
|
|
93
88
|
};
|
|
89
|
+
return response;
|
|
94
90
|
}
|
|
95
91
|
async function inspectTabs(params, requestId, deps) {
|
|
96
92
|
const signalList = Array.isArray(params.signals) && params.signals.length > 0
|
|
@@ -120,7 +116,6 @@ async function inspectTabs(params, requestId, deps) {
|
|
|
120
116
|
throw selection.error;
|
|
121
117
|
}
|
|
122
118
|
const tabs = selection.tabs;
|
|
123
|
-
const startedAt = Date.now();
|
|
124
119
|
const selectorSpecs = [];
|
|
125
120
|
if (Array.isArray(params.selectorSpecs)) {
|
|
126
121
|
selectorSpecs.push(...params.selectorSpecs);
|
|
@@ -140,19 +135,26 @@ async function inspectTabs(params, requestId, deps) {
|
|
|
140
135
|
}
|
|
141
136
|
const normalizedSelectors = selectorSpecs
|
|
142
137
|
.filter((spec) => spec && typeof spec.selector === "string" && spec.selector.length > 0)
|
|
143
|
-
.map((spec) =>
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
138
|
+
.map((spec) => {
|
|
139
|
+
const selector = spec.selector;
|
|
140
|
+
return {
|
|
141
|
+
name: typeof spec.name === "string" ? spec.name : undefined,
|
|
142
|
+
selector,
|
|
143
|
+
attr: typeof spec.attr === "string" ? spec.attr : "text",
|
|
144
|
+
all: Boolean(spec.all),
|
|
145
|
+
text: typeof spec.text === "string" && spec.text.trim() ? spec.text.trim() : undefined,
|
|
146
|
+
textMode: typeof spec.textMode === "string" ? spec.textMode.trim().toLowerCase() : undefined,
|
|
147
|
+
};
|
|
148
|
+
});
|
|
151
149
|
const selectorWarnings = normalizedSelectors
|
|
152
|
-
.filter((spec) =>
|
|
150
|
+
.filter((spec) => spec.selector.includes(":contains("))
|
|
153
151
|
.map((spec) => ({
|
|
152
|
+
code: "unsupported_selector_syntax",
|
|
153
|
+
signalId: "selector",
|
|
154
|
+
selector: spec.selector,
|
|
154
155
|
name: spec.name || spec.selector,
|
|
155
|
-
|
|
156
|
+
message: "Selector uses unsupported CSS :contains() syntax.",
|
|
157
|
+
hint: "Use selector text filters (text/textMode) or a different selector.",
|
|
156
158
|
}));
|
|
157
159
|
const signalDefs = [];
|
|
158
160
|
for (const signalId of signalList) {
|
|
@@ -195,20 +197,23 @@ async function inspectTabs(params, requestId, deps) {
|
|
|
195
197
|
const tabId = task.tab.tabId;
|
|
196
198
|
let result = null;
|
|
197
199
|
let error = null;
|
|
198
|
-
const started = Date.now();
|
|
199
200
|
try {
|
|
200
201
|
await waitForTabReady(tabId, params, signalTimeoutMs);
|
|
201
202
|
result = await task.signal.run(tabId);
|
|
203
|
+
if (task.signal.id === "selector" && result && typeof result === "object") {
|
|
204
|
+
const selectorErrors = Object.keys(result.errors || {});
|
|
205
|
+
if (selectorErrors.length > 0) {
|
|
206
|
+
error = `selector failures: ${selectorErrors.join(", ")}`;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
202
209
|
}
|
|
203
210
|
catch (err) {
|
|
204
211
|
const message = err instanceof Error ? err.message : "signal_error";
|
|
205
212
|
error = message;
|
|
206
213
|
}
|
|
207
|
-
const durationMs = Date.now() - started;
|
|
208
214
|
const entry = entryMap.get(tabId) || { tab: task.tab, signals: {} };
|
|
209
215
|
entry.signals[task.signal.id] = {
|
|
210
216
|
ok: error === null,
|
|
211
|
-
durationMs,
|
|
212
217
|
data: result,
|
|
213
218
|
error,
|
|
214
219
|
};
|
|
@@ -234,19 +239,16 @@ async function inspectTabs(params, requestId, deps) {
|
|
|
234
239
|
title: entry.tab.title,
|
|
235
240
|
signals: entry.signals,
|
|
236
241
|
}));
|
|
237
|
-
|
|
238
|
-
generatedAt: Date.now(),
|
|
242
|
+
const response = {
|
|
239
243
|
totals: {
|
|
240
244
|
tabs: tabs.length,
|
|
241
245
|
signals: signalDefs.length,
|
|
242
246
|
tasks: totalTasks,
|
|
243
247
|
},
|
|
244
|
-
meta: {
|
|
245
|
-
durationMs: Date.now() - startedAt,
|
|
246
|
-
signalTimeoutMs,
|
|
247
|
-
selectorCount: normalizedSelectors.length,
|
|
248
|
-
selectorWarnings: selectorWarnings.length > 0 ? selectorWarnings : undefined,
|
|
249
|
-
},
|
|
250
248
|
entries,
|
|
251
249
|
};
|
|
250
|
+
if (selectorWarnings.length > 0) {
|
|
251
|
+
response.warnings = selectorWarnings;
|
|
252
|
+
}
|
|
253
|
+
return response;
|
|
252
254
|
}
|
|
@@ -121,7 +121,7 @@ async function moveTab(params, deps) {
|
|
|
121
121
|
targetIndex = 0;
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
|
-
|
|
124
|
+
const fullResult = {
|
|
125
125
|
tabId,
|
|
126
126
|
from: { windowId: sourceWindow.windowId, index: sourceTab.index },
|
|
127
127
|
to: { windowId: targetWindowId, index: targetIndex },
|
|
@@ -144,6 +144,14 @@ async function moveTab(params, deps) {
|
|
|
144
144
|
},
|
|
145
145
|
txid: params.txid || null,
|
|
146
146
|
};
|
|
147
|
+
return {
|
|
148
|
+
tabId,
|
|
149
|
+
fromWindowId: sourceWindow.windowId,
|
|
150
|
+
toWindowId: targetWindowId,
|
|
151
|
+
summary: fullResult.summary,
|
|
152
|
+
undo: fullResult.undo,
|
|
153
|
+
txid: fullResult.txid,
|
|
154
|
+
};
|
|
147
155
|
}
|
|
148
156
|
let normalizedParams = params;
|
|
149
157
|
if (params.windowId != null) {
|
|
@@ -161,7 +169,7 @@ async function moveTab(params, deps) {
|
|
|
161
169
|
targetIndex -= 1;
|
|
162
170
|
}
|
|
163
171
|
const moved = await chrome.tabs.move(tabId, { windowId: targetWindowId, index: targetIndex });
|
|
164
|
-
|
|
172
|
+
const fullResult = {
|
|
165
173
|
tabId,
|
|
166
174
|
from: { windowId: sourceWindow.windowId, index: sourceTab.index },
|
|
167
175
|
to: { windowId: targetWindowId, index: moved.index },
|
|
@@ -184,6 +192,14 @@ async function moveTab(params, deps) {
|
|
|
184
192
|
},
|
|
185
193
|
txid: params.txid || null,
|
|
186
194
|
};
|
|
195
|
+
return {
|
|
196
|
+
tabId,
|
|
197
|
+
fromWindowId: sourceWindow.windowId,
|
|
198
|
+
toWindowId: targetWindowId,
|
|
199
|
+
summary: fullResult.summary,
|
|
200
|
+
undo: fullResult.undo,
|
|
201
|
+
txid: fullResult.txid,
|
|
202
|
+
};
|
|
187
203
|
}
|
|
188
204
|
async function moveGroup(params, deps) {
|
|
189
205
|
const groupId = Number.isFinite(params.groupId) ? Number(params.groupId) : null;
|
|
@@ -271,7 +287,7 @@ async function moveGroup(params, deps) {
|
|
|
271
287
|
groupCollapsed: source.group.collapsed ?? null,
|
|
272
288
|
}))
|
|
273
289
|
.filter((tab) => typeof tab.tabId === "number");
|
|
274
|
-
|
|
290
|
+
const fullResult = {
|
|
275
291
|
groupId: source.group.groupId,
|
|
276
292
|
windowId: source.windowId,
|
|
277
293
|
movedToWindowId: targetWindowId,
|
|
@@ -289,6 +305,15 @@ async function moveGroup(params, deps) {
|
|
|
289
305
|
},
|
|
290
306
|
txid: params.txid || null,
|
|
291
307
|
};
|
|
308
|
+
return {
|
|
309
|
+
groupId: source.group.groupId,
|
|
310
|
+
windowId: source.windowId,
|
|
311
|
+
movedToWindowId: targetWindowId,
|
|
312
|
+
newGroupId,
|
|
313
|
+
summary: fullResult.summary,
|
|
314
|
+
undo: fullResult.undo,
|
|
315
|
+
txid: fullResult.txid,
|
|
316
|
+
};
|
|
292
317
|
}
|
|
293
318
|
const target = resolveMoveTarget(snapshot, params, deps);
|
|
294
319
|
if (target.error) {
|
|
@@ -348,7 +373,7 @@ async function moveGroup(params, deps) {
|
|
|
348
373
|
groupCollapsed: source.group.collapsed ?? null,
|
|
349
374
|
}))
|
|
350
375
|
.filter((tab) => typeof tab.tabId === "number");
|
|
351
|
-
|
|
376
|
+
const fullResult = {
|
|
352
377
|
groupId: source.group.groupId,
|
|
353
378
|
windowId: source.windowId,
|
|
354
379
|
movedToWindowId: targetWindowId,
|
|
@@ -366,4 +391,13 @@ async function moveGroup(params, deps) {
|
|
|
366
391
|
},
|
|
367
392
|
txid: params.txid || null,
|
|
368
393
|
};
|
|
394
|
+
return {
|
|
395
|
+
groupId: source.group.groupId,
|
|
396
|
+
windowId: source.windowId,
|
|
397
|
+
movedToWindowId: targetWindowId,
|
|
398
|
+
newGroupId,
|
|
399
|
+
summary: fullResult.summary,
|
|
400
|
+
undo: fullResult.undo,
|
|
401
|
+
txid: fullResult.txid,
|
|
402
|
+
};
|
|
369
403
|
}
|
|
@@ -293,7 +293,6 @@ async function screenshotTabs(params, requestId, deps) {
|
|
|
293
293
|
const tabs = selection.tabs;
|
|
294
294
|
const entries = [];
|
|
295
295
|
let totalTiles = 0;
|
|
296
|
-
const startedAt = Date.now();
|
|
297
296
|
for (let index = 0; index < tabs.length; index += 1) {
|
|
298
297
|
const tab = tabs[index];
|
|
299
298
|
const tabId = tab.tabId;
|
|
@@ -351,17 +350,9 @@ async function screenshotTabs(params, requestId, deps) {
|
|
|
351
350
|
deps.sendProgress(requestId, { phase: "screenshot", processed: index + 1, total: tabs.length, tabId });
|
|
352
351
|
}
|
|
353
352
|
}
|
|
354
|
-
|
|
355
|
-
generatedAt: Date.now(),
|
|
353
|
+
const response = {
|
|
356
354
|
totals: { tabs: tabs.length, tiles: totalTiles },
|
|
357
|
-
meta: {
|
|
358
|
-
durationMs: Date.now() - startedAt,
|
|
359
|
-
mode,
|
|
360
|
-
format,
|
|
361
|
-
quality: format === "jpeg" ? quality : null,
|
|
362
|
-
tileMaxDim: adjustedTileMaxDim,
|
|
363
|
-
maxBytes: adjustedMaxBytes,
|
|
364
|
-
},
|
|
365
355
|
entries,
|
|
366
356
|
};
|
|
357
|
+
return response;
|
|
367
358
|
}
|
|
@@ -58,6 +58,17 @@ function normalizeTabIndex(value) {
|
|
|
58
58
|
const index = Number(value);
|
|
59
59
|
return Number.isFinite(index) ? index : null;
|
|
60
60
|
}
|
|
61
|
+
function shapeOpenResult(result) {
|
|
62
|
+
return {
|
|
63
|
+
windowId: result.windowId,
|
|
64
|
+
groupId: result.groupId,
|
|
65
|
+
createdTabIds: result.created
|
|
66
|
+
.map((tab) => tab.tabId)
|
|
67
|
+
.filter((id) => typeof id === "number"),
|
|
68
|
+
skipped: result.skipped,
|
|
69
|
+
summary: result.summary,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
61
72
|
function matchIncludes(value, needle) {
|
|
62
73
|
if (!needle) {
|
|
63
74
|
return false;
|
|
@@ -260,12 +271,9 @@ async function openTabs(params, deps) {
|
|
|
260
271
|
groupId = null;
|
|
261
272
|
}
|
|
262
273
|
}
|
|
263
|
-
return {
|
|
274
|
+
return shapeOpenResult({
|
|
264
275
|
windowId,
|
|
265
276
|
groupId,
|
|
266
|
-
groupTitle: groupTitle || null,
|
|
267
|
-
afterGroupTitle: null,
|
|
268
|
-
insertIndex: null,
|
|
269
277
|
created,
|
|
270
278
|
skipped,
|
|
271
279
|
summary: {
|
|
@@ -273,7 +281,7 @@ async function openTabs(params, deps) {
|
|
|
273
281
|
skippedUrls: skipped.length,
|
|
274
282
|
grouped: Boolean(groupId),
|
|
275
283
|
},
|
|
276
|
-
};
|
|
284
|
+
});
|
|
277
285
|
}
|
|
278
286
|
const snapshot = await deps.getTabSnapshot();
|
|
279
287
|
let openParams = params;
|
|
@@ -496,12 +504,9 @@ async function openTabs(params, deps) {
|
|
|
496
504
|
deps.log("Failed delayed grouping verification", error);
|
|
497
505
|
}
|
|
498
506
|
}
|
|
499
|
-
return {
|
|
507
|
+
return shapeOpenResult({
|
|
500
508
|
windowId,
|
|
501
509
|
groupId,
|
|
502
|
-
groupTitle: groupTitle || null,
|
|
503
|
-
afterGroupTitle: afterGroupTitle || null,
|
|
504
|
-
insertIndex,
|
|
505
510
|
created,
|
|
506
511
|
skipped,
|
|
507
512
|
summary: {
|
|
@@ -509,5 +514,5 @@ async function openTabs(params, deps) {
|
|
|
509
514
|
skippedUrls: skipped.length,
|
|
510
515
|
grouped: Boolean(groupId),
|
|
511
516
|
},
|
|
512
|
-
};
|
|
517
|
+
});
|
|
513
518
|
}
|